Merge: transform loops
authorJean Privat <jean@pryen.org>
Wed, 15 Oct 2014 20:15:47 +0000 (16:15 -0400)
committerJean Privat <jean@pryen.org>
Wed, 15 Oct 2014 20:15:47 +0000 (16:15 -0400)
In order to activate more optimizations and simplify code generators and analysis, the `while` and `for` loops must be transformed into simpler forms (in fact in complex forms but with simpler elements)

However, to do such a transformation, the escaping mechanism (break/continue/goto) must be rewrote.
Previously, for each loop control structure (loop for, while), a single EscapeMark was generated and breaks and continues are associated to the same mark, even if they branch in distinct points (so each escapemark had 2 branching mechanisms)
Now, with the first commits, two escapemarks are generated for each loop control structure, and breaks and continues are associated to a different one. The advantage is that each mark only have a single branching mechanism and that once associated to their mark, there is no need to distinguish break and continue (both become simple gotos).

The transformations of loops can be then implemented.

The `while`

~~~
while cond do block
~~~

is transformed into

~~~
loop if cond then block else break
~~~

and `for`

~~~
for i in col do block
~~~

is transformed into

~~~
var it = col.iterator
loop
   if it.is_ok then
      var i = it.item
      do block
      # ^ `continue` in the block it attached to the `do`
      # this is why the transformation of escape-marks where needed in fact.
      # and break is associated to the main loop
      it.next
   else
      break
   end
end
it.finish
~~~

Note that these transformations are basically what is currently implemented in rta, niti and nitg. Except that, now with a single transformation point, those three workers does not need to do their own transformation and basically can just rely on the one done on the AST level.

Pull-Request: #818
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

1  2 
src/astbuilder.nit
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit
src/semantize/typing.nit

diff --combined src/astbuilder.nit
@@@ -53,6 -53,12 +53,12 @@@ class ASTBuilde
                return new ABlockExpr.make
        end
  
+       # Make a new, empty, loop of statements
+       fun make_loop: ALoopExpr
+       do
+               return new ALoopExpr.make
+       end
        # Make a new variable read
        fun make_var_read(variable: Variable, mtype: MType): AVarExpr
        do
                return new ADoExpr.make
        end
  
+       # Make a new break for a given escapemark
+       fun make_break(escapemark: EscapeMark): ABreakExpr
+       do
+               return new ABreakExpr.make(escapemark)
+       end
        # Make a new condinionnal
        # `mtype` is the return type of the whole if, in case of a ternary operator.
        fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr
@@@ -142,6 -154,7 +154,7 @@@ redef class AExp
        # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3
        fun add(expr: AExpr)
        do
+               print "add not implemented in {inspect}"
                abort
        end
  end
@@@ -172,29 -185,54 +185,54 @@@ redef class ABlockExp
        end
  end
  
+ redef class ALoopExpr
+       private init make
+       do
+               _n_kwloop = new TKwloop
+               self.is_typed = true
+               n_block = new ABlockExpr
+               n_block.is_typed = true
+       end
+       redef fun add(expr: AExpr)
+       do
+               n_block.add expr
+       end
+ end
  redef class ADoExpr
        private init make
        do
                _n_kwdo = new TKwdo
-               escapemark = new EscapeMark(null, false)
+               self.is_typed = true
+               n_block = new ABlockExpr
+               n_block.is_typed = true
        end
  
        # Make a new break expression of the given do
        fun make_break: ABreakExpr
        do
-               var escapemark = self.escapemark
+               var escapemark = self.break_mark
                if escapemark == null then
-                       escapemark = new EscapeMark(null, false)
-                       self.escapemark = escapemark
+                       escapemark = new EscapeMark(null)
+                       self.break_mark = escapemark
                end
                return new ABreakExpr.make(escapemark)
        end
+       redef fun add(expr: AExpr)
+       do
+               n_block.add expr
+       end
  end
  
  redef class ABreakExpr
        private init make(escapemark: EscapeMark)
        do
+               _n_kwbreak = new TKwbreak
                self.escapemark = escapemark
+               escapemark.escapes.add self
+               self.is_typed = true
        end
  end
  
@@@ -251,6 -289,7 +289,6 @@@ redef class ACallExp
                if args != null then
                        self.n_args.n_exprs.add_all(args)
                end
 -              var mtype = recv.mtype.as(not null)
                self.callsite = callsite
                self.mtype = callsite.msignature.return_mtype
                self.is_typed = true
@@@ -22,48 -22,45 +22,46 @@@ import semantiz
  import platform
  import c_tools
  private import annotation
 +import mixin
  
  # Add compiling options
  redef class ToolContext
        # --output
 -      var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
 +      var opt_output = new OptionString("Output file", "-o", "--output")
        # --dir
 -      var opt_dir: OptionString = new OptionString("Output directory", "--dir")
 +      var opt_dir = new OptionString("Output directory", "--dir")
        # --no-cc
 -      var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
 +      var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
        # --no-main
 -      var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main")
 +      var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
        # --cc-paths
 -      var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
 +      var opt_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
 -      var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
 +      var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
        # --compile-dir
 -      var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir")
 +      var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
        # --hardening
 -      var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
 +      var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
-       # --no-shortcut-range
-       var opt_no_shortcut_range = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
        # --no-check-covariance
 -      var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
 +      var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
        # --no-check-attr-isset
 -      var opt_no_check_attr_isset: OptionBool = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
 +      var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
        # --no-check-assert
 -      var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
 +      var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
        # --no-check-autocast
 -      var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
 +      var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
        # --no-check-null
 -      var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
 +      var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
        # --no-check-all
 -      var opt_no_check_all: OptionBool = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
 +      var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
        # --typing-test-metrics
 -      var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
 +      var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
        # --invocation-metrics
 -      var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
 +      var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
        # --isset-checks-metrics
 -      var opt_isset_checks_metrics: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
 +      var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
        # --stacktrace
 -      var opt_stacktrace: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace")
 +      var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
        # --no-gcc-directives
        var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
        # --release
@@@ -72,7 -69,7 +70,7 @@@
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
+               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
                self.option_context.add_option(self.opt_stacktrace)
@@@ -182,6 -179,7 +180,6 @@@ class MakefileToolchai
        do
                gather_cc_paths
  
 -              var mainmodule = compiler.mainmodule
                var compile_dir = compile_dir
  
                # Generate the .h and .c files
                compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h"
  
                # FFI
 -              var m2m = toolcontext.modelbuilder.mmodule2nmodule
                for m in compiler.mainmodule.in_importation.greaters do
                        compiler.finalize_ffi_for_module(m)
                end
  
        fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
  
 -      fun default_outname(mainmodule: MModule): String do return mainmodule.name
 +      fun default_outname(mainmodule: MModule): String
 +      do
 +              # Search a non fictive module
 +              var res = mainmodule.name
 +              while mainmodule.is_fictive do
 +                      mainmodule = mainmodule.in_importation.direct_greaters.first
 +                      res = mainmodule.name
 +              end
 +              return res
 +      end
  
        # Combine options and platform informations to get the final path of the outfile
        fun outfile(mainmodule: MModule): String
                end
  
                var linker_options = new HashSet[String]
 -              var m2m = toolcontext.modelbuilder.mmodule2nmodule
                for m in mainmodule.in_importation.greaters do
                        var libs = m.collect_linker_libs
                        if libs != null then linker_options.add_all(libs)
@@@ -457,7 -448,7 +455,7 @@@ abstract class AbstractCompile
        # The real main module of the program
        var realmainmodule: MModule
  
 -      # The modeulbuilder used to know the model and the AST
 +      # The modelbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder is protected writable
  
        # Is hardening asked? (see --hardening)
  
        # The list of all associated files
        # Used to generate .c files
 -      var files: List[CodeFile] = new List[CodeFile]
 +      var files = new List[CodeFile]
  
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
        # Compile C headers
        # This method call compile_header_strucs method that has to be refined
        fun compile_header do
 -              var v = self.header
 -              var toolctx = modelbuilder.toolcontext
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
@@@ -896,7 -889,6 +894,7 @@@ extern void nitni_global_ref_decr( stru
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
 +                      if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
 +                      if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@@ -1051,7 -1042,7 +1049,7 @@@ abstract class AbstractCompilerVisito
        fun get_property(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
 -              return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
 +              return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
        end
  
        fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
  
        # Checks
  
 -      # Add a check and an abort for a null reciever if needed
 +      # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
  
        # Names handling
  
 -      private var names: HashSet[String] = new HashSet[String]
 +      private var names = new HashSet[String]
        private var last: Int = 0
  
        # Return a new name based on `s` and unique in the visitor
                return name
        end
  
+       # Insert a C label for associated with an escapemark
+       fun add_escape_label(e: nullable EscapeMark)
+       do
+               if e == null then return
+               if e.escapes.is_empty then return
+               add("BREAK_{escapemark_name(e)}: (void)0;")
+       end
        private var escapemark_names = new HashMap[EscapeMark, String]
  
        # Return a "const char*" variable associated to the classname of the dynamic type of an object
  
        # Variables handling
  
 -      protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
 +      protected var variables = new HashMap[Variable, RuntimeVariable]
  
        # Return the local runtime_variable associated to a Nit local variable
        fun variable(variable: Variable): RuntimeVariable
                return res
        end
  
 +      # Generate an integer value
 +      fun bool_instance(value: Bool): RuntimeVariable
 +      do
 +              var res = self.new_var(self.get_class("Bool").mclass_type)
 +              if value then
 +                      self.add("{res} = 1;")
 +              else
 +                      self.add("{res} = 0;")
 +              end
 +              return res
 +      end
 +
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
                return res
        end
  
 +      fun value_instance(object: Object): RuntimeVariable
 +      do
 +              if object isa Int then
 +                      return int_instance(object)
 +              else if object isa Bool then
 +                      return bool_instance(object)
 +              else if object isa String then
 +                      return string_instance(object)
 +              else
 +                      abort
 +              end
 +      end
 +
        # Generate an array value
        fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
  
@@@ -1845,7 -1819,6 +1851,7 @@@ redef class MMethodDe
        fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var modelbuilder = v.compiler.modelbuilder
 +              var val = constant_value
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
                        var oldnode = v.current_node
                        self.compile_parameter_check(v, arguments)
                        nclassdef.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
 +              else if val != null then
 +                      v.ret(v.value_instance(val))
                else
                        abort
                end
@@@ -2465,11 -2436,7 +2471,7 @@@ redef class ASelfExp
        redef fun expr(v) do return v.frame.arguments.first
  end
  
- redef class AContinueExpr
-       redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
- end
- redef class ABreakExpr
+ redef class AEscapeExpr
        redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
  end
  
@@@ -2532,10 -2499,7 +2534,7 @@@ redef class ADoExp
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               var escapemark = self.escapemark
-               if escapemark != null then
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-               end
+               v.add_escape_label(break_mark)
        end
  end
  
@@@ -2546,9 -2510,9 +2545,9 @@@ redef class AWhileExp
                var cond = v.expr_bool(self.n_expr)
                v.add("if (!{cond}) break;")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
        end
  end
  
@@@ -2557,47 -2521,15 +2556,15 @@@ redef class ALoopExp
        do
                v.add("for(;;) \{")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
        end
  end
  
  redef class AForExpr
        redef fun stmt(v)
        do
-               # Shortcut on explicit range
-               # Avoid the instantiation of the range and the iterator
-               var nexpr = self.n_expr
-               if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
-                       var from = v.expr(nexpr.n_expr, null)
-                       var to = v.expr(nexpr.n_expr2, null)
-                       var variable = v.variable(variables.first)
-                       var one = v.new_expr("1", v.get_class("Int").mclass_type)
-                       v.assign(variable, from)
-                       v.add("for(;;) \{ /* shortcut range */")
-                       var ok
-                       if nexpr isa AOrangeExpr then
-                               ok = v.send(v.get_property("<", variable.mtype), [variable, to])
-                       else
-                               ok = v.send(v.get_property("<=", variable.mtype), [variable, to])
-                       end
-                       assert ok != null
-                       v.add("if(!{ok}) break;")
-                       v.stmt(self.n_block)
-                       v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
-                       var succ = v.send(v.get_property("successor", variable.mtype), [variable, one])
-                       assert succ != null
-                       v.assign(variable, succ)
-                       v.add("\}")
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-                       return
-               end
                var cl = v.expr(self.n_expr, null)
                var it_meth = self.method_iterator
                assert it_meth != null
                        abort
                end
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                var next_meth = self.method_next
                assert next_meth != null
                v.compile_callsite(next_meth, [it])
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
  
                var method_finish = self.method_finish
                if method_finish != null then
@@@ -2782,7 -2714,7 +2749,7 @@@ redef class ACrangeExp
                var i2 = v.expr(self.n_expr2, null)
                var mtype = self.mtype.as(MClassType)
                var res = v.init_instance(mtype)
 -              var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
 +              v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
                return res
        end
  end
@@@ -2794,7 -2726,7 +2761,7 @@@ redef class AOrangeExp
                var i2 = v.expr(self.n_expr2, null)
                var mtype = self.mtype.as(MClassType)
                var res = v.init_instance(mtype)
 -              var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
 +              v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
                return res
        end
  end
@@@ -3060,6 -2992,9 +3027,6 @@@ en
  # Create a tool context to handle options and paths
  var toolcontext = new ToolContext
  
 -var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
 -toolcontext.option_context.add_option(opt_mixins)
 -
  toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
  
  # We do not add other options, so process them now!
@@@ -3078,6 -3013,7 +3045,6 @@@ en
  
  # Here we load an process all modules passed on the command line
  var mmodules = modelbuilder.parse(arguments)
 -var mixins = modelbuilder.parse(opt_mixins.value)
  
  if mmodules.is_empty then return
  modelbuilder.run_phases
  for mmodule in mmodules do
        toolcontext.info("*** PROCESS {mmodule} ***", 1)
        var ms = [mmodule]
 -      if not mixins.is_empty then
 -              ms.add_all mixins
 -      end
        toolcontext.run_global_phases(ms)
  end
@@@ -20,11 -20,10 +20,11 @@@ module naive_interprete
  import literal
  import semantize
  private import parser::tables
 +import mixin
  
  redef class ToolContext
        # --discover-call-trace
 -      var opt_discover_call_trace: OptionBool = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
 +      var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
  
        redef init
        do
@@@ -58,7 -57,7 +58,7 @@@ class NaiveInterprete
        # The modelbuilder that know the AST and its associations with the model
        var modelbuilder: ModelBuilder
  
 -      # The main moduleof the program (used to lookup methoda
 +      # The main module of the program (used to lookup method)
        var mainmodule: MModule
  
        # The command line arguments of the interpreted program
@@@ -66,7 -65,6 +66,7 @@@
        # arguments[1] is the first argument
        var arguments: Array[String]
  
 +      # The main Sys instance
        var mainobj: nullable Instance
  
        init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String])
        # Set this mark to skip the evaluation until the end of the specified method frame
        var returnmark: nullable Frame = null
  
-       # Is a break executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_break`
-       var breakmark: nullable EscapeMark = null
-       # Is a continue executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue`
-       var continuemark: nullable EscapeMark = null
+       # Is a break or a continue executed?
+       # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
+       var escapemark: nullable EscapeMark = null
  
        # Is a return or a break or a continue executed?
        # Use this function to know if you must skip the evaluation of statements
-       fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null
+       fun is_escaping: Bool do return returnmark != null or escapemark != null
  
        # The value associated with the current return/break/continue, if any.
        # Set the value when you set a escapemark.
        # Read the value when you catch a mark or reach the end of a method
        var escapevalue: nullable Instance = null
  
-       # If there is a break and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no break or if `escapemark` is null then return false.
-       # Use this function to catch a potential break.
-       fun is_break(escapemark: nullable EscapeMark): Bool
-       do
-               if escapemark != null and self.breakmark == escapemark then
-                       self.breakmark = null
-                       return true
-               else
-                       return false
-               end
-       end
-       # If there is a continue and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no continue or if `escapemark` is null then return false.
-       # Use this function to catch a potential continue.
-       fun is_continue(escapemark: nullable EscapeMark): Bool
+       # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark.
+       # If there is no break/continue or if `escapemark` is null then return false.
+       # Use this function to catch a potential break/continue.
+       fun is_escape(escapemark: nullable EscapeMark): Bool
        do
-               if escapemark != null and self.continuemark == escapemark then
-                       self.continuemark = null
+               if escapemark != null and self.escapemark == escapemark then
+                       self.escapemark = null
                        return true
                else
                        return false
                return new PrimitiveInstance[Float](ic.mclass_type, val)
        end
  
 -      # The unique intance of the `true` value.
 +      # The unique instance of the `true` value.
        var true_instance: Instance
  
 -      # The unique intance of the `false` value.
 +      # The unique instance of the `false` value.
        var false_instance: Instance
  
 -      # The unique intance of the `null` value.
 +      # The unique instance of the `null` value.
        var null_instance: Instance
  
        # Return a new array made of `values`.
                return res
        end
  
 +      fun value_instance(object: Object): Instance
 +      do
 +              if object isa Int then
 +                      return int_instance(object)
 +              else if object isa Bool then
 +                      return bool_instance(object)
 +              else if object isa String then
 +                      return string_instance(object)
 +              else
 +                      abort
 +              end
 +      end
 +
        # Return a new native string initialized with `txt`
        fun native_string_instance(txt: String): Instance
        do
                return new PrimitiveInstance[Buffer](ic.mclass_type, val)
        end
  
 +      # Return a new String instance for `txt`
 +      fun string_instance(txt: String): Instance
 +      do
 +              var nat = native_string_instance(txt)
 +              var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)])
 +              assert res != null
 +              return res
 +      end
 +
        # The current frame used to store local variables of the current method executed
        fun frame: Frame do return frames.first
  
        # The stack of all frames. The first one is the current one.
 -      var frames: List[Frame] = new List[Frame]
 +      var frames = new List[Frame]
  
 -      # Return a stack stace. One line per function
 +      # Return a stack trace. One line per function
        fun stack_trace: String
        do
                var b = new FlatBuffer
                f.map[v] = value
        end
  
 -      # Store known method, used to trace methods as thez are reached
 +      # Store known methods, used to trace methods as they are reached
        var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
  
        # Common code for calls to injected methods and normal methods
        end
  
        # Execute `mpropdef` for a `args` (where `args[0]` is the receiver).
 -      # Return a falue if `mpropdef` is a function, or null if it is a procedure.
 -      # The call is direct/static. There is no message-seding/late-binding.
 +      # Return a value if `mpropdef` is a function, or null if it is a procedure.
 +      # The call is direct/static. There is no message-sending/late-binding.
        fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
        do
                args = call_commons(mpropdef, args)
  
                # Look for the AST node that implements the property
                var mproperty = mpropdef.mproperty
 +              var val = mpropdef.constant_value
                if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then
                        var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef]
                        self.parameter_check(npropdef, mpropdef, args)
                        var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef]
                        self.parameter_check(nclassdef, mpropdef, args)
                        return nclassdef.call(self, mpropdef, args)
 +              else if val != null then
 +                      return value_instance(val)
                else
                        fatal("Fatal Error: method {mpropdef} not found in the AST")
                        abort
        end
  
        # Execute a full `callsite` for given `args`
 -      # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites
 +      # Use this method, instead of `send` to execute and control the additional behavior of the call-sites
        fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
        do
                var initializers = callsite.mpropdef.initializers
        end
  
        # Execute `mproperty` for a `args` (where `args[0]` is the receiver).
 -      # Return a falue if `mproperty` is a function, or null if it is a procedure.
 -      # The call is polimotphic. There is a message-seding/late-bindng according to te receiver (args[0]).
 +      # Return a value if `mproperty` is a function, or null if it is a procedure.
 +      # The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]).
        fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance
        do
                var recv = args.first
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
 +                      if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
                return res
        end
  
 -      var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
 +      private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
  
        # Fill the initial values of the newly created instance `recv`.
        # `recv.mtype` is used to know what must be filled.
@@@ -771,14 -726,14 +754,14 @@@ redef class AMethPropde
                        var txt = recv.mtype.to_s
                        return v.native_string_instance(txt)
                else if pname == "==" then
 -                      # == is correclt redefined for instances
 +                      # == is correctly redefined for instances
                        return v.bool_instance(args[0] == args[1])
                else if pname == "!=" then
                        return v.bool_instance(args[0] != args[1])
                else if pname == "is_same_type" then
                        return v.bool_instance(args[0].mtype == args[1].mtype)
                else if pname == "is_same_instance" then
 -                      return v.bool_instance(args[1] != null and args[0].eq_is(args[1]))
 +                      return v.bool_instance(args[0].eq_is(args[1]))
                else if pname == "exit" then
                        exit(args[1].to_i)
                        abort
@@@ -1227,20 -1182,7 +1210,7 @@@ redef class ASelfExp
        end
  end
  
- redef class AContinueExpr
-       redef fun stmt(v)
-       do
-               var ne = self.n_expr
-               if ne != null then
-                       var i = v.expr(ne)
-                       if i == null then return
-                       v.escapevalue = i
-               end
-               v.continuemark = self.escapemark
-       end
- end
- redef class ABreakExpr
+ redef class AEscapeExpr
        redef fun stmt(v)
        do
                var ne = self.n_expr
                        if i == null then return
                        v.escapevalue = i
                end
-               v.breakmark = self.escapemark
+               v.escapemark = self.escapemark
        end
  end
  
@@@ -1315,7 -1257,7 +1285,7 @@@ redef class ADoExp
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               v.is_break(self.escapemark) # Clear the break (if any)
+               v.is_escape(self.break_mark) # Clear the break (if any)
        end
  end
  
@@@ -1327,8 -1269,8 +1297,8 @@@ redef class AWhileExp
                        if cond == null then return
                        if not cond.is_true then return
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@@ -1339,8 -1281,8 +1309,8 @@@ redef class ALoopExp
        do
                loop
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@@ -1372,8 -1314,8 +1342,8 @@@ redef class AForExp
                                abort
                        end
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then break
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then break
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then break
                        v.callsite(method_next, [iter])
                end
@@@ -1492,7 -1434,9 +1462,7 @@@ redef class AStringFormExp
        redef fun expr(v)
        do
                var txt = self.value.as(not null)
 -              var nat = v.native_string_instance(txt)
 -              var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null)
 -              return res
 +              return v.string_instance(txt)
        end
  end
  
@@@ -1592,6 -1536,7 +1562,6 @@@ redef class AAsNotnullExp
        do
                var i = v.expr(self.n_expr)
                if i == null then return null
 -              var mtype = v.unanchor_type(self.mtype.as(not null))
                if i.mtype isa MNullType then
                        fatal(v, "Cast failed")
                end
@@@ -1676,7 -1621,7 +1646,7 @@@ redef class ASuperExp
  
                var callsite = self.callsite
                if callsite != null then
 -                      # Add additionnals arguments for the super init call
 +                      # Add additional arguments for the super init call
                        if args.length == 1 then
                                for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
                        args = v.frame.arguments
                end
  
 -              # stantard call-next-method
 +              # standard call-next-method
                var mpropdef = self.mpropdef
                mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
                var res = v.call_without_varargs(mpropdef, args)
diff --combined src/semantize/typing.nit
@@@ -47,7 -47,7 +47,7 @@@ private class TypeVisito
        # The analyzed property
        var mpropdef: nullable MPropDef
  
 -      var selfvariable: Variable = new Variable("self")
 +      var selfvariable = new Variable("self")
  
        # Is `self` use restricted?
        # * no explicit `self`
                return sup
        end
  
 +      # Special verification on != and == for null
 +      # Return true
 +      fun null_test(anode: ABinopExpr)
 +      do
 +              var mtype = anode.n_expr.mtype
 +              var mtype2 = anode.n_expr2.mtype
 +
 +              if mtype == null or mtype2 == null then return
 +
 +              if not mtype2 isa MNullType then return
 +
 +              # Check of useless null
 +              if not mtype isa MNullableType then
 +                      if not anchor_to(mtype) isa MNullableType then
 +                              modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
 +                      end
 +                      return
 +              end
 +
 +              # Check for type adaptation
 +              var variable = anode.n_expr.its_variable
 +              if variable == null then return
 +
 +              if anode isa AEqExpr then
 +                      anode.after_flow_context.when_true.set_var(variable, mtype2)
 +                      anode.after_flow_context.when_false.set_var(variable, mtype.mtype)
 +              else if anode isa ANeExpr then
 +                      anode.after_flow_context.when_false.set_var(variable, mtype2)
 +                      anode.after_flow_context.when_true.set_var(variable, mtype.mtype)
 +              else
 +                      abort
 +              end
 +      end
 +
        fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty
        do
                return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name)
        fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
        do
                if col.length == 1 then return col.first
 -              var res = new Array[nullable MType]
                for t1 in col do
                        if t1 == null then continue # return null
                        var found = true
@@@ -494,8 -461,8 +494,8 @@@ en
  
  redef class FlowContext
        # Store changes of types because of type evolution
 -      private var vars: HashMap[Variable, nullable MType] = new HashMap[Variable, nullable MType]
 -      private var cache: HashMap[Variable, nullable Array[nullable MType]] = new HashMap[Variable, nullable Array[nullable MType]]
 +      private var vars = new HashMap[Variable, nullable MType]
 +      private var cache = new HashMap[Variable, nullable Array[nullable MType]]
  
        # Adapt the variable to a static type
        # Warning1: do not modify vars directly.
@@@ -780,7 -747,7 +780,7 @@@ redef class AContinueExp
        do
                var nexpr = self.n_expr
                if nexpr != null then
 -                      var mtype = v.visit_expr(nexpr)
 +                      v.visit_expr(nexpr)
                end
                self.is_typed = true
        end
@@@ -791,7 -758,7 +791,7 @@@ redef class ABreakExp
        do
                var nexpr = self.n_expr
                if nexpr != null then
 -                      var mtype = v.visit_expr(nexpr)
 +                      v.visit_expr(nexpr)
                end
                self.is_typed = true
        end
@@@ -804,9 -771,9 +804,9 @@@ redef class AReturnExp
                var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype
                if nexpr != null then
                        if ret_type != null then
 -                              var mtype = v.visit_expr_subtype(nexpr, ret_type)
 +                              v.visit_expr_subtype(nexpr, ret_type)
                        else
 -                              var mtype = v.visit_expr(nexpr)
 +                              v.visit_expr(nexpr)
                                v.error(self, "Error: Return with value in a procedure.")
                        end
                else if ret_type != null then
@@@ -890,6 -857,9 +890,9 @@@ redef class AForExp
        var method_key: nullable CallSite
        var method_finish: nullable CallSite
  
+       var method_lt: nullable CallSite
+       var method_successor: nullable CallSite
        private fun do_type_iterator(v: TypeVisitor, mtype: MType)
        do
                if mtype isa MNullType then
                        end
                        self.method_key = keydef
                end
+               if self.variables.length == 1 and n_expr isa ARangeExpr then
+                       var variable = variables.first
+                       var vtype = variable.declared_type.as(not null)
+                       if n_expr isa AOrangeExpr then
+                               self.method_lt = v.get_method(self, vtype, "<", false)
+                       else
+                               self.method_lt = v.get_method(self, vtype, "<=", false)
+                       end
+                       self.method_successor = v.get_method(self, vtype, "successor", false)
+               end
        end
  
        redef fun accept_typing(v)
@@@ -1240,9 -1223,9 +1256,9 @@@ redef class AIsaExp
  
                var variable = self.n_expr.its_variable
                if variable != null then
 -                      var orig = self.n_expr.mtype
 -                      var from = if orig != null then orig.to_s else "invalid"
 -                      var to = if mtype != null then mtype.to_s else "invalid"
 +                      #var orig = self.n_expr.mtype
 +                      #var from = if orig != null then orig.to_s else "invalid"
 +                      #var to = if mtype != null then mtype.to_s else "invalid"
                        #debug("adapt {variable}: {from} -> {to}")
                        self.after_flow_context.when_true.set_var(variable, mtype)
                end
@@@ -1369,7 -1352,16 +1385,7 @@@ redef class AEqExp
        redef fun accept_typing(v)
        do
                super
 -
 -              var variable = self.n_expr.its_variable
 -              if variable == null then return
 -              var mtype = self.n_expr2.mtype
 -              if not mtype isa MNullType then return
 -              var vartype = v.get_variable(self, variable)
 -              if not vartype isa MNullableType then return
 -              self.after_flow_context.when_true.set_var(variable, mtype)
 -              self.after_flow_context.when_false.set_var(variable, vartype.mtype)
 -              #debug("adapt {variable}:{vartype} ; true->{mtype} false->{vartype.mtype}")
 +              v.null_test(self)
        end
  end
  redef class ANeExpr
        redef fun accept_typing(v)
        do
                super
 -
 -              var variable = self.n_expr.its_variable
 -              if variable == null then return
 -              var mtype = self.n_expr2.mtype
 -              if not mtype isa MNullType then return
 -              var vartype = v.get_variable(self, variable)
 -              if not vartype isa MNullableType then return
 -              self.after_flow_context.when_false.set_var(variable, mtype)
 -              self.after_flow_context.when_true.set_var(variable, vartype.mtype)
 -              #debug("adapt {variable}:{vartype} ; true->{vartype.mtype} false->{mtype}")
 +              v.null_test(self)
        end
  end
  redef class ALtExpr