niti, nitg & rta: use lookup_first_definition
[nit.git] / src / global_compiler.nit
index f170659..e09d040 100644 (file)
@@ -35,93 +35,97 @@ redef class ToolContext
        # --no-cc
        var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
 
+       # --make-flags
+       var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
+
        # --hardening
        var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
 
+       # --no-shortcut-range
+       var opt_no_shortcut_range: OptionBool = 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")
+
+       # --no-check-initialization
+       var opt_no_check_initialization: OptionBool = new OptionBool("Disable isset tests at the end of constructors (dangerous)", "--no-check-initialization")
+
+       # --no-check-assert
+       var opt_no_check_assert: OptionBool = 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")
+
+       # --no-check-other
+       var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
+
+       # --typing-test-metrics
+       var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
+
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_hardening)
+               self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_hardening, self.opt_no_shortcut_range)
+               self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other)
+               self.option_context.add_option(self.opt_typing_test_metrics)
        end
 end
 
 redef class ModelBuilder
+       # Entry point to performs a global compilation on the AST of a complete program.
+       # `mainmodule` is the main module of the program
+       # `runtime_type_analysis` is a already computer type analysis.
        fun run_global_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis)
        do
                var time0 = get_time
                self.toolcontext.info("*** COMPILING TO C ***", 1)
 
                var compiler = new GlobalCompiler(mainmodule, runtime_type_analysis, self)
-               var v = new GlobalCompilerVisitor(compiler)
-
-               v.add_decl("#include <stdlib.h>")
-               v.add_decl("#include <stdio.h>")
-               v.add_decl("#include <string.h>")
+               compiler.compile_header
+               var v = compiler.header
 
-               # TODO: Better way to activate the GC
-               #v.add_decl("#include <gc/gc.h>")
-               v.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
-
-               # Declare structure for each live type
-
-               v.add_decl("typedef struct \{int classid;\} val;")
                for t in runtime_type_analysis.live_types do
                        compiler.declare_runtimeclass(v, t)
                end
 
-               v.add_decl("extern int glob_argc;")
-               v.add_decl("extern char **glob_argv;")
-               v.add_decl("extern val *glob_sys;")
-
-               # Class names (for the class_name and output_class_name methods)
-
-               v.add_decl("extern const char const * class_names[];")
-               v.add("const char const * class_names[] = \{")
-               for t in runtime_type_analysis.live_types do
-                       v.add("\"{t}\", /* {compiler.classid(t)} */")
-               end
-               v.add("\};")
-               compiler.header = v
+               compiler.compile_class_names
 
                # Init instance code (allocate and init-arguments)
-
                for t in runtime_type_analysis.live_types do
-                       if t.ctype != "val*" then continue
-                       compiler.generate_init_instance(t)
+                       if t.ctype == "val*" then
+                               compiler.generate_init_instance(t)
+                               compiler.generate_check_init_instance(t)
+                       else
+                               compiler.generate_box_instance(t)
+                       end
                end
 
                # The main function of the C
-
-               v = new GlobalCompilerVisitor(compiler)
-               v.add_decl("int glob_argc;")
-               v.add_decl("char **glob_argv;")
-               v.add_decl("val *glob_sys;")
-               v.add_decl("int main(int argc, char** argv) \{")
-               v.add("glob_argc = argc; glob_argv = argv;")
-               var main_type = mainmodule.sys_type
-               if main_type == null then return # Nothing to compile
-               var glob_sys = v.init_instance(main_type)
-               v.add("glob_sys = {glob_sys};")
-               var main_init = mainmodule.try_get_primitive_method("init", main_type)
-               if main_init != null then
-                       v.send(main_init, [glob_sys])
-               end
-               var main_method = mainmodule.try_get_primitive_method("main", main_type)
-               if main_method != null then
-                       v.send(main_method, [glob_sys])
-               end
-               v.add("\}")
+               compiler.compile_main_function
 
                # Compile until all runtime_functions are visited
-
                while not compiler.todos.is_empty do
                        var m = compiler.todos.shift
-                       self.toolcontext.info("Compile {m.mmethoddef} for {m.recv} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
-                       m.mmethoddef.compile_to_c(compiler, self, m.recv)
+                       self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
+                       m.compile_to_c(compiler)
                end
                self.toolcontext.info("Total methods to compile to C: {compiler.visitors.length}", 2)
 
+               compiler.display_stats
+
+               var time1 = get_time
+               self.toolcontext.info("*** END VISITING: {time1-time0} ***", 2)
+               write_and_make(compiler)
+       end
+
+       protected fun write_and_make(compiler: GlobalCompiler)
+       do
+               var mainmodule = compiler.mainmodule
+
                # Generate the .h and .c files
+               # A single C file regroups many compiled rumtime functions
+               # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
+               var time0 = get_time
 
                var outname = self.toolcontext.opt_output.value
                if outname == null then
@@ -145,7 +149,7 @@ redef class ModelBuilder
                var i = 0
                for vis in compiler.visitors do
                        count += vis.lines.length
-                       if file == null or count > 10000 then
+                       if file == null or count > 10000 or vis.file_break then
                                i += 1
                                if file != null then file.close
                                var cfilename = ".nit_compile/{mainmodule.name}.{i}.c"
@@ -178,13 +182,23 @@ redef class ModelBuilder
                makefile.write("all: {outname}\n\n")
 
                var ofiles = new Array[String]
+               # Compile each generated file
                for f in cfiles do
                        var o = f.strip_extension(".c") + ".o"
-                       makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -I .nit_compile -I ../clib -c -o {o} {f}\n\n")
+                       makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
                        ofiles.add(o)
                end
-
-               makefile.write("{outname}: {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\t$(CC) -Wl,--warn-unresolved-symbols $(CFLAGS) $(LDFLAGS) $(LDLIBS) -I .nit_compile -I ../clib -o {outname} {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\n")
+               # Compile each required extern body into a specific .o
+               for f in compiler.extern_bodies do
+                       i += 1
+                       var o = ".nit_compile/{mainmodule.name}.{i}.o"
+                       makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
+                       ofiles.add(o)
+               end
+               # Link edition
+               makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n")
+               # Clean
+               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
                makefile.close
                self.toolcontext.info("Generated makefile: {makename}", 2)
 
@@ -197,13 +211,15 @@ redef class ModelBuilder
 
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
-               self.toolcontext.info("make -f {makename} -j 4", 2)
+               var makeflags = self.toolcontext.opt_make_flags.value
+               if makeflags == null then makeflags = ""
+               self.toolcontext.info("make -B -f {makename} -j 4 {makeflags}", 2)
 
                var res
                if self.toolcontext.verbose_level >= 3 then
-                       res = sys.system("make -f {makename} -j 4 2>&1")
+                       res = sys.system("make -B -f {makename} -j 4 {makeflags} 2>&1")
                else
-                       res = sys.system("make -f {makename} -j 4 2>&1 >/dev/null")
+                       res = sys.system("make -B -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
                end
                if res != 0 then
                        toolcontext.error(null, "make failed! Error code: {res}.")
@@ -215,9 +231,9 @@ redef class ModelBuilder
 end
 
 # Singleton that store the knowledge about the compilation process
-private class GlobalCompiler
+class GlobalCompiler
        # The main module of the program
-       var mainmodule: MModule
+       var mainmodule: MModule writable
 
        # The result of the RTA (used to know live types and methods)
        var runtime_type_analysis: RapidTypeAnalysis
@@ -225,11 +241,12 @@ private class GlobalCompiler
        # The modeulbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder
 
-       # Is hardening asked (see --hardening)
+       # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
        init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, modelbuilder: ModelBuilder)
        do
+               self.header = new_visitor
                self.mainmodule = mainmodule
                self.runtime_type_analysis = runtime_type_analysis
                self.modelbuilder = modelbuilder
@@ -241,15 +258,66 @@ private class GlobalCompiler
                end
        end
 
+       # Force the creation of a new file
+       # The point is to avoid contamination between must-be-compiled-separately files
+       fun new_file
+       do
+               var v = self.new_visitor
+               v.file_break = true
+       end
+
+       fun compile_header do
+               var v = self.header
+               self.header.add_decl("#include <stdlib.h>")
+               self.header.add_decl("#include <stdio.h>")
+               self.header.add_decl("#include <string.h>")
+               self.header.add_decl("#ifndef NOBOEHM")
+               self.header.add_decl("#include <gc/gc.h>")
+               self.header.add_decl("#ifdef NOBOEHM_ATOMIC")
+               self.header.add_decl("#undef GC_MALLOC_ATOMIC")
+               self.header.add_decl("#define GC_MALLOC_ATOMIC(x) GC_MALLOC(x)")
+               self.header.add_decl("#endif /*NOBOEHM_ATOMIC*/")
+               self.header.add_decl("#else /*NOBOEHM*/")
+               self.header.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
+               self.header.add_decl("#define GC_MALLOC_ATOMIC(x) calloc(1, (x))")
+               self.header.add_decl("#endif /*NOBOEHM*/")
+
+               compile_header_structs
+
+               # Global variable used by the legacy native interface
+               self.header.add_decl("extern int glob_argc;")
+               self.header.add_decl("extern char **glob_argv;")
+               self.header.add_decl("extern val *glob_sys;")
+       end
+
+       # Class names (for the class_name and output_class_name methods)
+       protected fun compile_class_names do
+               self.header.add_decl("extern const char const * class_names[];")
+               self.header.add("const char const * class_names[] = \{")
+               for t in self.runtime_type_analysis.live_types do
+                       self.header.add("\"{t}\", /* {self.classid(t)} */")
+               end
+               self.header.add("\};")
+       end
+
+       # Declaration of structures the live Nit types
+       # Each live type is generated as an independent C `struct' type.
+       # They only share a common first field `classid` used to implement the polymorphism.
+       # Usualy, all C variables that refers to a Nit object are typed on the abstract struct `val' that contains only the `classid` field.
+       protected fun compile_header_structs do
+               self.header.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */")
+       end
+
        # Subset of runtime_type_analysis.live_types that contains only primitive types
+       # Used to implement the equal test
        var live_primitive_types: Array[MClassType]
 
        # runtime_functions that need to be compiled
-       private var todos: List[RuntimeFunction] = new List[RuntimeFunction]
+       private var todos: List[AbstractRuntimeFunction] = new List[AbstractRuntimeFunction]
 
        # runtime_functions already seen (todo or done)
-       private var seen: HashSet[RuntimeFunction] = new HashSet[RuntimeFunction]
-       fun todo(m: RuntimeFunction)
+       private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction]
+       fun todo(m: AbstractRuntimeFunction)
        do
                if seen.has(m) then return
                todos.add(m)
@@ -259,11 +327,12 @@ private class GlobalCompiler
        # Where global declaration are stored (the main .h)
        #
        # FIXME: should not be a vistor but just somewhere to store lines
-       # FIXME: should not have a global .h since its does no helps recompilation
-       var header: nullable GlobalCompilerVisitor = null
+       # FIXME: should not have a global .h since it does not help recompilations
+       var header: GlobalCompilerVisitor writable
 
        # The list of all associated visitors
        # Used to generate .c files
+       # FIXME: should not be vistors but just somewhere to store lines
        private var visitors: List[GlobalCompilerVisitor] = new List[GlobalCompilerVisitor]
 
        # List of additional .c files required to compile (native interface)
@@ -281,7 +350,7 @@ private class GlobalCompiler
        end
 
        # Cache for classid (computed by declare_runtimeclass)
-       private var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
+       protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
 
        # Declare C structures and identifiers for a runtime class
        fun declare_runtimeclass(v: GlobalCompilerVisitor, mtype: MClassType)
@@ -292,11 +361,24 @@ private class GlobalCompiler
                var idname = "ID_" + mtype.c_name
                self.classids[mtype] = idname
                v.add_decl("#define {idname} {idnum} /* {mtype} */")
+
                v.add_decl("struct {mtype.c_name} \{")
                v.add_decl("int classid; /* must be {idname} */")
+
+               if mtype.mclass.name == "NativeArray" then
+                       # NativeArrays are just a instance header followed by an array of values
+                       v.add_decl("{mtype.arguments.first.ctype} values[1];")
+               end
+
                if mtype.ctype != "val*" then
+                       # Is the Nit type is native then the struct is a box with two fields:
+                       # * the `classid` to be polymorph
+                       # * the `value` that contains the native value.
                        v.add_decl("{mtype.ctype} value;")
                end
+
+               # Collect all attributes and associate them a field in the structure.
+               # Note: we do not try to optimize the order and helps CC to optimize the client code.
                for cd in mtype.collect_mclassdefs(self.mainmodule) do
                        for p in cd.intro_mproperties do
                                if not p isa MAttribute then continue
@@ -313,27 +395,91 @@ private class GlobalCompiler
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
                assert mtype.ctype == "val*"
-               var v = new GlobalCompilerVisitor(self)
+               var v = self.new_visitor
+
+               var is_native_array = mtype.mclass.name == "NativeArray"
+
+               var sig
+               if is_native_array then
+                       sig = "int length"
+               else
+                       sig = "void"
+               end
 
-               self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}(void);")
+               self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig});")
                v.add_decl("/* allocate {mtype} */")
-               v.add_decl("{mtype.ctype} NEW_{mtype.c_name}(void) \{")
+               v.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig}) \{")
                var res = v.new_var(mtype)
                res.is_exact = true
-               v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+               if is_native_array then
+                       var mtype_elt = mtype.arguments.first
+                       v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));")
+               else
+                       v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+               end
                v.add("{res}->classid = {self.classid(mtype)};")
 
+               self.generate_init_attr(v, res, mtype)
+               v.add("return {res};")
+               v.add("\}")
+       end
+
+       fun generate_check_init_instance(mtype: MClassType)
+       do
+               if self.modelbuilder.toolcontext.opt_no_check_initialization.value then return
+
+               var v = self.new_visitor
+               var res = new RuntimeVariable("self", mtype, mtype)
+               self.header.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype});")
+               v.add_decl("/* allocate {mtype} */")
+               v.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype} {res}) \{")
+               self.generate_check_attr(v, res, mtype)
+               v.add("\}")
+       end
+
+       # Generate code that collect initialize the attributes on a new instance
+       fun generate_init_attr(v: GlobalCompilerVisitor, recv: RuntimeVariable, mtype: MClassType)
+       do
                for cd in mtype.collect_mclassdefs(self.mainmodule)
                do
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
-                                       npropdef.init_expr(v, res)
+                                       npropdef.init_expr(v, recv)
                                end
                        end
                end
-               v.add("return {res};")
+       end
+
+       # Generate a check-init-instance
+       fun generate_check_attr(v: GlobalCompilerVisitor, recv: RuntimeVariable, mtype: MClassType)
+       do
+               for cd in mtype.collect_mclassdefs(self.mainmodule)
+               do
+                       var n = self.modelbuilder.mclassdef2nclassdef[cd]
+                       for npropdef in n.n_propdefs do
+                               if npropdef isa AAttrPropdef then
+                                       npropdef.check_expr(v, recv)
+                               end
+                       end
+               end
+       end
+
+       fun generate_box_instance(mtype: MClassType)
+       do
+               assert self.runtime_type_analysis.live_types.has(mtype)
+               assert mtype.ctype != "val*"
+               var v = self.new_visitor
+
+               self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
+               v.add_decl("/* allocate {mtype} */")
+               v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
+               v.add("struct {mtype.c_name}*res = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+               v.add("res->classid = {self.classid(mtype)};")
+               v.add("res->value = value;")
+               v.add("return (val*)res;")
                v.add("\}")
+
        end
 
        # look for a needed .h and .c file for a given .nit source-file
@@ -357,8 +503,132 @@ private class GlobalCompiler
                if tryfile.file_exists then
                        self.extern_bodies.add(tryfile)
                end
-               #(new OFStream.open("{file.basename("")}._nitni.h")).close
        end
+
+       # Initialize a visitor specific for a compiler engine
+       fun new_visitor: GlobalCompilerVisitor do return new GlobalCompilerVisitor(self)
+
+       var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
+       var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
+       var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
+       var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
+
+       private fun init_count_type_test_tags: HashMap[String, Int]
+       do
+               var res = new HashMap[String, Int]
+               for tag in count_type_test_tags do
+                       res[tag] = 0
+               end
+               return res
+       end
+
+       # Generate the main C function.
+       # This function:
+       # allocate the Sys object if it exists
+       # call init if is exists
+       # call main if it exists
+       fun compile_main_function
+       do
+               var v = self.new_visitor
+               v.add_decl("int glob_argc;")
+               v.add_decl("char **glob_argv;")
+               v.add_decl("val *glob_sys;")
+
+               if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+                       for tag in count_type_test_tags do
+                               v.add_decl("long count_type_test_resolved_{tag};")
+                               v.add_decl("long count_type_test_unresolved_{tag};")
+                               v.add_decl("long count_type_test_skipped_{tag};")
+                               v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
+                               v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
+                               v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
+                       end
+               end
+               v.add_decl("int main(int argc, char** argv) \{")
+               v.add("glob_argc = argc; glob_argv = argv;")
+               var main_type = mainmodule.sys_type
+               if main_type != null then
+                       var mainmodule = v.compiler.mainmodule
+                       var glob_sys = v.init_instance(main_type)
+                       v.add("glob_sys = {glob_sys};")
+                       var main_init = mainmodule.try_get_primitive_method("init", main_type)
+                       if main_init != null then
+                               v.send(main_init, [glob_sys])
+                       end
+                       var main_method = mainmodule.try_get_primitive_method("main", main_type)
+                       if main_method != null then
+                               v.send(main_method, [glob_sys])
+                       end
+               end
+
+               if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+                       v.add_decl("long count_type_test_resolved_total = 0;")
+                       v.add_decl("long count_type_test_unresolved_total = 0;")
+                       v.add_decl("long count_type_test_skipped_total = 0;")
+                       v.add_decl("long count_type_test_total_total = 0;")
+                       for tag in count_type_test_tags do
+                               v.add_decl("long count_type_test_total_{tag};")
+                               v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
+                               v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
+                               v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
+                               v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
+                               v.add("count_type_test_total_total += count_type_test_total_{tag};")
+                       end
+                       v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
+                       v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
+                       var tags = count_type_test_tags.to_a
+                       tags.add("total")
+                       for tag in tags do
+                               v.add("printf(\"{tag}\");")
+                               v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
+                               v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
+                               v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
+                               v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
+                       end
+               end
+               v.add("return 0;")
+               v.add("\}")
+
+       end
+
+       fun div(a,b:Int):String
+       do
+               if b == 0 then return "n/a"
+               return ((a*10000/b).to_f / 100.0).to_precision(2)
+       end
+
+       fun display_stats
+       do
+               if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+                       print "# static count_type_test"
+                       print "\tresolved:\tunresolved\tskipped\ttotal"
+                       var count_type_test_total = init_count_type_test_tags
+                       count_type_test_resolved["total"] = 0
+                       count_type_test_unresolved["total"] = 0
+                       count_type_test_skipped["total"] = 0
+                       count_type_test_total["total"] = 0
+                       for tag in count_type_test_tags do
+                               count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
+                               count_type_test_resolved["total"] += count_type_test_resolved[tag]
+                               count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
+                               count_type_test_skipped["total"] += count_type_test_skipped[tag]
+                               count_type_test_total["total"] += count_type_test_total[tag]
+                       end
+                       var count_type_test = count_type_test_total["total"]
+                       var tags = count_type_test_tags.to_a
+                       tags.add("total")
+                       for tag in tags do
+                               printn tag
+                               printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
+                               printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
+                               printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
+                               printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
+                               print ""
+                       end
+               end
+       end
+
+       private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
 end
 
 redef class String
@@ -425,10 +695,15 @@ redef class MType
                return "val*"
        end
 
+       fun ctypename: String
+       do
+               return "val"
+       end
+
        # Return the name of the C structure associated to a Nit live type
        # FIXME: move to GlobalCompiler so we can check that self is a live type
        fun c_name: String is abstract
-       private var c_name_cache: nullable String
+       protected var c_name_cache: nullable String protected writable
 end
 
 redef class MClassType
@@ -454,14 +729,36 @@ redef class MClassType
                else if mclass.name == "NativeString" then
                        return "char*"
                else if mclass.name == "NativeArray" then
-                       assert self isa MGenericType
-                       return "{self.arguments.first.ctype}*"
+                       #return "{self.arguments.first.ctype}*"
+                       return "val*"
                else if mclass.kind == extern_kind then
                        return "void*"
                else
                        return "val*"
                end
        end
+
+       redef fun ctypename: String
+       do
+               if mclass.name == "Int" then
+                       return "l"
+               else if mclass.name == "Bool" then
+                       return "s"
+               else if mclass.name == "Char" then
+                       return "c"
+               else if mclass.name == "Float" then
+                       return "d"
+               else if mclass.name == "NativeString" then
+                       return "str"
+               else if mclass.name == "NativeArray" then
+                       #return "{self.arguments.first.ctype}*"
+                       return "val"
+               else if mclass.kind == extern_kind then
+                       return "ptr"
+               else
+                       return "val"
+               end
+       end
 end
 
 redef class MGenericType
@@ -478,29 +775,52 @@ redef class MGenericType
        end
 end
 
-redef class MNullableType
-       redef fun c_name
+# A C function associated to a Nit method
+# Because of customization, a given Nit method can be compiler more that once
+abstract class AbstractRuntimeFunction
+       # The associated Nit method
+       var mmethoddef: MMethodDef
+
+       # The mangled c name of the runtime_function
+       # Subclasses should redefine `build_c_name` instead
+       fun c_name: String
        do
                var res = self.c_name_cache
                if res != null then return res
-               res = "nullable_{self.mtype.c_name}"
+               res = self.build_c_name
                self.c_name_cache = res
                return res
        end
+
+       # Non cached version of `c_name`
+       protected fun build_c_name: String is abstract
+
+       private var c_name_cache: nullable String = null
+
+       # Implements a call of the runtime_function
+       # May inline the body or generate a C function call
+       fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
+
+       # Generate the code for the RuntimeFunction
+       # Warning: compile more than once compilation makes CC unhappy
+       fun compile_to_c(compiler: GlobalCompiler) is abstract
 end
 
-# A C function associated to a Nit method
-# Because of customization, a given Nit method can be compiler more that once
-private class RuntimeFunction
-       # The associated Nit method
-       var mmethoddef: MMethodDef
+# A runtime function customized on a specific monomrph receiver type
+private class CustomizedRuntimeFunction
+       super AbstractRuntimeFunction
 
        # The considered reciever
        # (usually is a live type but no strong guarantee)
        var recv: MClassType
 
-       # The mangled c name of the runtime_function
-       fun c_name: String
+       init(mmethoddef: MMethodDef, recv: MClassType)
+       do
+               super(mmethoddef)
+               self.recv = recv
+       end
+
+       redef fun build_c_name: String
        do
                var res = self.c_name_cache
                if res != null then return res
@@ -513,12 +833,10 @@ private class RuntimeFunction
                return res
        end
 
-       private var c_name_cache: nullable String = null
-
        redef fun ==(o)
        # used in the compiler worklist
        do
-               if not o isa RuntimeFunction then return false
+               if not o isa CustomizedRuntimeFunction then return false
                if self.mmethoddef != o.mmethoddef then return false
                if self.recv != o.recv then return false
                return true
@@ -540,13 +858,81 @@ private class RuntimeFunction
                end
        end
 
-       # Implements a call of the runtime_function
-       # May inline the body
-       fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
+       # compile the code customized for the reciever
+       redef fun compile_to_c(compiler)
+       do
+               var recv = self.recv
+               var mmethoddef = self.mmethoddef
+               if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then
+                       print("problem: why do we compile {self} for {recv}?")
+                       abort
+               end
+
+               var v = compiler.new_visitor
+               var selfvar = new RuntimeVariable("self", recv, recv)
+               if compiler.runtime_type_analysis.live_types.has(recv) then
+                       selfvar.is_exact = true
+               end
+               var arguments = new Array[RuntimeVariable]
+               var frame = new Frame(v, mmethoddef, recv, arguments)
+               v.frame = frame
+
+               var sig = new Buffer
+               var comment = new Buffer
+               var ret = mmethoddef.msignature.return_mtype
+               if ret != null then
+                       ret = v.resolve_for(ret, selfvar)
+                       sig.append("{ret.ctype} ")
+               else if mmethoddef.mproperty.is_new then
+                       ret = recv
+                       sig.append("{ret.ctype} ")
+               else
+                       sig.append("void ")
+               end
+               sig.append(self.c_name)
+               sig.append("({recv.ctype} {selfvar}")
+               comment.append("(self: {recv}")
+               arguments.add(selfvar)
+               for i in [0..mmethoddef.msignature.arity[ do
+                       var mtype = mmethoddef.msignature.mparameters[i].mtype
+                       if i == mmethoddef.msignature.vararg_rank then
+                               mtype = v.get_class("Array").get_mtype([mtype])
+                       end
+                       mtype = v.resolve_for(mtype, selfvar)
+                       comment.append(", {mtype}")
+                       sig.append(", {mtype.ctype} p{i}")
+                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
+                       arguments.add(argvar)
+               end
+               sig.append(")")
+               comment.append(")")
+               if ret != null then
+                       comment.append(": {ret}")
+               end
+               compiler.header.add_decl("{sig};")
+
+               v.add_decl("/* method {self} for {comment} */")
+               v.add_decl("{sig} \{")
+               #v.add("printf(\"method {self} for {comment}\\n\");")
+               if ret != null then
+                       frame.returnvar = v.new_var(ret)
+               end
+               frame.returnlabel = v.get_name("RET_LABEL")
+
+               mmethoddef.compile_inside_to_c(v, arguments)
+
+               v.add("{frame.returnlabel.as(not null)}:;")
+               if ret != null then
+                       v.add("return {frame.returnvar.as(not null)};")
+               end
+               v.add("\}")
+       end
+
+       redef fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var ret = self.mmethoddef.msignature.return_mtype
                if self.mmethoddef.mproperty.is_new then
-                       ret = arguments.first.mtype
+                       ret = recv
                end
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
@@ -579,9 +965,11 @@ private class RuntimeFunction
        end
 end
 
-# A runtime variable hold a runtime value in C
-# Runtime variables are associated to local variables and intermediate results
-private class RuntimeVariable
+# A runtime variable hold a runtime value in C.
+# Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
+#
+# The tricky point is that a single C variable can be associated to more than one RuntimeVariable because the static knowledge of the type of an expression can vary in the C code.
+class RuntimeVariable
        # The name of the variable in the C code
        var name: String
 
@@ -589,11 +977,20 @@ private class RuntimeVariable
        var mtype: MType
 
        # The current casted type of the variable (as known in Nit)
-       var mcasttype: MType
+       var mcasttype: MType writable
 
        # If the variable exaclty a mcasttype?
        # false (usual value) means that the variable is a mcasttype or a subtype.
-       var is_exact: Bool = false
+       var is_exact: Bool writable = false
+
+       init(name: String, mtype: MType, mcasttype: MType)
+       do
+               self.name = name
+               self.mtype = mtype
+               self.mcasttype = mcasttype
+               assert not mtype.need_anchor
+               assert not mcasttype.need_anchor
+       end
 
        redef fun to_s do return name
 
@@ -611,13 +1008,13 @@ private class RuntimeVariable
                else
                        type_str = "{mtype}({mcasttype}{exact_str})"
                end
-               return "<{inspect_head} {name}:{type_str}>"
+               return "<{name}:{type_str}>"
        end
 end
 
-# Visit the AST to generate the C code.
+# A visitor on the AST of property definition that generate the C code.
 # Because of inlining, a visitor can visit more than one property.
-private class GlobalCompilerVisitor
+class GlobalCompilerVisitor
        # The associated compiler
        var compiler: GlobalCompiler
 
@@ -627,6 +1024,8 @@ private class GlobalCompilerVisitor
                compiler.visitors.add(self)
        end
 
+       var file_break: Bool = false
+
        # Alias for self.compiler.mainmodule.object_type
        fun object_type: MClassType do return self.compiler.mainmodule.object_type
 
@@ -642,11 +1041,11 @@ private class GlobalCompilerVisitor
        # Force to get the primitive property named `name' in the instance `recv' or abort
        fun get_property(name: String, recv: MType): MMethod
        do
-               return self.compiler.mainmodule.force_get_primitive_method(name, recv)
+               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule)
        end
 
        # The current Frame
-       var frame: nullable Frame
+       var frame: nullable Frame writable
 
        # Anchor a type to the main module and the current receiver
        fun anchor(mtype: MType): MType
@@ -673,9 +1072,10 @@ private class GlobalCompilerVisitor
        private var decl_lines: List[String] = new List[String]
 
        # The current visited AST node
-       var current_node: nullable AExpr = null
+       var current_node: nullable ANode = null
 
-       # Compile an expression an return its
+       # Compile an expression an return its result
+       # `mtype` is the expected return type, pass null if no specific type is expected.
        fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
        do
                var old = self.current_node
@@ -685,25 +1085,36 @@ private class GlobalCompilerVisitor
                        mtype = self.anchor(mtype)
                        res = self.autobox(res, mtype)
                end
+               res = autoadapt(res, nexpr.mtype.as(not null))
+               var implicit_cast_to = nexpr.implicit_cast_to
+               if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then
+                       var castres = self.type_test(res, implicit_cast_to, "auto")
+                       self.add("if (!{castres}) \{")
+                       self.add_abort("Cast failed")
+                       self.add("\}")
+                       res = autoadapt(res, implicit_cast_to)
+               end
                self.current_node = old
                return res
        end
 
        # Unsafely cast a value to a new type
+       # ie the result share the same C variable but my have a different mcasttype
+       # NOTE: if the adaptation is useless then `value' is returned as it.
        # ENSURE: return.name == value.name
        fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
        do
                mtype = self.anchor(mtype)
-               if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
+               var valmtype = value.mcasttype
+               if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                        return value
                end
 
-               var valmtype = value.mtype
                if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
-                       var res = new RuntimeVariable(value.name, value.mtype, valmtype.mtype)
+                       var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
                        return res
                else
-                       var res = new RuntimeVariable(value.name, value.mtype, mtype)
+                       var res = new RuntimeVariable(value.name, valmtype, mtype)
                        return res
                end
        end
@@ -712,10 +1123,12 @@ private class GlobalCompilerVisitor
        # ENSURE: result.mtype.ctype == mtype.ctype
        fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable
        do
-               if value.mtype.ctype == mtype.ctype then
+               if value.mtype == mtype then
+                       return value
+               else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
                        return value
                else if value.mtype.ctype == "val*" then
-                       return self.new_expr("((struct {mtype.c_name}*){value})->value /* autounbox from {value.mtype} to {mtype} */", mtype)
+                       return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if mtype.ctype == "val*" then
                        var valtype = value.mtype.as(MClassType)
                        var res = self.new_var(mtype)
@@ -724,12 +1137,10 @@ private class GlobalCompilerVisitor
                                self.add("printf(\"Dead code executed!\\n\"); exit(1);")
                                return res
                        end
-                       self.add("{res} = GC_MALLOC(sizeof(struct {valtype.c_name})); /* autobox from {value.mtype} to {mtype} */")
-                       self.add("{res}->classid = {self.compiler.classid(valtype)};")
-                       self.add("((struct {valtype.c_name}*){res})->value = {value};")
+                       self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
                else
-                       # Bad things will append!
+                       # Bad things will appen!
                        var res = self.new_var(mtype)
                        self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
                        self.add("printf(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); exit(1);")
@@ -752,7 +1163,7 @@ private class GlobalCompilerVisitor
                return expr(nexpr, bool_type)
        end
 
-       # Compile statement
+       # Compile a statement (if any)
        fun stmt(nexpr: nullable AExpr)
        do
                if nexpr == null then return
@@ -762,7 +1173,7 @@ private class GlobalCompilerVisitor
                self.current_node = old
        end
 
-       # Safely show a debug message on the current node and repeat the message in the C code
+       # Safely show a debug message on the current node and repeat the message in the C code as a comment
        fun debug(message: String)
        do
                var node = self.current_node
@@ -777,17 +1188,22 @@ private class GlobalCompilerVisitor
        # Return a new uninitialized local runtime_variable
        fun new_var(mtype: MType): RuntimeVariable
        do
-               if mtype isa MNullType then
-                       mtype = self.object_type
-               else
-                       mtype = self.anchor(mtype)
-               end
+               mtype = self.anchor(mtype)
                var name = self.get_name("var")
                var res = new RuntimeVariable(name, mtype, mtype)
                self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
                return res
        end
 
+       # Return a new uninitialized named runtime_variable
+       fun new_named_var(mtype: MType, name: String): RuntimeVariable
+       do
+               mtype = self.anchor(mtype)
+               var res = new RuntimeVariable(name, mtype, mtype)
+               self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
+               return res
+       end
+
        # Return a new local runtime_variable initialized with the C expression `cexpr'.
        fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
        do
@@ -814,6 +1230,20 @@ private class GlobalCompilerVisitor
 
        private var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
 
+       # Return an unique and stable identifier associated with an escapemark
+       fun escapemark_name(e: nullable EscapeMark): String
+       do
+               assert e != null
+               if escapemark_names.has_key(e) then return escapemark_names[e]
+               var name = e.name
+               if name == null then name = "label"
+               name = get_name(name)
+               escapemark_names[e] = name
+               return name
+       end
+
+       private var escapemark_names = new HashMap[EscapeMark, String]
+
        # Return a new name based on `s' and unique in the visitor
        fun get_name(s: String): String
        do
@@ -854,7 +1284,7 @@ private class GlobalCompilerVisitor
                        var types = [mtype]
                        return types
                end
-               var cache = self.collect_types_cache
+               var cache = self.compiler.collect_types_cache
                if cache.has_key(mtype) then
                        return cache[mtype]
                end
@@ -868,14 +1298,53 @@ private class GlobalCompilerVisitor
                return types
        end
 
-       private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
+       fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
+       do
+               if not mtype.need_anchor then return mtype
+               #debug("resolve for {mtype} to {recv}:{recv.mcasttype}(declared as {recv.mtype}) (in {self.reciever.to_s}:{self.reciever.mtype})")
+               var res = mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
+               return res
+       end
+
+       fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable])
+       do
+               var elttype = arguments.first.mtype
+               var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values"
+               if pname == "[]" then
+                       self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
+                       return
+               else if pname == "[]=" then
+                       self.add("{recv}[{arguments[1]}]={arguments[2]};")
+                       return
+               else if pname == "copy_to" then
+                       var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values"
+                       self.add("memcpy({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));")
+                       return
+               end
+       end
+
+       fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable])
+       do
+               self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
+       end
 
-       fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
+       # Add a check and an abort for a null reciever is needed
+       fun check_recv_notnull(recv: RuntimeVariable)
        do
-               if not mtype.need_anchor then return mtype
-               #debug("resolve for {mtype} to {recv}:{recv.mcasttype}(declared as {recv.mtype}) (in {self.reciever.to_s}:{self.reciever.mtype})")
-               var res = mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
-               return res
+               if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
+
+               var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
+               if maybenull then
+                       self.add("if ({recv} == NULL) \{")
+                       self.add_abort("Reciever is null")
+                       self.add("\}")
+               end
+       end
+
+       fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               var ret = self.send(callsite.mproperty, args)
+               return ret
        end
 
        # Generate a polymorphic send for the method `m' and the arguments `args'
@@ -895,36 +1364,37 @@ private class GlobalCompilerVisitor
                        res = self.new_var(ret)
                end
 
-               if types.is_empty then
-                       self.add("/*BUG: no live types for {args.first.inspect} . {m}*/")
-                       return res
-               end
                self.add("/* send {m} on {args.first.inspect} */")
                if args.first.mtype.ctype != "val*" then
-                       var propdefs = m.lookup_definitions(self.compiler.mainmodule, args.first.mtype)
-                       if propdefs.length == 0 then
+                       var mclasstype = args.first.mtype.as(MClassType)
+                       if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
                                self.add("/* skip, no method {m} */")
                                return res
                        end
-                       assert propdefs.length == 1
-                       var propdef = propdefs.first
-                       var res2 = self.call(propdef, args.first.mtype.as(MClassType), args)
+                       var propdef = m.lookup_first_definition(self.compiler.mainmodule, mclasstype)
+                       var res2 = self.call(propdef, mclasstype, args)
                        if res != null then self.assign(res, res2.as(not null))
                        return res
                end
-               if args.first.mcasttype isa MNullableType then
+               var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or m.name == "==" or m.name == "!="
+               if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then
+                       # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException
                        self.add("if ({args.first} == NULL) \{ /* Special null case */")
                        if m.name == "==" then
                                assert res != null
-                               if args[1].mcasttype.ctype == "val*" then
+                               if args[1].mcasttype isa MNullableType then
                                        self.add("{res} = ({args[1]} == NULL);")
+                               else if args[1].mcasttype isa MNullType then
+                                       self.add("{res} = 1; /* is null */")
                                else
                                        self.add("{res} = 0; /* {args[1].inspect} cannot be null */")
                                end
                        else if m.name == "!=" then
                                assert res != null
-                               if args[1].mcasttype.ctype == "val*" then
+                               if args[1].mcasttype isa MNullableType then
                                        self.add("{res} = ({args[1]} != NULL);")
+                               else if args[1].mcasttype isa MNullType then
+                                       self.add("{res} = 0; /* is null */")
                                else
                                        self.add("{res} = 1; /* {args[1].inspect} cannot be null */")
                                end
@@ -933,16 +1403,19 @@ private class GlobalCompilerVisitor
                        end
                        self.add "\} else"
                end
+               if types.is_empty then
+                       self.add("\{")
+                       self.add("/*BUG: no live types for {args.first.inspect} . {m}*/")
+                       self.bugtype(args.first)
+                       self.add("\}")
+                       return res
+               end
+
                self.add("switch({args.first}->classid) \{")
                var last = types.last
                var defaultpropdef: nullable MMethodDef = null
                for t in types do
-                       var propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
-                       if propdefs.length == 0 then
-                               self.add("/* skip {t}, no method {m} */")
-                               continue
-                       end
-                       var propdef = propdefs.first
+                       var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
                        if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
                                defaultpropdef = propdef
                                continue
@@ -968,6 +1441,14 @@ private class GlobalCompilerVisitor
                return res
        end
 
+       # Generate a monomorphic send for the method `m', the type `t' and the arguments `args'
+       fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               assert t isa MClassType
+               var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
+               return self.call(propdef, t, args)
+       end
+
        fun check_valid_reciever(recvtype: MClassType)
        do
                if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return
@@ -975,6 +1456,7 @@ private class GlobalCompilerVisitor
                abort
        end
 
+       # Generate a static call on a method definition
        fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                check_valid_reciever(recvtype)
@@ -985,9 +1467,41 @@ private class GlobalCompilerVisitor
                var recv = self.autobox(args.first, recvtype)
                recv = self.autoadapt(recv, recvtype)
 
-               var vararg_rank = m.msignature.vararg_rank
+               args = args.to_a
+               self.varargize(m, m.msignature.as(not null), args)
+               if args.length != m.msignature.arity + 1 then # because of self
+                       add("printf(\"NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.\\n\"); exit(1);")
+                       debug("NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.")
+                       return null
+               end
+
+               args.first = recv
+               var rm = new CustomizedRuntimeFunction(m, recvtype)
+               return rm.call(self, args)
+       end
+
+       fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable])
+       do
+               var recv = args.first
+               for i in [0..m.msignature.arity[ do
+                       var t = m.msignature.mparameters[i].mtype
+                       if i == m.msignature.vararg_rank then
+                               t = args[i+1].mtype
+                       end
+                       t = self.resolve_for(t, recv)
+                       args[i+1] = self.autobox(args[i+1], t)
+               end
+       end
+
+       # Transform varargs, in raw arguments, into a single argument of type Array
+       # Note: this method modify the given `args`
+       # If there is no vararg, then `args` is not modified.
+       fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
+       do
+               var recv = args.first
+               var vararg_rank = msignature.vararg_rank
                if vararg_rank >= 0 then
-                       assert args.length >= m.msignature.arity + 1 # because of self
+                       assert args.length >= msignature.arity + 1 # because of self
                        var rawargs = args
                        args = new Array[RuntimeVariable]
 
@@ -997,51 +1511,90 @@ private class GlobalCompilerVisitor
                                args.add(rawargs[i+1])
                        end
 
-                       var vararg_lastrank = vararg_rank + rawargs.length-1-m.msignature.arity
+                       var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
                        var vararg = new Array[RuntimeVariable]
                        for i in [vararg_rank..vararg_lastrank] do
                                vararg.add(rawargs[i+1])
                        end
-                       # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
-                       var elttype = m.msignature.parameter_mtypes[vararg_rank]
-                       elttype = self.resolve_for(elttype, recv)
-                       args.add(self.array_instance(vararg, elttype))
+
+                       var elttype = msignature.mparameters[vararg_rank].mtype
+                       args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
 
                        for i in [vararg_lastrank+1..rawargs.length-1[ do
                                args.add(rawargs[i+1])
                        end
-               else
-                       args = args.to_a
+                       rawargs.clear
+                       rawargs.add_all(args)
                end
-               assert args.length == m.msignature.arity + 1 # because of self
-
-               args.first = recv
-               var rm = new RuntimeFunction(m, recvtype)
-               return rm.call(self, args)
        end
 
-       fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable])
+       # Get an instance of a anny for a vararg
+       fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable
        do
-               var recv = args.first
-               for i in [0..m.msignature.arity[ do
-                       var t = m.msignature.parameter_mtypes[i]
-                       if i == m.msignature.vararg_rank then
-                               t = args[i+1].mtype
-                       end
-                       t = self.resolve_for(t, recv)
-                       args[i+1] = self.autobox(args[i+1], t)
-               end
+               # FIXME: this is currently buggy since recv is not exact
+
+               elttype = self.resolve_for(elttype, recv)
+               return self.array_instance(varargs, elttype)
        end
 
+
        fun bugtype(recv: RuntimeVariable)
        do
                if recv.mtype.ctype != "val*" then return
                self.add("fprintf(stderr, \"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
+               self.add("exit(1);")
+       end
+
+       # Generate a polymorphic attribute is_set test
+       fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
+       do
+               check_recv_notnull(recv)
+
+               var types = self.collect_types(recv)
+
+               var res = self.new_var(bool_type)
+
+               if types.is_empty then
+                       self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+                       self.bugtype(recv)
+                       return res
+               end
+               self.add("/* isset {a} on {recv.inspect} */")
+               self.add("switch({recv}->classid) \{")
+               var last = types.last
+               for t in types do
+                       if not self.compiler.hardening and t == last then
+                               self.add("default: /*{self.compiler.classid(t)}*/")
+                       else
+                               self.add("case {self.compiler.classid(t)}:")
+                       end
+                       var recv2 = self.autoadapt(recv, t)
+                       var ta = a.intro.static_mtype.as(not null)
+                       ta = self.resolve_for(ta, recv2)
+                       var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
+                       if not ta isa MNullableType then
+                               if ta.ctype == "val*" then
+                                       self.add("{res} = ({attr} != NULL);")
+                               else
+                                       self.add("{res} = 1; /*NOTYET isset on primitive attributes*/")
+                               end
+                       end
+                       self.add("break;")
+               end
+               if self.compiler.hardening then
+                       self.add("default: /* Bug */")
+                       self.bugtype(recv)
+               end
+               self.add("\}")
+
+               return res
        end
 
        # Generate a polymorphic attribute read
        fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
        do
+               check_recv_notnull(recv)
+
                var types = self.collect_types(recv)
 
                var ret = a.intro.static_mtype.as(not null)
@@ -1050,6 +1603,7 @@ private class GlobalCompilerVisitor
 
                if types.is_empty then
                        self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+                       self.bugtype(recv)
                        return res
                end
                self.add("/* read {a} on {recv.inspect} */")
@@ -1065,7 +1619,7 @@ private class GlobalCompilerVisitor
                        var ta = a.intro.static_mtype.as(not null)
                        ta = self.resolve_for(ta, recv2)
                        var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
-                       if not ta isa MNullableType then
+                       if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then
                                if ta.ctype == "val*" then
                                        self.add("if ({res2} == NULL) \{")
                                        self.add_abort("Uninitialized attribute {a.name}")
@@ -1089,10 +1643,13 @@ private class GlobalCompilerVisitor
        # Generate a polymorphic attribute write
        fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable)
        do
+               check_recv_notnull(recv)
+
                var types = self.collect_types(recv)
 
                if types.is_empty then
                        self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+                       self.bugtype(recv)
                        return
                end
                self.add("/* write {a} on {recv.inspect} */")
@@ -1130,15 +1687,31 @@ private class GlobalCompilerVisitor
        end
 
        # Generate a polymorphic subtype test
-       fun type_test(value: RuntimeVariable, mtype: MType): RuntimeVariable
+       fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable
        do
                mtype = self.anchor(mtype)
+               var mclasstype = mtype
+               if mtype isa MNullableType then mclasstype = mtype.mtype
+               assert mclasstype isa MClassType
+               if not self.compiler.runtime_type_analysis.live_cast_types.has(mclasstype) then
+                       debug "problem: {mtype} was detected cast-dead"
+                       abort
+               end
+
                var types = self.collect_types(value)
 
                var res = self.new_var(bool_type)
 
                self.add("/* isa {mtype} on {value.inspect} */")
-               if value.mcasttype isa MNullableType then
+               if value.mtype.ctype != "val*" then
+                       if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
+                               self.add("{res} = 1;")
+                       else
+                               self.add("{res} = 0;")
+                       end
+                       return res
+               end
+               if value.mcasttype isa MNullableType or value.mcasttype isa MNullType then
                        self.add("if ({value} == NULL) \{")
                        if mtype isa MNullableType then
                                self.add("{res} = 1; /* isa {mtype} */")
@@ -1162,6 +1735,42 @@ private class GlobalCompilerVisitor
                return res
        end
 
+       # Generate the code required to dynamically check if 2 objects share the same runtime type
+       fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable
+       do
+               var res = self.new_var(bool_type)
+               if value2.mtype.ctype == "val*" then
+                       if value1.mtype.ctype == "val*" then
+                               self.add "{res} = {value1}->classid == {value2}->classid;"
+                       else
+                               self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;"
+                       end
+               else
+                       if value1.mtype.ctype == "val*" then
+                               self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};"
+                       else if value1.mcasttype == value2.mcasttype then
+                               self.add "{res} = 1;"
+                       else
+                               self.add "{res} = 0;"
+                       end
+               end
+               return res
+       end
+
+       # Return a "const char*" variable associated to the classname of the dynamic type of an object
+       # NOTE: we do not return a RuntimeVariable "NativeString" as the class may not exist in the module/program
+       fun class_name_string(value: RuntimeVariable): String
+       do
+               var res = self.get_name("var_class_name")
+               self.add_decl("const char* {res};")
+               if value.mtype.ctype == "val*" then
+                       self.add "{res} = class_names[{value}->classid];"
+               else
+                       self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];"
+               end
+               return res
+       end
+
        # Generate a Nit "is" for two runtime_variables
        fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable
        do
@@ -1172,7 +1781,7 @@ private class GlobalCompilerVisitor
                        value2 = tmp
                end
                if value1.mtype.ctype != "val*" then
-                       if value2.mtype.ctype == value1.mtype.ctype then
+                       if value2.mtype == value1.mtype then
                                self.add("{res} = {value1} == {value2};")
                        else if value2.mtype.ctype != "val*" then
                                self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
@@ -1200,9 +1809,16 @@ private class GlobalCompilerVisitor
        end
 
        # Generate a check-init-instance
-       # TODO: is an empty stub currently
-       fun check_init_instance(recv: RuntimeVariable)
+       fun check_init_instance(recv: RuntimeVariable, mtype: MClassType)
        do
+               if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return
+
+               mtype = self.anchor(mtype).as(MClassType)
+               if not self.compiler.runtime_type_analysis.live_types.has(mtype) then
+                       debug "problem: {mtype} was detected dead"
+               end
+
+               self.add("CHECK_NEW_{mtype.c_name}({recv});")
        end
 
        # Generate an integer value
@@ -1217,18 +1833,19 @@ private class GlobalCompilerVisitor
        fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable
        do
                elttype = self.anchor(elttype)
-               var res = self.init_instance(self.get_class("Array").get_mtype([elttype]))
+               var arraytype = self.get_class("Array").get_mtype([elttype])
+               var res = self.init_instance(arraytype)
                self.add("\{ /* {res} = array_instance Array[{elttype}] */")
                var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
                nat.is_exact = true
-               self.add("{nat} = GC_MALLOC({array.length} * sizeof({elttype.ctype}));")
+               self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});")
                for i in [0..array.length[ do
                        var r = self.autobox(array[i], elttype)
-                       self.add("{nat}[{i}] = {r};")
+                       self.add("((struct {nat.mtype.c_name}*) {nat})->values[{i}] = {r};")
                end
                var length = self.int_instance(array.length)
-               self.send(self.get_property("with_native", res.mtype), [res, nat, length])
-               self.check_init_instance(res)
+               self.send(self.get_property("with_native", arraytype), [res, nat, length])
+               self.check_init_instance(res, arraytype)
                self.add("\}")
                return res
        end
@@ -1248,8 +1865,8 @@ private class GlobalCompilerVisitor
                var res2 = self.init_instance(mtype)
                self.add("{res} = {res2};")
                var length = self.int_instance(string.length)
-               self.send(self.get_property("with_native", res.mtype), [res, nat, length])
-               self.check_init_instance(res)
+               self.send(self.get_property("with_native", mtype), [res, nat, length])
+               self.check_init_instance(res, mtype)
                self.add("{name} = {res};")
                self.add("\}")
                return res
@@ -1260,16 +1877,16 @@ private class GlobalCompilerVisitor
        fun add_abort(message: String)
        do
                if self.current_node != null and self.current_node.location.file != null then
-                       self.add("fprintf(stderr, \"%s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+                       self.add("fprintf(stderr, \"Runtime error: %s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
                else
-                       self.add("fprintf(stderr, \"%s\\n\", \"{message.escape_to_c}\");")
+                       self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");")
                end
                self.add("exit(1);")
        end
 end
 
 # A frame correspond to a visited property in a GlobalCompilerVisitor
-private class Frame
+class Frame
        # The associated visitor
 
        var visitor: GlobalCompilerVisitor
@@ -1285,10 +1902,10 @@ private class Frame
        var arguments: Array[RuntimeVariable]
 
        # The runtime_variable associated to the return (in a function)
-       var returnvar: nullable RuntimeVariable = null
+       var returnvar: nullable RuntimeVariable writable = null
 
        # The label at the end of the property
-       var returnlabel: nullable String = null
+       var returnlabel: nullable String writable = null
 end
 
 redef class MPropDef
@@ -1307,7 +1924,7 @@ end
 
 redef class MMethodDef
        # Can the body be inlined?
-       private fun can_inline(v: GlobalCompilerVisitor): Bool
+       fun can_inline(v: GlobalCompilerVisitor): Bool
        do
                var modelbuilder = v.compiler.modelbuilder
                if modelbuilder.mpropdef2npropdef.has_key(self) then
@@ -1322,109 +1939,67 @@ redef class MMethodDef
        end
 
        # Inline the body in another visitor
-       private fun compile_inside_to_c(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
+       fun compile_inside_to_c(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var modelbuilder = v.compiler.modelbuilder
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
+                       var oldnode = v.current_node
+                       v.current_node = npropdef
+                       self.compile_parameter_check(v, arguments)
                        npropdef.compile_to_c(v, self, arguments)
+                       v.current_node = oldnode
                else if self.mproperty.name == "init" then
                        var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
+                       var oldnode = v.current_node
+                       v.current_node = nclassdef
+                       self.compile_parameter_check(v, arguments)
                        nclassdef.compile_to_c(v, self, arguments)
+                       v.current_node = oldnode
                else
                        abort
                end
                return null
        end
 
-       # Compile the body in a new visitor
-       private fun compile_to_c(compiler: GlobalCompiler, modelbuilder: ModelBuilder, recv: MClassType)
+       # Generate type checks in the C code to check covariant parameters
+       fun compile_parameter_check(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable])
        do
-               if not recv.is_subtype(compiler.mainmodule, null, self.mclassdef.bound_mtype) then
-                       print("problem: why do we compile {self} for {recv}?")
-                       abort
-               end
-
-               var v = new GlobalCompilerVisitor(compiler)
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               if compiler.runtime_type_analysis.live_types.has(recv) then
-                       selfvar.is_exact = true
-               end
-               var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, self, recv, arguments)
-               v.frame = frame
+               if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
 
-               var sig = new Buffer
-               var comment = new Buffer
-               var ret = self.msignature.return_mtype
-               if ret != null then
-                       ret = v.resolve_for(ret, selfvar)
-                       sig.append("{ret.ctype} ")
-               else if self.mproperty.is_new then
-                       ret = recv
-                       sig.append("{ret.ctype} ")
-               else
-                       sig.append("void ")
-               end
-               sig.append(self.c_name)
-               if recv != self.mclassdef.bound_mtype then
-                       sig.append("__{recv.c_name}")
-               end
-               sig.append("({recv.ctype} self")
-               comment.append("(self: {recv}")
-               arguments.add(selfvar)
-               for i in [0..self.msignature.arity[ do
-                       var mtype = self.msignature.parameter_mtypes[i]
-                       if i == self.msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
-                       end
-                       mtype = v.resolve_for(mtype, selfvar)
-                       comment.append(", {mtype}")
-                       sig.append(", {mtype.ctype} p{i}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
-               end
-               sig.append(")")
-               comment.append(")")
-               if ret != null then
-                       comment.append(": {ret}")
-               end
-               compiler.header.add_decl("{sig};")
+               for i in [0..msignature.arity[ do
+                       # skip test for vararg since the array is instantiated with the correct polymorphic type
+                       if msignature.vararg_rank == i then continue
 
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               #v.add("printf(\"method {self} for {comment}\\n\");")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
+                       # skip if the cast is not required
+                       var origmtype =  self.mproperty.intro.msignature.mparameters[i].mtype
+                       if not origmtype.need_anchor then continue
 
-               if modelbuilder.mpropdef2npropdef.has_key(self) then
-                       var npropdef = modelbuilder.mpropdef2npropdef[self]
-                       npropdef.compile_to_c(v, self, arguments)
-               else if self.mproperty.name == "init" then
-                       var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
-                       nclassdef.compile_to_c(v, self, arguments)
-               else
-                       abort
-               end
+                       # get the parameter type
+                       var mtype = self.msignature.mparameters[i].mtype
 
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
+                       # generate the cast
+                       # note that v decides if and how to implements the cast
+                       v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
+                       var cond = v.type_test(arguments[i+1], mtype, "covariance")
+                       v.add("if (!{cond}) \{")
+                       #var x = v.class_name_string(arguments[i+1])
+                       #var y = v.class_name_string(arguments.first)
+                       #v.add("fprintf(stderr, \"expected {mtype} (self is %s), got %s for {arguments[i+1].inspect}\\n\", {y}, {x});")
+                       v.add_abort("Cast failed")
+                       v.add("\}")
                end
-               v.add("\}")
        end
 end
 
 redef class APropdef
-       private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+       fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
-               v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");")
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
                debug("Not yet implemented")
        end
 
-       private fun can_inline: Bool do return true
+       fun can_inline: Bool do return true
 end
 
 redef class AConcreteMethPropdef
@@ -1446,7 +2021,6 @@ redef class AConcreteMethPropdef
                                end
                        end
                end
-
                v.stmt(self.n_block)
        end
 
@@ -1543,6 +2117,12 @@ redef class AInternMethPropdef
                        else if pname == "object_id" then
                                v.ret(arguments.first)
                                return
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
                                return
@@ -1674,17 +2254,8 @@ redef class AInternMethPropdef
                                return
                        end
                else if cname == "NativeArray" then
-                       var elttype = arguments.first.mtype
-                       if pname == "[]" then
-                               v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
-                               return
-                       else if pname == "[]=" then
-                               v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
-                               return
-                       else if pname == "copy_to" then
-                               v.add("memcpy({arguments[1]},{arguments[0]},{arguments[2]}*sizeof({elttype.ctype}));")
-                               return
-                       end
+                       v.native_array_def(pname, ret, arguments)
+                       return
                end
                if pname == "exit" then
                        v.add("exit({arguments[1]});")
@@ -1693,38 +2264,30 @@ redef class AInternMethPropdef
                        v.ret(v.new_expr("glob_sys", ret.as(not null)))
                        return
                else if pname == "calloc_string" then
-                       v.ret(v.new_expr("(char*)GC_MALLOC({arguments[1]})", ret.as(not null)))
+                       v.ret(v.new_expr("(char*)GC_MALLOC_ATOMIC({arguments[1]})", ret.as(not null)))
                        return
                else if pname == "calloc_array" then
-                       var elttype = arguments.first.mtype.supertype_to(v.compiler.mainmodule,arguments.first.mtype.as(MClassType),v.get_class("ArrayCapable")).as(MGenericType).arguments.first
-                       v.ret(v.new_expr("({elttype.ctype}*)GC_MALLOC({arguments[1]} * sizeof({elttype.ctype}))", ret.as(not null)))
+                       v.calloc_array(ret.as(not null), arguments)
                        return
                else if pname == "object_id" then
                        v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                        return
                else if pname == "is_same_type" then
-                       if arguments[0].mtype.ctype == "val*" then
-                               v.ret(v.new_expr("{arguments[0]}->classid == {arguments[1]}->classid", ret.as(not null)))
-                       else
-                               v.ret(v.new_expr("{v.compiler.classid(arguments[0].mtype.as(MClassType))} == {arguments[1]}->classid", ret.as(not null)))
-                       end
+                       v.ret(v.is_same_type_test(arguments[0], arguments[1]))
                        return
                else if pname == "output_class_name" then
-                       if arguments[0].mtype.ctype == "val*" then
-                               v.add("printf(\"%s\\n\", class_names[{arguments.first}->classid]);")
-                       else
-                               v.add("printf(\"%s\\n\", class_names[{v.compiler.classid(arguments.first.mtype.as(MClassType))}]);")
-                       end
+                       var nat = v.class_name_string(arguments.first)
+                       v.add("printf(\"%s\\n\", {nat});")
                        return
                else if pname == "native_class_name" then
-                       if arguments[0].mtype.ctype == "val*" then
-                               v.ret(v.new_expr("(char*)(void*)class_names[{arguments.first}->classid]", ret.as(not null)))
-                       else
-                               v.ret(v.new_expr("(char*)(void*)class_names[{v.compiler.classid(arguments.first.mtype.as(MClassType))}]", ret.as(not null)))
-                       end
+                       var nat = v.class_name_string(arguments.first)
+                       v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
+                       return
+               else if pname == "force_garbage_collection" then
+                       v.add("GC_gcollect();")
                        return
                end
-               v.add("printf(\"Not implemented {class_name}:{mpropdef} at {location.to_s}\\n\");")
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
                debug("Not implemented {mpropdef}")
        end
 end
@@ -1735,7 +2298,8 @@ redef class AExternMethPropdef
                var externname
                var nextern = self.n_extern
                if nextern == null then
-                       debug("{mpropdef} need extern name")
+                       v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
+                       v.add("exit(1);")
                        return
                end
                externname = nextern.text.substring(1, nextern.text.length-2)
@@ -1749,6 +2313,7 @@ redef class AExternMethPropdef
                        ret = v.resolve_for(ret, arguments.first)
                        res = v.new_var(ret)
                end
+               v.adapt_signature(mpropdef, arguments)
 
                if res == null then
                        v.add("{externname}({arguments.join(", ")});")
@@ -1773,6 +2338,7 @@ redef class AExternInitPropdef
                        var file = location.file.filename
                        v.compiler.add_extern(file)
                end
+               v.adapt_signature(mpropdef, arguments)
                var ret = arguments.first.mtype
                var res = v.new_var(ret)
 
@@ -1794,18 +2360,37 @@ redef class AAttrPropdef
                end
        end
 
-       private fun init_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
+       fun init_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
        do
                var nexpr = self.n_expr
                if nexpr != null then
+                       var oldnode = v.current_node
+                       v.current_node = self
                        var old_frame = v.frame
                        var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
                        v.frame = frame
                        var value = v.expr(nexpr, self.mpropdef.static_mtype)
                        v.write_attribute(self.mpropdef.mproperty, recv, value)
                        v.frame = old_frame
+                       v.current_node = oldnode
                end
        end
+
+       fun check_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
+       do
+               var nexpr = self.n_expr
+               if nexpr != null then return
+
+               var oldnode = v.current_node
+               v.current_node = self
+               var old_frame = v.frame
+               var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
+               v.frame = frame
+               # Force read to check the initialization
+               v.read_attribute(self.mpropdef.mproperty, recv)
+               v.frame = old_frame
+               v.current_node = oldnode
+       end
 end
 
 redef class AClassdef
@@ -1838,8 +2423,7 @@ end
 redef class ADeferredMethPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");")
-               v.add("exit(1);")
+               v.add_abort("Deferred method called")
        end
 
        redef fun can_inline do return true
@@ -1850,8 +2434,7 @@ redef class AExpr
        # Do not call this method directly, use `v.expr' instead
        private fun expr(v: GlobalCompilerVisitor): nullable RuntimeVariable
        do
-               debug("Unimplemented expr {class_name}")
-               v.add("printf(\"Not implemented {class_name}:{location.to_s}\\n\");")
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
                var mtype = self.mtype
                if mtype == null then
                        return null
@@ -1917,7 +2500,7 @@ redef class AVarReassignExpr
                var variable = self.variable.as(not null)
                var vari = v.variable(variable)
                var value = v.expr(self.n_value, variable.declared_type)
-               var res = v.send(reassign_property.mproperty, [vari, value])
+               var res = v.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
                assert res != null
                v.assign(v.variable(variable), res)
        end
@@ -1933,14 +2516,14 @@ end
 redef class AContinueExpr
        redef fun stmt(v)
        do
-               v.add("goto CONTINUE_{self.escapemark.object_id};")
+               v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
        end
 end
 
 redef class ABreakExpr
        redef fun stmt(v)
        do
-               v.add("goto BREAK_{self.escapemark.object_id};")
+               v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
        end
 end
 
@@ -1996,7 +2579,7 @@ redef class ADoExpr
                v.stmt(self.n_block)
                var escapemark = self.escapemark
                if escapemark != null then
-                       v.add("BREAK_{escapemark.object_id}: (void)0;")
+                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
                end
        end
 end
@@ -2008,9 +2591,9 @@ redef class AWhileExpr
                var cond = v.expr_bool(self.n_expr)
                v.add("if (!{cond}) break;")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
                v.add("\}")
-               v.add("BREAK_{escapemark.object_id}: (void)0;")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
        end
 end
 
@@ -2019,36 +2602,87 @@ redef class ALoopExpr
        do
                v.add("for(;;) \{")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
                v.add("\}")
-               v.add("BREAK_{escapemark.object_id}: (void)0;")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
        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 AOrangeExpr 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)
+
+                       v.assign(variable, from)
+                       v.add("for(;;) \{ /* shortcut range */")
+
+                       var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
+                       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("succ", variable.mtype), [variable])
+                       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 = v.send(v.get_property("iterator", cl.mtype), [cl])
+               var it_meth = self.method_iterator
+               assert it_meth != null
+               var it = v.send(it_meth, [cl])
                assert it != null
                v.add("for(;;) \{")
-               var ok = v.send(v.get_property("is_ok", it.mtype), [it])
+               var isok_meth = self.method_is_ok
+               assert isok_meth != null
+               var ok = v.send(isok_meth, [it])
                assert ok != null
                v.add("if(!{ok}) break;")
-               var i = v.send(v.get_property("item", it.mtype), [it])
-               assert i != null
-               v.assign(v.variable(variables.first), i)
+               if self.variables.length == 1 then
+                       var item_meth = self.method_item
+                       assert item_meth != null
+                       var i = v.send(item_meth, [it])
+                       assert i != null
+                       v.assign(v.variable(variables.first), i)
+               else if self.variables.length == 2 then
+                       var key_meth = self.method_key
+                       assert key_meth != null
+                       var i = v.send(key_meth, [it])
+                       assert i != null
+                       v.assign(v.variable(variables[0]), i)
+                       var item_meth = self.method_item
+                       assert item_meth != null
+                       i = v.send(item_meth, [it])
+                       assert i != null
+                       v.assign(v.variable(variables[1]), i)
+               else
+                       abort
+               end
                v.stmt(self.n_block)
-               v.add("CONTINUE_{escapemark.object_id}: (void)0;")
-               v.send(v.get_property("next", it.mtype), [it])
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               var next_meth = self.method_next
+               assert next_meth != null
+               v.send(next_meth, [it])
                v.add("\}")
-               v.add("BREAK_{escapemark.object_id}: (void)0;")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
        end
 end
 
 redef class AAssertExpr
        redef fun stmt(v)
        do
+               if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
+
                var cond = v.expr_bool(self.n_expr)
                v.add("if (!{cond}) \{")
                v.stmt(self.n_else)
@@ -2148,7 +2782,7 @@ end
 redef class AArrayExpr
        redef fun expr(v)
        do
-               var mtype = self.mtype.as(MGenericType).arguments.first
+               var mtype = self.mtype.as(MClassType).arguments.first
                var array = new Array[RuntimeVariable]
                for nexpr in self.n_exprs.n_exprs do
                        var i = v.expr(nexpr, mtype)
@@ -2185,9 +2819,10 @@ redef class ACrangeExpr
        do
                var i1 = v.expr(self.n_expr, null)
                var i2 = v.expr(self.n_expr2, null)
-               var res = v.init_instance(self.mtype.as(MClassType))
+               var mtype = self.mtype.as(MClassType)
+               var res = v.init_instance(mtype)
                var it = v.send(v.get_property("init", res.mtype), [res, i1, i2])
-               v.check_init_instance(res)
+               v.check_init_instance(res, mtype)
                return res
        end
 end
@@ -2197,9 +2832,10 @@ redef class AOrangeExpr
        do
                var i1 = v.expr(self.n_expr, null)
                var i2 = v.expr(self.n_expr2, null)
-               var res = v.init_instance(self.mtype.as(MClassType))
+               var mtype = self.mtype.as(MClassType)
+               var res = v.init_instance(mtype)
                var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2])
-               v.check_init_instance(res)
+               v.check_init_instance(res, mtype)
                return res
        end
 end
@@ -2230,7 +2866,7 @@ redef class AIsaExpr
        redef fun expr(v)
        do
                var i = v.expr(self.n_expr, null)
-               return v.type_test(i, self.cast_type.as(not null))
+               return v.type_test(i, self.cast_type.as(not null), "isa")
        end
 end
 
@@ -2238,7 +2874,9 @@ redef class AAsCastExpr
        redef fun expr(v)
        do
                var i = v.expr(self.n_expr, null)
-               var cond = v.type_test(i, self.mtype.as(not null))
+               if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
+
+               var cond = v.type_test(i, self.mtype.as(not null), "as")
                v.add("if (!{cond}) \{")
                v.add_abort("Cast failed")
                v.add("\}")
@@ -2250,6 +2888,8 @@ redef class AAsNotnullExpr
        redef fun expr(v)
        do
                var i = v.expr(self.n_expr, null)
+               if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
+
                v.add("if ({i} == NULL) \{")
                v.add_abort("Cast failed")
                v.add("\}")
@@ -2290,11 +2930,10 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr, null)
                var args = [recv]
-               for a in compute_raw_arguments do
+               for a in self.raw_arguments.as(not null) do
                        args.add(v.expr(a, null))
                end
-               var mproperty = self.mproperty.as(not null)
-               return v.send(mproperty, args)
+               return v.compile_callsite(self.callsite.as(not null), args)
        end
 end
 
@@ -2303,20 +2942,19 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr, null)
                var args = [recv]
-               for a in compute_raw_arguments do
+               for a in self.raw_arguments.as(not null) do
                        args.add(v.expr(a, null))
                end
                var value = v.expr(self.n_value, null)
 
-               var mproperty = self.mproperty.as(not null)
-               var left = v.send(mproperty, args)
+               var left = v.compile_callsite(self.callsite.as(not null), args)
                assert left != null
 
-               var res = v.send(reassign_property.mproperty, [left, value])
+               var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
                assert res != null
 
                args.add(res)
-               v.send(self.write_mproperty.as(not null), args)
+               v.compile_callsite(self.write_callsite.as(not null), args)
        end
 end
 
@@ -2347,6 +2985,7 @@ redef class ASuperExpr
                # FIXME: we do not want an ugly static call!
                var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype)
                if mpropdefs.length != 1 then
+                       v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
                        debug("MPRODFEFS for super {mpropdef} for {recv}: {mpropdefs.join(", ")}")
                end
                mpropdef = mpropdefs.first
@@ -2359,7 +2998,6 @@ end
 redef class ANewExpr
        redef fun expr(v)
        do
-               var mproperty = self.mproperty.as(not null)
                var mtype = self.mtype.as(MClassType)
                var recv
                var ctype = mtype.ctype
@@ -2375,12 +3013,12 @@ redef class ANewExpr
                for a in self.n_args.n_exprs do
                        args.add(v.expr(a, null))
                end
-               var res2 = v.send(mproperty, args)
+               var res2 = v.compile_callsite(self.callsite.as(not null), args)
                if res2 != null then
                        #self.debug("got {res2} from {mproperty}. drop {recv}")
                        return res2
                end
-               v.check_init_instance(recv)
+               v.check_init_instance(recv, mtype)
                return recv
        end
 end
@@ -2411,13 +3049,19 @@ redef class AAttrReassignExpr
                var value = v.expr(self.n_value, null)
                var mproperty = self.mproperty.as(not null)
                var attr = v.read_attribute(mproperty, recv)
-               var res = v.send(reassign_property.mproperty, [attr, value])
+               var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
                assert res != null
                v.write_attribute(mproperty, recv, res)
        end
 end
 
 redef class AIssetAttrExpr
+       redef fun expr(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var mproperty = self.mproperty.as(not null)
+               return v.isset_attribute(mproperty, recv)
+       end
 end
 
 redef class ADebugTypeExpr