nitg: added class AbstractCompiler to refactor nitg nitg-s nitg-se.
authorAlexandre Terrasa <alexandre@moz-code.org>
Tue, 29 Jan 2013 00:42:25 +0000 (19:42 -0500)
committerAlexandre Terrasa <alexandre@moz-code.org>
Mon, 4 Mar 2013 18:20:00 +0000 (13:20 -0500)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/abstract_compiler.nit [new file with mode: 0644]
src/global_compiler.nit
src/separate_compiler.nit
src/separate_erasure_compiler.nit

diff --git a/src/abstract_compiler.nit b/src/abstract_compiler.nit
new file mode 100644 (file)
index 0000000..3360d80
--- /dev/null
@@ -0,0 +1,2251 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Abstract compiler
+module abstract_compiler
+
+import literal
+import typing
+import auto_super_init
+
+# Add compiling options
+redef class ToolContext
+       # --output
+       var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
+       # --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_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
+       protected fun write_and_make(compiler: AbstractCompiler)
+       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
+                       outname = "{mainmodule.name}.bin"
+               end
+
+               var hfilename = ".nit_compile/{mainmodule.name}.1.h"
+               var h = new OFStream.open(hfilename)
+               for l in compiler.header.decl_lines do
+                       h.write l
+                       h.write "\n"
+               end
+               h.close
+
+               var cfiles = new Array[String]
+
+               var file: nullable OFStream = null
+               var count = 0
+
+               ".nit_compile".mkdir
+               var i = 0
+               for vis in compiler.visitors do
+                       count += vis.lines.length
+                       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"
+                               cfiles.add(cfilename)
+                               file = new OFStream.open(cfilename)
+                               file.write "#include \"{mainmodule.name}.1.h\"\n"
+                               count = vis.lines.length
+                       end
+                       if vis != compiler.header then
+                               for l in vis.decl_lines do
+                                       file.write l
+                                       file.write "\n"
+                               end
+                       end
+                       for l in vis.lines do
+                               file.write l
+                               file.write "\n"
+                       end
+               end
+               if file != null then file.close
+
+               self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
+
+               # Generate the Makefile
+
+               var makename = ".nit_compile/{mainmodule.name}.mk"
+               var makefile = new OFStream.open(makename)
+
+               makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc\n\n")
+               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) -D NONITCNI -c -o {o} {f}\n\n")
+                       ofiles.add(o)
+               end
+               # 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)
+
+               var time1 = get_time
+               self.toolcontext.info("*** END COMPILING TO C: {time1-time0} ***", 2)
+
+               # Execute the Makefile
+
+               if self.toolcontext.opt_no_cc.value then return
+
+               time0 = time1
+               self.toolcontext.info("*** COMPILING C ***", 1)
+               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 -B -f {makename} -j 4 {makeflags} 2>&1")
+               else
+                       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}.")
+               end
+
+               time1 = get_time
+               self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
+       end
+end
+
+# Singleton that store the knowledge about the compilation process
+abstract class AbstractCompiler
+       type VISITOR: AbstractCompilerVisitor
+
+       # The main module of the program
+       var mainmodule: MModule protected writable
+
+       # The modeulbuilder used to know the model and the AST
+       var modelbuilder: ModelBuilder protected writable
+
+       # Is hardening asked? (see --hardening)
+       fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
+
+       init(mainmodule: MModule, modelbuilder: ModelBuilder)
+       do
+               self.mainmodule = mainmodule
+               self.modelbuilder = modelbuilder
+       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
+
+       # The list of all associated visitors
+       # Used to generate .c files
+       # FIXME: should not be vistors but just somewhere to store lines
+       var visitors: List[VISITOR] = new List[VISITOR]
+
+       # Initialize a visitor specific for a compiler engine
+       fun new_visitor: VISITOR is abstract
+
+       # Where global declaration are stored (the main .h)
+       #
+       # FIXME: should not be a visitor but just somewhere to store lines
+       # FIXME: should not have a global .h since it does not help recompilations
+       var header: VISITOR writable
+
+       # Compile C headers
+       # This method call compile_header_strucs method that has to be refined
+       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
+
+       # Declaration of structures the live Nit types
+       protected fun compile_header_structs is abstract
+
+       # 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
+
+       # look for a needed .h and .c file for a given .nit source-file
+       # FIXME: bad API, parameter should be a MModule, not its source-file
+       fun add_extern(file: String)
+       do
+               file = file.strip_extension(".nit")
+               var tryfile = file + ".nit.h"
+               if tryfile.file_exists then
+                       self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
+               end
+               tryfile = file + "_nit.h"
+               if tryfile.file_exists then
+                       self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
+               end
+               tryfile = file + ".nit.c"
+               if tryfile.file_exists then
+                       self.extern_bodies.add(tryfile)
+               end
+               tryfile = file + "_nit.c"
+               if tryfile.file_exists then
+                       self.extern_bodies.add(tryfile)
+               end
+       end
+
+       # List of additional .c files required to compile (native interface)
+       var extern_bodies = new ArraySet[String]
+
+       # Generate code that check if an instance is correctly initialized
+       fun generate_check_init_instance(mtype: MClassType) is abstract
+
+       # Generate code that initialize the attributes on a new instance
+       fun generate_init_attr(v: VISITOR, 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, recv)
+                               end
+                       end
+               end
+       end
+
+       # Generate code that check if an attribute is correctly initialized
+       fun generate_check_attr(v: VISITOR, 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
+
+       # stats
+
+       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
+
+       protected 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
+
+       # Display stats about compilation process
+       # Metrics used:
+       #       * type tests against resolved types (x isa Collection[Animal])
+       #       * type tests against unresolved types (x isa Collection[E])
+       #       * type tests skipped
+       #       * type tests total
+       #       *
+       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
+
+       # Division facility
+       # Avoid division by zero by returning the string "n/a"
+       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
+end
+
+# A visitor on the AST of property definition that generate the C code.
+abstract class AbstractCompilerVisitor
+
+       type COMPILER: AbstractCompiler
+
+       # The associated compiler
+       var compiler: COMPILER
+
+       # The current visited AST node
+       var current_node: nullable ANode writable = null
+
+       # The current Frame
+       var frame: nullable Frame writable
+
+       # Alias for self.compiler.mainmodule.object_type
+       fun object_type: MClassType do return self.compiler.mainmodule.object_type
+
+       # Alias for self.compiler.mainmodule.bool_type
+       fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
+
+       var file_break: Bool = false
+
+       init(compiler: COMPILER)
+       do
+               self.compiler = compiler
+               compiler.visitors.add(self)
+       end
+
+       # Force to get the primitive class named `name' or abort
+       fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
+
+       # 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.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule)
+       end
+
+       fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               return self.send(callsite.mproperty, args)
+       end
+
+       fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
+
+       fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
+
+       # 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 >= msignature.arity + 1 # because of self
+                       var rawargs = args
+                       args = new Array[RuntimeVariable]
+
+                       args.add(rawargs.first) # recv
+
+                       for i in [0..vararg_rank[ do
+                               args.add(rawargs[i+1])
+                       end
+
+                       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
+
+                       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
+                       rawargs.clear
+                       rawargs.add_all(args)
+               end
+       end
+
+       # Type handling
+
+       # Anchor a type to the main module and the current receiver
+       fun anchor(mtype: MType): MType
+       do
+               if not mtype.need_anchor then return mtype
+               return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
+       end
+
+       fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
+       do
+               if not mtype.need_anchor then return mtype
+               return mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true)
+       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)
+               var valmtype = value.mcasttype
+               if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
+                       return value
+               end
+
+               if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
+                       var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
+                       return res
+               else
+                       var res = new RuntimeVariable(value.name, valmtype, mtype)
+                       return res
+               end
+       end
+
+       fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
+
+       # Box or unbox a value to another type iff a C type conversion is needed
+       # ENSURE: result.mtype.ctype == mtype.ctype
+       fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
+
+       #  Generate a polymorphic subtype test
+       fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
+
+       #  Generate the code required to dynamically check if 2 objects share the same runtime type
+       fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
+
+       #  Generate a Nit "is" for two runtime_variables
+       fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable is abstract
+
+       # Sends
+
+       #  Generate a static call on a method definition
+       fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
+
+       #  Generate a polymorphic send for the method `m' and the arguments `args'
+       fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
+
+       # 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 propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
+               if propdefs.length == 0 then
+                       abort
+               end
+               if propdefs.length > 1 then
+                       self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
+               end
+               var propdef = propdefs.first
+               return self.call(propdef, t, args)
+       end
+
+       # Attributes handling
+
+       # Generate a polymorphic attribute is_set test
+       fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
+
+       # Generate a polymorphic attribute read
+       fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable is abstract
+
+       # Generate a polymorphic attribute write
+       fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) is abstract
+
+       # Checks
+
+       # Add a check and an abort for a null reciever if needed
+       fun check_recv_notnull(recv: RuntimeVariable)
+       do
+               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
+
+       # Generate a check-init-instance
+       fun check_init_instance(recv: RuntimeVariable, mtype: MClassType) is abstract
+
+       # Names handling
+
+       private var names: HashSet[String] = new HashSet[String]
+       private var last: Int = 0
+
+       # Return a new name based on `s' and unique in the visitor
+       fun get_name(s: String): String
+       do
+               if not self.names.has(s) then
+                       self.names.add(s)
+                       return s
+               end
+               var i = self.last + 1
+               loop
+                       var s2 = s + i.to_s
+                       if not self.names.has(s2) then
+                               self.last = i
+                               self.names.add(s2)
+                               return s2
+                       end
+                       i = i + 1
+               end
+       end
+
+       # 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 "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 is abstract
+
+       # Variables handling
+
+       protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
+
+       # Return the local runtime_variable associated to a Nit local variable
+       fun variable(variable: Variable): RuntimeVariable
+       do
+               if self.variables.has_key(variable) then
+                       return self.variables[variable]
+               else
+                       var name = self.get_name("var_{variable.name}")
+                       var mtype = variable.declared_type.as(not null)
+                       mtype = self.anchor(mtype)
+                       var res = new RuntimeVariable(name, mtype, mtype)
+                       self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
+                       self.variables[variable] = res
+                       return res
+               end
+       end
+
+       # Return a new uninitialized local runtime_variable
+       fun new_var(mtype: MType): RuntimeVariable
+       do
+               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
+
+       # Correctly assign a left and a right value
+       # Boxing and unboxing is performed if required
+       fun assign(left, right: RuntimeVariable)
+       do
+               right = self.autobox(right, left.mtype)
+               self.add("{left} = {right};")
+       end
+
+       # Generate instances
+
+       # Generate a alloc-instance + init-attributes
+       fun init_instance(mtype: MClassType): RuntimeVariable is abstract
+
+       # Generate an integer value
+       fun int_instance(value: Int): RuntimeVariable
+       do
+               var res = self.new_var(self.get_class("Int").mclass_type)
+               self.add("{res} = {value};")
+               return res
+       end
+
+       # Generate a string value
+       fun string_instance(string: String): RuntimeVariable
+       do
+               var mtype = self.get_class("String").mclass_type
+               var name = self.get_name("varonce")
+               self.add_decl("static {mtype.ctype} {name};")
+               var res = self.new_var(mtype)
+               self.add("if ({name}) \{")
+               self.add("{res} = {name};")
+               self.add("\} else \{")
+               var nat = self.new_var(self.get_class("NativeString").mclass_type)
+               self.add("{nat} = \"{string.escape_to_c}\";")
+               var res2 = self.init_instance(mtype)
+               self.add("{res} = {res2};")
+               var length = self.int_instance(string.length)
+               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
+       end
+
+       # Generate an array value
+       fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
+
+       # Get an instance of a array for a vararg
+       fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
+
+       # Code generation
+
+       private var lines: List[String] = new List[String]
+       private var decl_lines: List[String] = new List[String]
+
+       # Add a line in the main part of the generated C
+       fun add(s: String) do self.lines.add(s)
+
+       # Add a line in the
+       # (used for local or global declaration)
+       fun add_decl(s: String) do self.decl_lines.add(s)
+
+       # Return a new local runtime_variable initialized with the C expression `cexpr'.
+       fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
+       do
+               var res = new_var(mtype)
+               self.add("{res} = {cexpr};")
+               return res
+       end
+
+       # Generate generic abort
+       # used by aborts, asserts, casts, etc.
+       fun add_abort(message: String)
+       do
+               if self.current_node != null and self.current_node.location.file != null then
+                       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, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");")
+               end
+               self.add("exit(1);")
+       end
+
+       # Generate a return with the value `s'
+       fun ret(s: RuntimeVariable)
+       do
+               self.assign(self.frame.returnvar.as(not null), s)
+               self.add("goto {self.frame.returnlabel.as(not null)};")
+       end
+
+       # Compile a statement (if any)
+       fun stmt(nexpr: nullable AExpr)
+       do
+               if nexpr == null then return
+               var old = self.current_node
+               self.current_node = nexpr
+               nexpr.stmt(self)
+               self.current_node = old
+       end
+
+       # 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
+               self.current_node = nexpr
+               var res = nexpr.expr(self).as(not null)
+               if mtype != null then
+                       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
+
+       # Alias for `self.expr(nexpr, self.bool_type)'
+       fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type)
+
+       # 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
+               if node == null then
+                       print "?: {message}"
+               else
+                       node.debug(message)
+               end
+               self.add("/* DEBUG: {message} */")
+       end
+end
+
+# A C function associated to a Nit method
+# Because of customization, a given Nit method can be compiler more that once
+abstract class AbstractRuntimeFunction
+
+       type COMPILER: AbstractCompiler
+       type VISITOR: AbstractCompilerVisitor
+
+       # 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 = 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
+
+       protected var c_name_cache: nullable String writable = null
+
+       # Implements a call of the runtime_function
+       # May inline the body or generate a C function call
+       fun call(v: VISITOR, 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: COMPILER) is abstract
+end
+
+# 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
+
+       # The static type of the variable (as declard in C)
+       var mtype: MType
+
+       # The current casted type of the variable (as known in Nit)
+       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 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
+
+       redef fun inspect
+       do
+               var exact_str
+               if self.is_exact then
+                       exact_str = " exact"
+               else
+                       exact_str = ""
+               end
+               var type_str
+               if self.mtype == self.mcasttype then
+                       type_str = "{mtype}{exact_str}"
+               else
+                       type_str = "{mtype}({mcasttype}{exact_str})"
+               end
+               return "<{name}:{type_str}>"
+       end
+end
+
+# A frame correspond to a visited property in a GlobalCompilerVisitor
+class Frame
+
+       type VISITOR: AbstractCompilerVisitor
+
+       # The associated visitor
+       var visitor: VISITOR
+
+       # The executed property.
+       # A Method in case of a call, an attribute in case of a default initialization.
+       var mpropdef: MPropDef
+
+       # The static type of the receiver
+       var receiver: MClassType
+
+       # Arguments of the method (the first is the receiver)
+       var arguments: Array[RuntimeVariable]
+
+       # The runtime_variable associated to the return (in a function)
+       var returnvar: nullable RuntimeVariable writable = null
+
+       # The label at the end of the property
+       var returnlabel: nullable String writable = null
+end
+
+redef class String
+       # Mangle a string to be a unique valid C identifier
+       fun to_cmangle: String
+       do
+               var res = new Buffer
+               var underscore = false
+               for c in self do
+                       if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
+                               res.add(c)
+                               underscore = false
+                               continue
+                       end
+                       if underscore then
+                               res.append('_'.ascii.to_s)
+                               res.add('d')
+                       end
+                       if c >= '0' and c <= '9' then
+                               res.add(c)
+                               underscore = false
+                       else if c == '_' then
+                               res.add(c)
+                               underscore = true
+                       else
+                               res.add('_')
+                               res.append(c.ascii.to_s)
+                               res.add('d')
+                               underscore = false
+                       end
+               end
+               return res.to_s
+       end
+
+       # Escape " \ ' and non printable characters for literal C strings or characters
+       fun escape_to_c: String
+       do
+               var b = new Buffer
+               for c in self do
+                       if c == '\n' then
+                               b.append("\\n")
+                       else if c == '\0' then
+                               b.append("\\0")
+                       else if c == '"' then
+                               b.append("\\\"")
+                       else if c == '\'' then
+                               b.append("\\\'")
+                       else if c == '\\' then
+                               b.append("\\\\")
+                       else if c.ascii < 32 then
+                               b.append("\\{c.ascii.to_base(8, false)}")
+                       else
+                               b.add(c)
+                       end
+               end
+               return b.to_s
+       end
+end
+
+redef class MType
+       # Return the C type associated to a given Nit static type
+       fun ctype: String do return "val*"
+
+       fun ctypename: String do return "val"
+
+       # Return the name of the C structure associated to a Nit live type
+       fun c_name: String is abstract
+       protected var c_name_cache: nullable String protected writable
+end
+
+redef class MClassType
+       redef fun c_name
+       do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
+               self.c_name_cache = res
+               return res
+       end
+
+       redef fun ctype: String
+       do
+               if mclass.name == "Int" then
+                       return "long"
+               else if mclass.name == "Bool" then
+                       return "short int"
+               else if mclass.name == "Char" then
+                       return "char"
+               else if mclass.name == "Float" then
+                       return "double"
+               else if mclass.name == "NativeString" then
+                       return "char*"
+               else if mclass.name == "NativeArray" then
+                       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
+       redef fun c_name
+       do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = super
+               for t in self.arguments do
+                       res = res + t.c_name
+               end
+               self.c_name_cache = res
+               return res
+       end
+end
+
+redef class MParameterType
+       redef fun c_name
+       do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "{self.mclass.c_name}_FT{self.rank}"
+               self.c_name_cache = res
+               return res
+       end
+end
+
+redef class MVirtualType
+       redef fun c_name
+       do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
+               self.c_name_cache = res
+               return res
+       end
+end
+
+redef class MNullableType
+       redef fun c_name
+       do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "nullable_{self.mtype.c_name}"
+               self.c_name_cache = res
+               return res
+       end
+end
+
+redef class MClass
+       # Return the name of the C structure associated to a Nit class
+       fun c_name: String do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
+               self.c_name_cache = res
+               return res
+       end
+       private var c_name_cache: nullable String
+end
+
+redef class MProperty
+       fun c_name: String do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "{self.intro.c_name}"
+               self.c_name_cache = res
+               return res
+       end
+       private var c_name_cache: nullable String
+end
+
+redef class MPropDef
+       type VISITOR: AbstractCompilerVisitor
+
+       private var c_name_cache: nullable String
+
+       # The mangled name associated to the property
+       fun c_name: String
+       do
+               var res = self.c_name_cache
+               if res != null then return res
+               res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
+               self.c_name_cache = res
+               return res
+       end
+end
+
+redef class MMethodDef
+       # Can the body be inlined?
+       fun can_inline(v: VISITOR): Bool
+       do
+               var modelbuilder = v.compiler.modelbuilder
+               if modelbuilder.mpropdef2npropdef.has_key(self) then
+                       var npropdef = modelbuilder.mpropdef2npropdef[self]
+                       return npropdef.can_inline
+               else if self.mproperty.name == "init" then
+                       # Automatic free init is always inlined since it is empty or contains only attribtes assigments
+                       return true
+               else
+                       abort
+               end
+       end
+
+       # Inline the body in another visitor
+       fun compile_inside_to_c(v: VISITOR, 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
+
+       # Generate type checks in the C code to check covariant parameters
+       fun compile_parameter_check(v: VISITOR, arguments: Array[RuntimeVariable])
+       do
+               if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
+
+               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
+
+                       # skip if the cast is not required
+                       var origmtype =  self.mproperty.intro.msignature.mparameters[i].mtype
+                       if not origmtype.need_anchor then continue
+
+                       # get the parameter type
+                       var mtype = self.msignature.mparameters[i].mtype
+
+                       # 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}) \{")
+                       v.add_abort("Cast failed")
+                       v.add("\}")
+               end
+       end
+end
+
+redef class ANode
+       type VISITOR: AbstractCompilerVisitor
+end
+
+redef class APropdef
+       fun compile_to_c(v: VISITOR, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+       do
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
+               debug("Not yet implemented")
+       end
+
+       fun can_inline: Bool do return true
+end
+
+redef class AConcreteMethPropdef
+       redef fun compile_to_c(v, mpropdef, arguments)
+       do
+               for i in [0..mpropdef.msignature.arity[ do
+                       var variable = self.n_signature.n_params[i].variable.as(not null)
+                       v.assign(v.variable(variable), arguments[i+1])
+               end
+               # Call the implicit super-init
+               var auto_super_inits = self.auto_super_inits
+               if auto_super_inits != null then
+                       var selfarg = [arguments.first]
+                       for auto_super_init in auto_super_inits do
+                               if auto_super_init.intro.msignature.arity == 0 then
+                                       v.send(auto_super_init, selfarg)
+                               else
+                                       v.send(auto_super_init, arguments)
+                               end
+                       end
+               end
+               v.stmt(self.n_block)
+       end
+
+       redef fun can_inline
+       do
+               if self.auto_super_inits != null then return false
+               var nblock = self.n_block
+               if nblock == null then return true
+               if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
+               if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
+               return false
+       end
+end
+
+redef class AInternMethPropdef
+       redef fun compile_to_c(v, mpropdef, arguments)
+       do
+               var pname = mpropdef.mproperty.name
+               var cname = mpropdef.mclassdef.mclass.name
+               var ret = mpropdef.msignature.return_mtype
+               if ret != null then
+                       ret = v.resolve_for(ret, arguments.first)
+               end
+               if pname != "==" and pname != "!=" then
+                       v.adapt_signature(mpropdef, arguments)
+               end
+               if cname == "Int" then
+                       if pname == "output" then
+                               v.add("printf(\"%ld\\n\", {arguments.first});")
+                               return
+                       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 == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return
+                       else if pname == "succ" then
+                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
+                               return
+                       else if pname == "prec" then
+                               v.ret(v.new_expr("{arguments[0]}-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.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 == "lshift" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return
+                       else if pname == "rshift" 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
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", 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.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.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+                               return
+                       end
+               else if cname == "Char" then
+                       if pname == "output" then
+                               v.add("printf(\"%c\", {arguments.first});")
+                               return
+                       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
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return
+                       else if pname == "succ" then
+                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
+                               return
+                       else if pname == "prec" then
+                               v.ret(v.new_expr("{arguments[0]}-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.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.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
+                               return
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return
+                       end
+               else if cname == "Bool" then
+                       if pname == "output" then
+                               v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
+                               return
+                       else if pname == "object_id" then
+                               v.ret(arguments.first)
+                               return
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return
+                       end
+               else if cname == "Float" then
+                       if pname == "output" then
+                               v.add("printf(\"%f\\n\", {arguments.first});")
+                               return
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(double){arguments.first}", 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.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return
+                       else if pname == "succ" then
+                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
+                               return
+                       else if pname == "prec" then
+                               v.ret(v.new_expr("{arguments[0]}-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.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
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", 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.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.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return
+                       end
+               else if cname == "Char" then
+                       if pname == "output" then
+                               v.add("printf(\"%c\", {arguments.first});")
+                               return
+                       else if pname == "object_id" then
+                               v.ret(arguments.first)
+                               return
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+                               return
+                       end
+               else if cname == "NativeString" then
+                       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[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
+                               return
+                       else if pname == "atoi" then
+                               v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
+                               return
+                       end
+               else if cname == "NativeArray" then
+                       v.native_array_def(pname, ret, arguments)
+                       return
+               end
+               if pname == "exit" then
+                       v.add("exit({arguments[1]});")
+                       return
+               else if pname == "sys" then
+                       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_ATOMIC({arguments[1]})", ret.as(not null)))
+                       return
+               else if pname == "calloc_array" then
+                       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
+                       v.ret(v.is_same_type_test(arguments[0], arguments[1]))
+                       return
+               else if pname == "output_class_name" then
+                       var nat = v.class_name_string(arguments.first)
+                       v.add("printf(\"%s\\n\", {nat});")
+                       return
+               else if pname == "native_class_name" then
+                       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 YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
+               debug("Not implemented {mpropdef}")
+       end
+end
+
+redef class AExternMethPropdef
+       redef fun compile_to_c(v, mpropdef, arguments)
+       do
+               var externname
+               var nextern = self.n_extern
+               if nextern == null then
+                       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)
+               if location.file != null then
+                       var file = location.file.filename
+                       v.compiler.add_extern(file)
+               end
+               var res: nullable RuntimeVariable = null
+               var ret = mpropdef.msignature.return_mtype
+               if ret != null then
+                       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(", ")});")
+               else
+                       v.add("{res} = {externname}({arguments.join(", ")});")
+                       v.ret(res)
+               end
+       end
+end
+
+redef class AExternInitPropdef
+       redef fun compile_to_c(v, mpropdef, arguments)
+       do
+               var externname
+               var nextern = self.n_extern
+               if nextern == null then
+                       debug("{mpropdef} need extern name")
+                       return
+               end
+               externname = nextern.text.substring(1, nextern.text.length-2)
+               if location.file != null then
+                       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)
+
+               arguments.shift
+
+               v.add("{res} = {externname}({arguments.join(", ")});")
+               v.ret(res)
+       end
+end
+
+redef class AAttrPropdef
+       redef fun compile_to_c(v, mpropdef, arguments)
+       do
+               if arguments.length == 1 then
+                       var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
+                       v.assign(v.frame.returnvar.as(not null), res)
+               else
+                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+               end
+       end
+
+       fun init_expr(v: VISITOR, 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: VISITOR, 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
+       private fun compile_to_c(v: VISITOR, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+       do
+               if mpropdef == self.mfree_init then
+                       var super_inits = self.super_inits
+                       if super_inits != null then
+                               assert arguments.length == 1
+                               for su in super_inits do
+                                       v.send(su, arguments)
+                               end
+                               return
+                       end
+                       var recv = arguments.first
+                       var i = 1
+                       # Collect undefined attributes
+                       for npropdef in self.n_propdefs do
+                               if npropdef isa AAttrPropdef and npropdef.n_expr == null then
+                                       v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
+                                       i += 1
+                               end
+                       end
+               else
+                       abort
+               end
+       end
+end
+
+redef class ADeferredMethPropdef
+       redef fun compile_to_c(v, mpropdef, arguments) do v.add_abort("Deferred method called")
+       redef fun can_inline do return true
+end
+
+redef class AExpr
+       # Try to compile self as an expression
+       # Do not call this method directly, use `v.expr' instead
+       private fun expr(v: VISITOR): nullable RuntimeVariable
+       do
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
+               var mtype = self.mtype
+               if mtype == null then
+                       return null
+               else
+                       var res = v.new_var(mtype)
+                       v.add("/* {res} = NOT YET {class_name} */")
+                       return res
+               end
+       end
+
+       # Try to compile self as a statement
+       # Do not call this method directly, use `v.stmt' instead
+       private fun stmt(v: VISITOR)
+       do
+               var res = expr(v)
+               if res != null then v.add("{res};")
+       end
+end
+
+redef class ABlockExpr
+       redef fun stmt(v)
+       do
+               for e in self.n_expr do v.stmt(e)
+       end
+end
+
+redef class AVardeclExpr
+       redef fun stmt(v)
+       do
+               var variable = self.variable.as(not null)
+               var ne = self.n_expr
+               if ne != null then
+                       var i = v.expr(ne, variable.declared_type)
+                       v.assign(v.variable(variable), i)
+               end
+       end
+end
+
+redef class AVarExpr
+       redef fun expr(v)
+       do
+               var res = v.variable(self.variable.as(not null))
+               var mtype = self.mtype.as(not null)
+               return v.autoadapt(res, mtype)
+       end
+end
+
+redef class AVarAssignExpr
+       redef fun stmt(v)
+       do
+               var variable = self.variable.as(not null)
+               var i = v.expr(self.n_value, variable.declared_type)
+               v.assign(v.variable(variable), i)
+       end
+end
+
+redef class AVarReassignExpr
+       redef fun stmt(v)
+       do
+               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.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
+               assert res != null
+               v.assign(v.variable(variable), res)
+       end
+end
+
+redef class ASelfExpr
+       redef fun expr(v) do return v.frame.arguments.first
+end
+
+redef class AContinueExpr
+       redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
+end
+
+redef class ABreakExpr
+       redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
+end
+
+redef class AReturnExpr
+       redef fun stmt(v)
+       do
+               var nexpr = self.n_expr
+               if nexpr != null then
+                       var returnvar = v.frame.returnvar.as(not null)
+                       var i = v.expr(nexpr, returnvar.mtype)
+                       v.assign(returnvar, i)
+               end
+               v.add("goto {v.frame.returnlabel.as(not null)};")
+       end
+end
+
+redef class AAbortExpr
+       redef fun stmt(v) do v.add_abort("Aborted")
+end
+
+redef class AIfExpr
+       redef fun stmt(v)
+       do
+               var cond = v.expr_bool(self.n_expr)
+               v.add("if ({cond})\{")
+               v.stmt(self.n_then)
+               v.add("\} else \{")
+               v.stmt(self.n_else)
+               v.add("\}")
+       end
+end
+
+redef class AIfexprExpr
+       redef fun expr(v)
+       do
+               var res = v.new_var(self.mtype.as(not null))
+               var cond = v.expr_bool(self.n_expr)
+               v.add("if ({cond})\{")
+               v.assign(res, v.expr(self.n_then, null))
+               v.add("\} else \{")
+               v.assign(res, v.expr(self.n_else, null))
+               v.add("\}")
+               return res
+       end
+end
+
+redef class ADoExpr
+       redef fun stmt(v)
+       do
+               v.stmt(self.n_block)
+               var escapemark = self.escapemark
+               if escapemark != null then
+                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               end
+       end
+end
+
+redef class AWhileExpr
+       redef fun stmt(v)
+       do
+               v.add("for(;;) \{")
+               var cond = v.expr_bool(self.n_expr)
+               v.add("if (!{cond}) break;")
+               v.stmt(self.n_block)
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add("\}")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+       end
+end
+
+redef class ALoopExpr
+       redef fun stmt(v)
+       do
+               v.add("for(;;) \{")
+               v.stmt(self.n_block)
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add("\}")
+               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_meth = self.method_iterator
+               assert it_meth != null
+               var it = v.send(it_meth, [cl])
+               assert it != null
+               v.add("for(;;) \{")
+               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;")
+               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_{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_{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)
+               var nid = self.n_id
+               if nid != null then
+                       v.add_abort("Assert '{nid.text}' failed")
+               else
+                       v.add_abort("Assert failed")
+               end
+               v.add("\}")
+       end
+end
+
+redef class AOrExpr
+       redef fun expr(v)
+       do
+               var res = v.new_var(self.mtype.as(not null))
+               var i1 = v.expr_bool(self.n_expr)
+               v.add("if ({i1}) \{")
+               v.add("{res} = 1;")
+               v.add("\} else \{")
+               var i2 = v.expr_bool(self.n_expr2)
+               v.add("{res} = {i2};")
+               v.add("\}")
+               return res
+       end
+end
+
+redef class AAndExpr
+       redef fun expr(v)
+       do
+               var res = v.new_var(self.mtype.as(not null))
+               var i1 = v.expr_bool(self.n_expr)
+               v.add("if (!{i1}) \{")
+               v.add("{res} = 0;")
+               v.add("\} else \{")
+               var i2 = v.expr_bool(self.n_expr2)
+               v.add("{res} = {i2};")
+               v.add("\}")
+               return res
+       end
+end
+
+redef class ANotExpr
+       redef fun expr(v)
+       do
+               var cond = v.expr_bool(self.n_expr)
+               return v.new_expr("!{cond}", self.mtype.as(not null))
+       end
+end
+
+redef class AOrElseExpr
+       redef fun expr(v)
+       do
+               var res = v.new_var(self.mtype.as(not null))
+               var i1 = v.expr(self.n_expr, null)
+               v.add("if ({i1}!=NULL) \{")
+               v.assign(res, i1)
+               v.add("\} else \{")
+               var i2 = v.expr(self.n_expr2, null)
+               v.assign(res, i2)
+               v.add("\}")
+               return res
+       end
+end
+
+redef class AEeExpr
+       redef fun expr(v)
+       do
+               var value1 = v.expr(self.n_expr, null)
+               var value2 = v.expr(self.n_expr2, null)
+               return v.equal_test(value1, value2)
+       end
+end
+
+redef class AIntExpr
+       redef fun expr(v) do return v.new_expr("{self.n_number.text}", self.mtype.as(not null))
+end
+
+redef class AFloatExpr
+       redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null))
+end
+
+redef class ACharExpr
+       redef fun expr(v) do return v.new_expr("{self.n_char.text}", self.mtype.as(not null))
+end
+
+redef class AArrayExpr
+       redef fun expr(v)
+       do
+               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)
+                       array.add(i)
+               end
+               return v.array_instance(array, mtype)
+       end
+end
+
+redef class AStringFormExpr
+       redef fun expr(v) do return v.string_instance(self.value.as(not null))
+end
+
+redef class ASuperstringExpr
+       redef fun expr(v)
+       do
+               var array = new Array[RuntimeVariable]
+               for ne in self.n_exprs do
+                       if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
+                       var i = v.expr(ne, null)
+                       array.add(i)
+               end
+               var a = v.array_instance(array, v.object_type)
+               var res = v.send(v.get_property("to_s", a.mtype), [a])
+               return res
+       end
+end
+
+redef class ACrangeExpr
+       redef fun expr(v)
+       do
+               var i1 = v.expr(self.n_expr, null)
+               var i2 = v.expr(self.n_expr2, null)
+               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, mtype)
+               return res
+       end
+end
+
+redef class AOrangeExpr
+       redef fun expr(v)
+       do
+               var i1 = v.expr(self.n_expr, null)
+               var i2 = v.expr(self.n_expr2, null)
+               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, mtype)
+               return res
+       end
+end
+
+redef class ATrueExpr
+       redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
+end
+
+redef class AFalseExpr
+       redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
+end
+
+redef class ANullExpr
+       redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
+end
+
+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), "isa")
+       end
+end
+
+redef class AAsCastExpr
+       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
+
+               var cond = v.type_test(i, self.mtype.as(not null), "as")
+               v.add("if (!{cond}) \{")
+               v.add_abort("Cast failed")
+               v.add("\}")
+               return i
+       end
+end
+
+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("\}")
+               return i
+       end
+end
+
+redef class AParExpr
+       redef fun expr(v) do return v.expr(self.n_expr, null)
+end
+
+redef class AOnceExpr
+       redef fun expr(v)
+       do
+               var mtype = self.mtype.as(not null)
+               var name = v.get_name("varonce")
+               var guard = v.get_name(name + "_guard")
+               v.add_decl("static {mtype.ctype} {name};")
+               v.add_decl("static int {guard};")
+               var res = v.new_var(mtype)
+               v.add("if ({guard}) \{")
+               v.add("{res} = {name};")
+               v.add("\} else \{")
+               var i = v.expr(self.n_expr, mtype)
+               v.add("{res} = {i};")
+               v.add("{name} = {res};")
+               v.add("{guard} = 1;")
+               v.add("\}")
+               return res
+       end
+end
+
+redef class ASendExpr
+       redef fun expr(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var args = [recv]
+               for a in self.raw_arguments.as(not null) do
+                       args.add(v.expr(a, null))
+               end
+               return v.compile_callsite(self.callsite.as(not null), args)
+       end
+end
+
+redef class ASendReassignFormExpr
+       redef fun stmt(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var args = [recv]
+               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 left = v.compile_callsite(self.callsite.as(not null), args)
+               assert left != null
+
+               var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
+               assert res != null
+
+               args.add(res)
+               v.compile_callsite(self.write_callsite.as(not null), args)
+       end
+end
+
+redef class ASuperExpr
+       redef fun expr(v)
+       do
+               var recv = v.frame.arguments.first
+               var args = [recv]
+               for a in self.n_args.n_exprs do
+                       args.add(v.expr(a, null))
+               end
+               if args.length == 1 then
+                       args = v.frame.arguments
+               end
+
+               var mproperty = self.mproperty
+               if mproperty != null then
+                       if mproperty.intro.msignature.arity == 0 then
+                               args = [recv]
+                       end
+                       # Super init call
+                       var res = v.send(mproperty, args)
+                       return res
+               end
+
+               # stantard call-next-method
+               var mpropdef = v.frame.mpropdef
+               # 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
+               assert mpropdef isa MMethodDef
+               var res = v.call(mpropdef, recv.mtype.as(MClassType), args)
+               return res
+       end
+end
+
+redef class ANewExpr
+       redef fun expr(v)
+       do
+               var mtype = self.mtype.as(MClassType)
+               var recv
+               var ctype = mtype.ctype
+               if ctype == "val*" then
+                       recv = v.init_instance(mtype)
+               else if ctype == "void*" then
+                       recv = v.new_expr("NULL/*special!*/", mtype)
+               else
+                       debug("cannot new {mtype}")
+                       abort
+               end
+               var args = [recv]
+               for a in self.n_args.n_exprs do
+                       args.add(v.expr(a, null))
+               end
+               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, mtype)
+               return recv
+       end
+end
+
+redef class AAttrExpr
+       redef fun expr(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var mproperty = self.mproperty.as(not null)
+               return v.read_attribute(mproperty, recv)
+       end
+end
+
+redef class AAttrAssignExpr
+       redef fun stmt(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var i = v.expr(self.n_value, null)
+               var mproperty = self.mproperty.as(not null)
+               v.write_attribute(mproperty, recv, i)
+       end
+end
+
+redef class AAttrReassignExpr
+       redef fun stmt(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               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.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
+       redef fun stmt(v)
+       do
+               # do nothing
+       end
+end
\ No newline at end of file
index 468fc04..344dcd0 100644 (file)
 #   * inlining
 module global_compiler
 
-import literal
-import typing
-import auto_super_init
+import abstract_compiler
 import rapid_type_analysis
 
-redef class ToolContext
-       # --output
-       var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
-
-       # --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_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
@@ -80,7 +35,7 @@ redef class ModelBuilder
                var time0 = get_time
                self.toolcontext.info("*** COMPILING TO C ***", 1)
 
-               var compiler = new GlobalCompiler(mainmodule, runtime_type_analysis, self)
+               var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis)
                compiler.compile_header
                var v = compiler.header
 
@@ -117,139 +72,25 @@ redef class ModelBuilder
                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
-                       outname = "{mainmodule.name}.bin"
-               end
-
-               var hfilename = ".nit_compile/{mainmodule.name}.1.h"
-               var h = new OFStream.open(hfilename)
-               for l in compiler.header.decl_lines do
-                       h.write l
-                       h.write "\n"
-               end
-               h.close
-
-               var cfiles = new Array[String]
-
-               var file: nullable OFStream = null
-               var count = 0
-
-               ".nit_compile".mkdir
-               var i = 0
-               for vis in compiler.visitors do
-                       count += vis.lines.length
-                       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"
-                               cfiles.add(cfilename)
-                               file = new OFStream.open(cfilename)
-                               file.write "#include \"{mainmodule.name}.1.h\"\n"
-                               count = vis.lines.length
-                       end
-                       if vis != compiler.header then
-                               for l in vis.decl_lines do
-                                       file.write l
-                                       file.write "\n"
-                               end
-                       end
-                       for l in vis.lines do
-                               file.write l
-                               file.write "\n"
-                       end
-               end
-               if file != null then file.close
-
-               self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
-
-               # Generate the Makefile
-
-               var makename = ".nit_compile/{mainmodule.name}.mk"
-               var makefile = new OFStream.open(makename)
-
-               makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc\n\n")
-               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) -D NONITCNI -c -o {o} {f}\n\n")
-                       ofiles.add(o)
-               end
-               # 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)
-
-               var time1 = get_time
-               self.toolcontext.info("*** END COMPILING TO C: {time1-time0} ***", 2)
-
-               # Execute the Makefile
-
-               if self.toolcontext.opt_no_cc.value then return
-
-               time0 = time1
-               self.toolcontext.info("*** COMPILING C ***", 1)
-               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 -B -f {makename} -j 4 {makeflags} 2>&1")
-               else
-                       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}.")
-               end
-
-               time1 = get_time
-               self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
-       end
 end
 
-# Singleton that store the knowledge about the compilation process
+# Compiler that use global compilation and perform hard optimisations like:
+#   * customization
+#   * switch dispatch
+#   * inlining
 class GlobalCompiler
-       # The main module of the program
-       var mainmodule: MModule writable
+       super AbstractCompiler
+
+       redef type VISITOR: GlobalCompilerVisitor
 
        # The result of the RTA (used to know live types and methods)
        var runtime_type_analysis: RapidTypeAnalysis
 
-       # The modeulbuilder used to know the model and the AST
-       var modelbuilder: ModelBuilder
-
-       # 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)
+       init(mainmodule: MModule, modelbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis)
        do
+               super
                self.header = new_visitor
-               self.mainmodule = mainmodule
                self.runtime_type_analysis = runtime_type_analysis
-               self.modelbuilder = modelbuilder
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
                        if t.ctype != "val*" then
@@ -258,39 +99,7 @@ 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)
+       # Compile 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[] = \{")
@@ -300,11 +109,25 @@ class GlobalCompiler
                self.header.add("\};")
        end
 
+       # Return the C symbol associated to a live type runtime
+       # REQUIRE: self.runtime_type_analysis.live_types.has(mtype)
+       fun classid(mtype: MClassType): String
+       do
+               if self.classids.has_key(mtype) then
+                       return self.classids[mtype]
+               end
+               print "No classid for {mtype}"
+               abort
+       end
+
+       # Cache for classid
+       protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
+
        # 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
+       redef fun compile_header_structs do
                self.header.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */")
        end
 
@@ -312,11 +135,7 @@ class GlobalCompiler
        # Used to implement the equal test
        var live_primitive_types: Array[MClassType]
 
-       # runtime_functions that need to be compiled
-       private var todos: List[AbstractRuntimeFunction] = new List[AbstractRuntimeFunction]
-
-       # runtime_functions already seen (todo or done)
-       private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction]
+       # Add a new todo task
        fun todo(m: AbstractRuntimeFunction)
        do
                if seen.has(m) then return
@@ -324,36 +143,14 @@ class GlobalCompiler
                seen.add(m)
        end
 
-       # 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 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)
-       var extern_bodies = new ArraySet[String]
-
-       # Return the C symbol associated to a live type runtime
-       # REQUIRE: self.runtime_type_analysis.live_types.has(mtype)
-       fun classid(mtype: MClassType): String
-       do
-               if self.classids.has_key(mtype) then
-                       return self.classids[mtype]
-               end
-               print "No classid for {mtype}"
-               abort
-       end
+       # runtime_functions that need to be compiled
+       private var todos: List[AbstractRuntimeFunction] = new List[AbstractRuntimeFunction]
 
-       # Cache for classid (computed by declare_runtimeclass)
-       protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
+       # runtime_functions already seen (todo or done)
+       private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction]
 
        # Declare C structures and identifiers for a runtime class
-       fun declare_runtimeclass(v: GlobalCompilerVisitor, mtype: MClassType)
+       fun declare_runtimeclass(v: VISITOR, mtype: MClassType)
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
                v.add_decl("/* runtime class {mtype} */")
@@ -424,7 +221,7 @@ class GlobalCompiler
                v.add("\}")
        end
 
-       fun generate_check_init_instance(mtype: MClassType)
+       redef fun generate_check_init_instance(mtype)
        do
                if self.modelbuilder.toolcontext.opt_no_check_initialization.value then return
 
@@ -437,34 +234,6 @@ class GlobalCompiler
                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, recv)
-                               end
-                       end
-               end
-       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)
@@ -482,2612 +251,694 @@ class GlobalCompiler
 
        end
 
-       # look for a needed .h and .c file for a given .nit source-file
-       # FIXME: bad API, parameter should be a MModule, not its source-file
-       fun add_extern(file: String)
-       do
-               file = file.strip_extension(".nit")
-               var tryfile = file + ".nit.h"
-               if tryfile.file_exists then
-                       self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
-               end
-               tryfile = file + "_nit.h"
-               if tryfile.file_exists then
-                       self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
-               end
-               tryfile = file + ".nit.c"
-               if tryfile.file_exists then
-                       self.extern_bodies.add(tryfile)
-               end
-               tryfile = file + "_nit.c"
-               if tryfile.file_exists then
-                       self.extern_bodies.add(tryfile)
-               end
-       end
+       redef fun new_visitor do return new GlobalCompilerVisitor(self)
 
-       # Initialize a visitor specific for a compiler engine
-       fun new_visitor: GlobalCompilerVisitor do return new GlobalCompilerVisitor(self)
+       private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
+end
 
-       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
+# A visitor on the AST of property definition that generate the C code.
+# Because of inlining, a visitor can visit more than one property.
+class GlobalCompilerVisitor
+       super AbstractCompilerVisitor
 
-       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
+       redef type COMPILER: GlobalCompiler
 
-       # 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
+       redef fun autobox(value, mtype)
        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);")
+               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)
+               else if mtype.ctype == "val*" then
+                       var valtype = value.mtype.as(MClassType)
+                       var res = self.new_var(mtype)
+                       if not compiler.runtime_type_analysis.live_types.has(valtype) then
+                               self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
+                               self.add("printf(\"Dead code executed!\\n\"); exit(1);")
+                               return res
                        end
+                       self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
+                       return res
+               else
+                       # 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);")
+                       return res
                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
+       # The runtime types that are acceptable for a given receiver.
+       fun collect_types(recv: RuntimeVariable): Array[MClassType]
        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
+               var mtype = recv.mcasttype
+               if recv.is_exact then
+                       assert mtype isa MClassType
+                       assert self.compiler.runtime_type_analysis.live_types.has(mtype)
+                       var types = [mtype]
+                       return types
                end
-       end
-
-       private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
-end
-
-redef class String
-       # Mangle a string to be a unique valid C identifier
-       fun to_cmangle: String
-       do
-               var res = new Buffer
-               var underscore = false
-               for c in self do
-                       if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
-                               res.add(c)
-                               underscore = false
-                               continue
-                       end
-                       if underscore then
-                               res.append('_'.ascii.to_s)
-                               res.add('d')
-                       end
-                       if c >= '0' and c <= '9' then
-                               res.add(c)
-                               underscore = false
-                       else if c == '_' then
-                               res.add(c)
-                               underscore = true
-                       else
-                               res.add('_')
-                               res.append(c.ascii.to_s)
-                               res.add('d')
-                               underscore = false
-                       end
+               var cache = self.compiler.collect_types_cache
+               if cache.has_key(mtype) then
+                       return cache[mtype]
                end
-               return res.to_s
-       end
-
-       # Escape " \ ' and non printable characters for literal C strings or characters
-       fun escape_to_c: String
-       do
-               var b = new Buffer
-               for c in self do
-                       if c == '\n' then
-                               b.append("\\n")
-                       else if c == '\0' then
-                               b.append("\\0")
-                       else if c == '"' then
-                               b.append("\\\"")
-                       else if c == '\'' then
-                               b.append("\\\'")
-                       else if c == '\\' then
-                               b.append("\\\\")
-                       else if c.ascii < 32 then
-                               b.append("\\{c.ascii.to_base(8, false)}")
-                       else
-                               b.add(c)
-                       end
+               var types = new Array[MClassType]
+               var mainmodule = self.compiler.mainmodule
+               for t in self.compiler.runtime_type_analysis.live_types do
+                       if not t.is_subtype(mainmodule, null, mtype) then continue
+                       types.add(t)
                end
-               return b.to_s
-       end
-end
-
-redef class MType
-       # Return the C type associated to a given Nit static type
-       fun ctype: String
-       do
-               return "val*"
+               cache[mtype] = types
+               return types
        end
 
-       fun ctypename: String
+       redef fun native_array_def(pname, ret_type, arguments)
        do
-               return "val"
+               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
 
-       # 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
-       protected var c_name_cache: nullable String protected writable
-end
-
-redef class MClassType
-       redef fun c_name
+       redef fun calloc_array(ret_type, arguments)
        do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
-               self.c_name_cache = res
-               return res
+               self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
        end
 
-       redef fun ctype: String
+       redef fun send(m, args)
        do
-               if mclass.name == "Int" then
-                       return "long"
-               else if mclass.name == "Bool" then
-                       return "short int"
-               else if mclass.name == "Char" then
-                       return "char"
-               else if mclass.name == "Float" then
-                       return "double"
-               else if mclass.name == "NativeString" then
-                       return "char*"
-               else if mclass.name == "NativeArray" then
-                       #return "{self.arguments.first.ctype}*"
-                       return "val*"
-               else if mclass.kind == extern_kind then
-                       return "void*"
-               else
-                       return "val*"
-               end
-       end
+               var types = self.collect_types(args.first)
 
-       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"
+               var res: nullable RuntimeVariable
+               var ret = m.intro.msignature.return_mtype
+               if m.is_new then
+                       ret = args.first.mtype
+                       res = self.new_var(ret)
+               else if ret == null then
+                       res = null
                else
-                       return "val"
+                       ret = self.resolve_for(ret, args.first)
+                       res = self.new_var(ret)
                end
-       end
-end
 
-redef class MGenericType
-       redef fun c_name
-       do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = super
-               for t in self.arguments do
-                       res = res + t.c_name
+               self.add("/* send {m} on {args.first.inspect} */")
+               if args.first.mtype.ctype != "val*" 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
+                       var propdefs = m.lookup_definitions(self.compiler.mainmodule, mclasstype)
+                       if propdefs.length == 0 then
+                               self.add("/* skip, no method {m} */")
+                               return res
+                       end
+                       assert propdefs.length == 1
+                       var propdef = propdefs.first
+                       var res2 = self.call(propdef, mclasstype, args)
+                       if res != null then self.assign(res, res2.as(not null))
+                       return res
                end
-               self.c_name_cache = res
-               return res
-       end
-end
-
-# 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 = 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 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
-
-       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
-               if self.mmethoddef.mclassdef.bound_mtype == self.recv then
-                       res = self.mmethoddef.c_name
-               else
-                       res = "{mmethoddef.c_name}__{recv.c_name}"
-               end
-               self.c_name_cache = res
-               return res
-       end
-
-       redef fun ==(o)
-       # used in the compiler worklist
-       do
-               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
-       end
-
-       redef fun hash
-       # used in the compiler work-list
-       do
-               var res = self.mmethoddef.hash + self.recv.hash
-               return res
-       end
-
-       redef fun to_s
-       do
-               if self.mmethoddef.mclassdef.bound_mtype == self.recv then
-                       return self.mmethoddef.to_s
-               else
-                       return "{self.mmethoddef}@{self.recv}"
-               end
-       end
-
-       # 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 = recv
-               end
-               if ret != null then
-                       ret = v.resolve_for(ret, arguments.first)
-               end
-               if self.mmethoddef.can_inline(v) then
-                       var frame = new Frame(v, self.mmethoddef, self.recv, arguments)
-                       frame.returnlabel = v.get_name("RET_LABEL")
-                       if ret != null then
-                               frame.returnvar = v.new_var(ret)
-                       end
-                       var old_frame = v.frame
-                       v.frame = frame
-                       v.add("\{ /* Inline {self} ({arguments.join(",")}) */")
-                       self.mmethoddef.compile_inside_to_c(v, arguments)
-                       v.add("{frame.returnlabel.as(not null)}:(void)0;")
-                       v.add("\}")
-                       v.frame = old_frame
-                       return frame.returnvar
-               end
-               v.adapt_signature(self.mmethoddef, arguments)
-               v.compiler.todo(self)
-               if ret == null then
-                       v.add("{self.c_name}({arguments.join(",")});")
-                       return null
-               else
-                       var res = v.new_var(ret)
-                       v.add("{res} = {self.c_name}({arguments.join(",")});")
-                       return res
-               end
-       end
-end
-
-# 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
-
-       # The static type of the variable (as declard in C)
-       var mtype: MType
-
-       # The current casted type of the variable (as known in Nit)
-       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 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
-
-       redef fun inspect
-       do
-               var exact_str
-               if self.is_exact then
-                       exact_str = " exact"
-               else
-                       exact_str = ""
-               end
-               var type_str
-               if self.mtype == self.mcasttype then
-                       type_str = "{mtype}{exact_str}"
-               else
-                       type_str = "{mtype}({mcasttype}{exact_str})"
-               end
-               return "<{name}:{type_str}>"
-       end
-end
-
-# A visitor on the AST of property definition that generate the C code.
-# Because of inlining, a visitor can visit more than one property.
-class GlobalCompilerVisitor
-       # The associated compiler
-       var compiler: GlobalCompiler
-
-       init(compiler: GlobalCompiler)
-       do
-               self.compiler = compiler
-               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
-
-       # Alias for self.compiler.mainmodule.bool_type
-       fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
-
-       # Force to get the primitive class named `name' or abort
-       fun get_class(name: String): MClass
-       do
-               return self.compiler.mainmodule.get_primitive_class(name)
-       end
-
-       # 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.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule)
-       end
-
-       # The current Frame
-       var frame: nullable Frame writable
-
-       # Anchor a type to the main module and the current receiver
-       fun anchor(mtype: MType): MType
-       do
-               if not mtype.need_anchor then return mtype
-               #debug("anchor {mtype} to {self.reciever.as(not null)}:{self.reciever.mtype}")
-               return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver)
-       end
-
-       # Add a line in the main part of the generated C
-       fun add(s: String)
-       do
-               self.lines.add(s)
-       end
-
-       # Add a line in the
-       # (used for local or global declaration)
-       fun add_decl(s: String)
-       do
-               self.decl_lines.add(s)
-       end
-
-       private var lines: List[String] = new List[String]
-       private var decl_lines: List[String] = new List[String]
-
-       # The current visited AST node
-       var current_node: nullable ANode = null
-
-       # 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
-               self.current_node = nexpr
-               var res = nexpr.expr(self).as(not null)
-               if mtype != null then
-                       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)
-               var valmtype = value.mcasttype
-               if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
-                       return value
-               end
-
-               if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
-                       var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
-                       return res
-               else
-                       var res = new RuntimeVariable(value.name, valmtype, mtype)
-                       return res
-               end
-       end
-
-       # Box or unbox a value to another type iff a C type conversion is needed
-       # ENSURE: result.mtype.ctype == mtype.ctype
-       fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable
-       do
-               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)
-               else if mtype.ctype == "val*" then
-                       var valtype = value.mtype.as(MClassType)
-                       var res = self.new_var(mtype)
-                       if not compiler.runtime_type_analysis.live_types.has(valtype) then
-                               self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
-                               self.add("printf(\"Dead code executed!\\n\"); exit(1);")
-                               return res
-                       end
-                       self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
-                       return res
-               else
-                       # 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);")
-                       return res
-               end
-       end
-
-       # Correctly assign a left and a right value
-       # Boxing and unboxing is performed if required
-       fun assign(left, right: RuntimeVariable)
-       do
-               right = self.autobox(right, left.mtype)
-               self.add("{left} = {right};")
-       end
-
-
-       # Alias for `self.expr(nexpr, self.bool_type)'
-       fun expr_bool(nexpr: AExpr): RuntimeVariable
-       do
-               return expr(nexpr, bool_type)
-       end
-
-       # Compile a statement (if any)
-       fun stmt(nexpr: nullable AExpr)
-       do
-               if nexpr == null then return
-               var old = self.current_node
-               self.current_node = nexpr
-               nexpr.stmt(self)
-               self.current_node = old
-       end
-
-       # 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
-               if node == null then
-                       print "?: {message}"
-               else
-                       node.debug(message)
-               end
-               self.add("/* DEBUG: {message} */")
-       end
-
-       # Return a new uninitialized local runtime_variable
-       fun new_var(mtype: MType): RuntimeVariable
-       do
-               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
-               var res = new_var(mtype)
-               self.add("{res} = {cexpr};")
-               return res
-       end
-
-       # Return the local runtime_variable associated to a Nit local variable
-       fun variable(variable: Variable): RuntimeVariable
-       do
-               if self.variables.has_key(variable) then
-                       return self.variables[variable]
-               else
-                       var name = self.get_name("var_{variable.name}")
-                       var mtype = variable.declared_type.as(not null)
-                       mtype = self.anchor(mtype)
-                       var res = new RuntimeVariable(name, mtype, mtype)
-                       self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;")
-                       self.variables[variable] = res
-                       return res
-               end
-       end
-
-       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
-               if not self.names.has(s) then
-                       self.names.add(s)
-                       return s
-               end
-               var i = self.last + 1
-               loop
-                       var s2 = s + i.to_s
-                       if not self.names.has(s2) then
-                               self.last = i
-                               self.names.add(s2)
-                               return s2
-                       end
-                       i = i + 1
-               end
-       end
-
-       private var last: Int = 0
-
-       private var names: HashSet[String] = new HashSet[String]
-
-       # Generate a return with the value `s'
-       fun ret(s: RuntimeVariable)
-       do
-               self.assign(self.frame.returnvar.as(not null), s)
-               self.add("goto {self.frame.returnlabel.as(not null)};")
-       end
-
-       # The runtime types that are acceptable for a given receiver.
-       fun collect_types(recv: RuntimeVariable): Array[MClassType]
-       do
-               var mtype = recv.mcasttype
-               if recv.is_exact then
-                       assert mtype isa MClassType
-                       assert self.compiler.runtime_type_analysis.live_types.has(mtype)
-                       var types = [mtype]
-                       return types
-               end
-               var cache = self.compiler.collect_types_cache
-               if cache.has_key(mtype) then
-                       return cache[mtype]
-               end
-               var types = new Array[MClassType]
-               var mainmodule = self.compiler.mainmodule
-               for t in self.compiler.runtime_type_analysis.live_types do
-                       if not t.is_subtype(mainmodule, null, mtype) then continue
-                       types.add(t)
-               end
-               cache[mtype] = types
-               return types
-       end
-
-       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
-
-       # Add a check and an abort for a null reciever is needed
-       fun check_recv_notnull(recv: RuntimeVariable)
-       do
-               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'
-       fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable
-       do
-               var types = self.collect_types(args.first)
-
-               var res: nullable RuntimeVariable
-               var ret = m.intro.msignature.return_mtype
-               if m.is_new then
-                       ret = args.first.mtype
-                       res = self.new_var(ret)
-               else if ret == null then
-                       res = null
-               else
-                       ret = self.resolve_for(ret, args.first)
-                       res = self.new_var(ret)
-               end
-
-               self.add("/* send {m} on {args.first.inspect} */")
-               if args.first.mtype.ctype != "val*" 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
-                       var propdefs = m.lookup_definitions(self.compiler.mainmodule, mclasstype)
-                       if propdefs.length == 0 then
-                               self.add("/* skip, no method {m} */")
-                               return res
-                       end
-                       assert propdefs.length == 1
-                       var propdef = propdefs.first
-                       var res2 = self.call(propdef, mclasstype, args)
-                       if res != null then self.assign(res, res2.as(not null))
-                       return res
-               end
-               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 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 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
-                       else
-                               self.add_abort("Reciever is null")
-                       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
-                       if propdefs.length > 1 then
-                               self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
-                       end
-                       var propdef = propdefs.first
-                       if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
-                               defaultpropdef = propdef
-                               continue
-                       end
-                       if not self.compiler.hardening and t == last and defaultpropdef == null then
-                               self.add("default: /* test {t} */")
-                       else
-                               self.add("case {self.compiler.classid(t)}: /* test {t} */")
-                       end
-                       var res2 = self.call(propdef, t, args)
-                       if res != null then self.assign(res, res2.as(not null))
-                       self.add "break;"
-               end
-               if defaultpropdef != null then
-                       self.add("default: /* default is Object */")
-                       var res2 = self.call(defaultpropdef, defaultpropdef.mclassdef.bound_mtype, args)
-                       if res != null then self.assign(res, res2.as(not null))
-               else if self.compiler.hardening then
-                       self.add("default: /* bug */")
-                       self.bugtype(args.first)
-               end
-               self.add("\}")
-               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 propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
-               if propdefs.length == 0 then
-                       abort
-               end
-               if propdefs.length > 1 then
-                       self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
-               end
-               var propdef = propdefs.first
-               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
-               print "{recvtype} is not a live type"
-               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)
-               #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}")
-               if m.mclassdef.mclass.name == "Object" and recvtype.ctype == "val*" then
-                       recvtype = m.mclassdef.bound_mtype
-               end
-               var recv = self.autobox(args.first, recvtype)
-               recv = self.autoadapt(recv, recvtype)
-
-               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 >= msignature.arity + 1 # because of self
-                       var rawargs = args
-                       args = new Array[RuntimeVariable]
-
-                       args.add(rawargs.first) # recv
-
-                       for i in [0..vararg_rank[ do
-                               args.add(rawargs[i+1])
-                       end
-
-                       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
-
-                       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
-                       rawargs.clear
-                       rawargs.add_all(args)
-               end
-       end
-
-       # Get an instance of a anny for a vararg
-       fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable
-       do
-               # 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)
-               ret = self.resolve_for(ret, recv)
-               var res = self.new_var(ret)
-
-               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} */")
-               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 res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
-                       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}")
-                                       self.add("\}")
-                               else
-                                       self.add("/*NOTYET isset on primitive attributes*/")
-                               end
-                       end
-                       self.assign(res, res2)
-                       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 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} */")
-               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)
-                       self.add("((struct {t.c_name}*){recv})->{a.intro.c_name} = {self.autobox(value, ta)};")
-                       self.add("break;")
-               end
-               if self.compiler.hardening then
-                       self.add("default: /* Bug*/")
-                       self.bugtype(recv)
-               end
-               self.add("\}")
-       end
-
-       # Generate a alloc-instance + init-attributes
-       fun init_instance(mtype: MClassType): RuntimeVariable
-       do
-               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
-               var res = self.new_expr("NEW_{mtype.c_name}()", mtype)
-               res.is_exact = true
-               return res
-       end
-
-       # Generate a polymorphic subtype test
-       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.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} */")
-                       else
-                               self.add("{res} = 0; /* not isa {mtype} */")
-                       end
-                       self.add("\} else ")
-               end
-               self.add("switch({value}->classid) \{")
-               for t in types do
-                       if t.is_subtype(self.compiler.mainmodule, null, mtype) then
-                               self.add("case {self.compiler.classid(t)}: /* {t} */")
-                       end
-               end
-               self.add("{res} = 1;")
-               self.add("break;")
-               self.add("default:")
-               self.add("{res} = 0;")
-               self.add("\}")
-
-               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
-               var res = self.new_var(bool_type)
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
-                       var tmp = value1
-                       value1 = value2
-                       value2 = tmp
-               end
-               if value1.mtype.ctype != "val*" 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}*/")
-                       else
-                               var mtype1 = value1.mtype.as(MClassType)
-                               self.add("{res} = ({value2} != NULL) && ({value2}->classid == {self.compiler.classid(mtype1)});")
-                               self.add("if ({res}) \{")
-                               self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
-                               self.add("\}")
-                       end
-               else
-                       var s = new Array[String]
-                       for t in self.compiler.live_primitive_types do
-                               if not t.is_subtype(self.compiler.mainmodule, null, value1.mcasttype) then continue
-                               if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue
-                               s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)"
-                       end
-                       if s.is_empty then
-                               self.add("{res} = {value1} == {value2};")
-                       else
-                               self.add("{res} = {value1} == {value2} || ({value1} != NULL && {value2} != NULL && {value1}->classid == {value2}->classid && ({s.join(" || ")}));")
-                       end
-               end
-               return res
-       end
-
-       # Generate a check-init-instance
-       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
-       fun int_instance(value: Int): RuntimeVariable
-       do
-               var res = self.new_var(self.get_class("Int").mclass_type)
-               self.add("{res} = {value};")
-               return res
-       end
-
-       # Generate an array value
-       fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable
-       do
-               elttype = self.anchor(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} = NEW_{nat.mtype.c_name}({array.length});")
-               for i in [0..array.length[ do
-                       var r = self.autobox(array[i], elttype)
-                       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", arraytype), [res, nat, length])
-               self.check_init_instance(res, arraytype)
-               self.add("\}")
-               return res
-       end
-
-       # Generate a string value
-       fun string_instance(string: String): RuntimeVariable
-       do
-               var mtype = self.get_class("String").mclass_type
-               var name = self.get_name("varonce")
-               self.add_decl("static {mtype.ctype} {name};")
-               var res = self.new_var(mtype)
-               self.add("if ({name}) \{")
-               self.add("{res} = {name};")
-               self.add("\} else \{")
-               var nat = self.new_var(self.get_class("NativeString").mclass_type)
-               self.add("{nat} = \"{string.escape_to_c}\";")
-               var res2 = self.init_instance(mtype)
-               self.add("{res} = {res2};")
-               var length = self.int_instance(string.length)
-               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
-       end
-
-       # Generate generic abort
-       # used by aborts, asserts, casts, etc.
-       fun add_abort(message: String)
-       do
-               if self.current_node != null and self.current_node.location.file != null then
-                       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, \"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
-class Frame
-       # The associated visitor
-
-       var visitor: GlobalCompilerVisitor
-
-       # The executed property.
-       # A Method in case of a call, an attribute in case of a default initialization.
-       var mpropdef: MPropDef
-
-       # The static type of the receiver
-       var receiver: MClassType
-
-       # Arguments of the method (the first is the receiver)
-       var arguments: Array[RuntimeVariable]
-
-       # The runtime_variable associated to the return (in a function)
-       var returnvar: nullable RuntimeVariable writable = null
-
-       # The label at the end of the property
-       var returnlabel: nullable String writable = null
-end
-
-redef class MPropDef
-       private var c_name_cache: nullable String
-
-       # The mangled name associated to the property
-       fun c_name: String
-       do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
-end
-
-redef class MMethodDef
-       # Can the body be inlined?
-       fun can_inline(v: GlobalCompilerVisitor): Bool
-       do
-               var modelbuilder = v.compiler.modelbuilder
-               if modelbuilder.mpropdef2npropdef.has_key(self) then
-                       var npropdef = modelbuilder.mpropdef2npropdef[self]
-                       return npropdef.can_inline
-               else if self.mproperty.name == "init" then
-                       # Automatic free init is always inlined since it is empty or contains only attribtes assigments
-                       return true
-               else
-                       abort
-               end
-       end
-
-       # Inline the body in another visitor
-       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
-
-       # Generate type checks in the C code to check covariant parameters
-       fun compile_parameter_check(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable])
-       do
-               if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
-
-               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
-
-                       # skip if the cast is not required
-                       var origmtype =  self.mproperty.intro.msignature.mparameters[i].mtype
-                       if not origmtype.need_anchor then continue
-
-                       # get the parameter type
-                       var mtype = self.msignature.mparameters[i].mtype
-
-                       # 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
-       end
-end
-
-redef class APropdef
-       fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
-       do
-               v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
-               debug("Not yet implemented")
-       end
-
-       fun can_inline: Bool do return true
-end
-
-redef class AConcreteMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
-       do
-               for i in [0..mpropdef.msignature.arity[ do
-                       var variable = self.n_signature.n_params[i].variable.as(not null)
-                       v.assign(v.variable(variable), arguments[i+1])
-               end
-               # Call the implicit super-init
-               var auto_super_inits = self.auto_super_inits
-               if auto_super_inits != null then
-                       var selfarg = [arguments.first]
-                       for auto_super_init in auto_super_inits do
-                               if auto_super_init.intro.msignature.arity == 0 then
-                                       v.send(auto_super_init, selfarg)
+               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 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 isa MNullableType then
+                                       self.add("{res} = ({args[1]} != NULL);")
+                               else if args[1].mcasttype isa MNullType then
+                                       self.add("{res} = 0; /* is null */")
                                else
-                                       v.send(auto_super_init, arguments)
+                                       self.add("{res} = 1; /* {args[1].inspect} cannot be null */")
                                end
+                       else
+                               self.add_abort("Reciever is null")
                        end
+                       self.add "\} else"
                end
-               v.stmt(self.n_block)
-       end
-
-       redef fun can_inline
-       do
-               if self.auto_super_inits != null then return false
-               var nblock = self.n_block
-               if nblock == null then return true
-               if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true
-               if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
-               return false
-       end
-end
-
-redef class AInternMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
-       do
-               var pname = mpropdef.mproperty.name
-               var cname = mpropdef.mclassdef.mclass.name
-               var ret = mpropdef.msignature.return_mtype
-               if ret != null then
-                       ret = v.resolve_for(ret, arguments.first)
-               end
-               if pname != "==" and pname != "!=" then
-                       v.adapt_signature(mpropdef, arguments)
+               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
-               if cname == "Int" then
-                       if pname == "output" then
-                               v.add("printf(\"%ld\\n\", {arguments.first});")
-                               return
-                       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 == "unary -" then
-                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
-                               return
-                       else if pname == "succ" then
-                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return
-                       else if pname == "prec" then
-                               v.ret(v.new_expr("{arguments[0]}-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.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 == "lshift" then
-                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
-                               return
-                       else if pname == "rshift" 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
-                       else if pname == "!=" then
-                               var res = v.equal_test(arguments[0], arguments[1])
-                               v.ret(v.new_expr("!{res}", 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.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.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
-                               return
-                       else if pname == "to_f" then
-                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
-                               return
-                       else if pname == "ascii" then
-                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
-                               return
-                       end
-               else if cname == "Char" then
-                       if pname == "output" then
-                               v.add("printf(\"%c\", {arguments.first});")
-                               return
-                       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
-                       else if pname == "!=" then
-                               var res = v.equal_test(arguments[0], arguments[1])
-                               v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
-                       else if pname == "succ" then
-                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return
-                       else if pname == "prec" then
-                               v.ret(v.new_expr("{arguments[0]}-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.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.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
-                               return
-                       else if pname == "to_i" then
-                               v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
-                               return
-                       else if pname == "ascii" then
-                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
-                               return
-                       end
-               else if cname == "Bool" then
-                       if pname == "output" then
-                               v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
-                               return
-                       else if pname == "object_id" then
-                               v.ret(arguments.first)
-                               return
-                       else if pname == "==" then
-                               v.ret(v.equal_test(arguments[0], arguments[1]))
-                               return
-                       else if pname == "!=" then
-                               var res = v.equal_test(arguments[0], arguments[1])
-                               v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
-                       end
-               else if cname == "Float" then
-                       if pname == "output" then
-                               v.add("printf(\"%f\\n\", {arguments.first});")
-                               return
-                       else if pname == "object_id" then
-                               v.ret(v.new_expr("(double){arguments.first}", 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.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
-                               return
-                       else if pname == "unary -" then
-                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
-                               return
-                       else if pname == "succ" then
-                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return
-                       else if pname == "prec" then
-                               v.ret(v.new_expr("{arguments[0]}-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.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
-                       else if pname == "!=" then
-                               var res = v.equal_test(arguments[0], arguments[1])
-                               v.ret(v.new_expr("!{res}", 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.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.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
-                               return
-                       else if pname == "to_i" then
-                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
-                               return
-                       end
-               else if cname == "Char" then
-                       if pname == "output" then
-                               v.add("printf(\"%c\", {arguments.first});")
-                               return
-                       else if pname == "object_id" then
-                               v.ret(arguments.first)
-                               return
-                       else if pname == "==" then
-                               v.ret(v.equal_test(arguments[0], arguments[1]))
-                               return
-                       else if pname == "!=" then
-                               var res = v.equal_test(arguments[0], arguments[1])
-                               v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
-                       else if pname == "ascii" then
-                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
-                               return
+
+               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
-               else if cname == "NativeString" then
-                       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[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
-                               return
-                       else if pname == "atoi" then
-                               v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
-                               return
+                       if propdefs.length > 1 then
+                               self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
                        end
-               else if cname == "NativeArray" then
-                       v.native_array_def(pname, ret, arguments)
-                       return
-               end
-               if pname == "exit" then
-                       v.add("exit({arguments[1]});")
-                       return
-               else if pname == "sys" then
-                       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_ATOMIC({arguments[1]})", ret.as(not null)))
-                       return
-               else if pname == "calloc_array" then
-                       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
-                       v.ret(v.is_same_type_test(arguments[0], arguments[1]))
-                       return
-               else if pname == "output_class_name" then
-                       var nat = v.class_name_string(arguments.first)
-                       v.add("printf(\"%s\\n\", {nat});")
-                       return
-               else if pname == "native_class_name" then
-                       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 YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
-               debug("Not implemented {mpropdef}")
-       end
-end
-
-redef class AExternMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
-       do
-               var externname
-               var nextern = self.n_extern
-               if nextern == null then
-                       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)
-               if location.file != null then
-                       var file = location.file.filename
-                       v.compiler.add_extern(file)
-               end
-               var res: nullable RuntimeVariable = null
-               var ret = mpropdef.msignature.return_mtype
-               if ret != null then
-                       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(", ")});")
-               else
-                       v.add("{res} = {externname}({arguments.join(", ")});")
-                       v.ret(res)
-               end
-       end
-end
-
-redef class AExternInitPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
-       do
-               var externname
-               var nextern = self.n_extern
-               if nextern == null then
-                       debug("{mpropdef} need extern name")
-                       return
-               end
-               externname = nextern.text.substring(1, nextern.text.length-2)
-               if location.file != null then
-                       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)
-
-               arguments.shift
-
-               v.add("{res} = {externname}({arguments.join(", ")});")
-               v.ret(res)
-       end
-end
-
-redef class AAttrPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
-       do
-               if arguments.length == 1 then
-                       var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
-                       v.assign(v.frame.returnvar.as(not null), res)
-               else
-                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
-               end
-       end
-
-       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
-       private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
-       do
-               if mpropdef == self.mfree_init then
-                       var super_inits = self.super_inits
-                       if super_inits != null then
-                               assert arguments.length == 1
-                               for su in super_inits do
-                                       v.send(su, arguments)
-                               end
-                               return
+                       var propdef = propdefs.first
+                       if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
+                               defaultpropdef = propdef
+                               continue
                        end
-                       var recv = arguments.first
-                       var i = 1
-                       # Collect undefined attributes
-                       for npropdef in self.n_propdefs do
-                               if npropdef isa AAttrPropdef and npropdef.n_expr == null then
-                                       v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
-                                       i += 1
-                               end
+                       if not self.compiler.hardening and t == last and defaultpropdef == null then
+                               self.add("default: /* test {t} */")
+                       else
+                               self.add("case {self.compiler.classid(t)}: /* test {t} */")
                        end
-               else
-                       abort
-               end
-       end
-end
-
-redef class ADeferredMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
-       do
-               v.add_abort("Deferred method called")
-       end
-
-       redef fun can_inline do return true
-end
-
-redef class AExpr
-       # Try to compile self as an expression
-       # Do not call this method directly, use `v.expr' instead
-       private fun expr(v: GlobalCompilerVisitor): nullable RuntimeVariable
-       do
-               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
-               var mtype = self.mtype
-               if mtype == null then
-                       return null
-               else
-                       var res = v.new_var(mtype)
-                       v.add("/* {res} = NOT YET {class_name} */")
-                       return res
+                       var res2 = self.call(propdef, t, args)
+                       if res != null then self.assign(res, res2.as(not null))
+                       self.add "break;"
                end
-       end
-
-       # Try to compile self as a statement
-       # Do not call this method directly, use `v.stmt' instead
-       private fun stmt(v: GlobalCompilerVisitor)
-       do
-               var res = expr(v)
-               if res != null then v.add("{res};")
-       end
-
-end
-
-redef class ABlockExpr
-       redef fun stmt(v)
-       do
-               for e in self.n_expr do
-                       v.stmt(e)
+               if defaultpropdef != null then
+                       self.add("default: /* default is Object */")
+                       var res2 = self.call(defaultpropdef, defaultpropdef.mclassdef.bound_mtype, args)
+                       if res != null then self.assign(res, res2.as(not null))
+               else if self.compiler.hardening then
+                       self.add("default: /* bug */")
+                       self.bugtype(args.first)
                end
+               self.add("\}")
+               return res
        end
-end
 
-redef class AVardeclExpr
-       redef fun stmt(v)
+       fun check_valid_reciever(recvtype: MClassType)
        do
-               var variable = self.variable.as(not null)
-               var ne = self.n_expr
-               if ne != null then
-                       var i = v.expr(ne, variable.declared_type)
-                       v.assign(v.variable(variable), i)
-               end
+               if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return
+               print "{recvtype} is not a live type"
+               abort
        end
-end
 
-redef class AVarExpr
-       redef fun expr(v)
+       redef fun call(m, recvtype, args)
        do
-               var res = v.variable(self.variable.as(not null))
-               var mtype = self.mtype.as(not null)
-               return v.autoadapt(res, mtype)
-       end
-end
+               check_valid_reciever(recvtype)
+               #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}")
+               if m.mclassdef.mclass.name == "Object" and recvtype.ctype == "val*" then
+                       recvtype = m.mclassdef.bound_mtype
+               end
+               var recv = self.autobox(args.first, recvtype)
+               recv = self.autoadapt(recv, recvtype)
 
-redef class AVarAssignExpr
-       redef fun stmt(v)
-       do
-               var variable = self.variable.as(not null)
-               var i = v.expr(self.n_value, variable.declared_type)
-               v.assign(v.variable(variable), i)
-       end
-end
+               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
 
-redef class AVarReassignExpr
-       redef fun stmt(v)
-       do
-               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.compile_callsite(self.reassign_callsite.as(not null), [vari, value])
-               assert res != null
-               v.assign(v.variable(variable), res)
+               args.first = recv
+               var rm = new CustomizedRuntimeFunction(m, recvtype)
+               return rm.call(self, args)
        end
-end
 
-redef class ASelfExpr
-       redef fun expr(v)
+       redef fun adapt_signature(m, args)
        do
-               return v.frame.arguments.first
+               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
-end
 
-redef class AContinueExpr
-       redef fun stmt(v)
+       # FIXME: this is currently buggy since recv is not exact
+       redef fun vararg_instance(mpropdef, recv, varargs, elttype)
        do
-               v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
+               elttype = self.resolve_for(elttype, recv)
+               return self.array_instance(varargs, elttype)
        end
-end
 
-redef class ABreakExpr
-       redef fun stmt(v)
+       fun bugtype(recv: RuntimeVariable)
        do
-               v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
+               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
-end
 
-redef class AReturnExpr
-       redef fun stmt(v)
+       redef fun isset_attribute(a, recv)
        do
-               var nexpr = self.n_expr
-               if nexpr != null then
-                       var returnvar = v.frame.returnvar.as(not null)
-                       var i = v.expr(nexpr, returnvar.mtype)
-                       v.assign(returnvar, i)
-               end
-               v.add("goto {v.frame.returnlabel.as(not null)};")
-       end
-end
+               check_recv_notnull(recv)
 
-redef class AAbortExpr
-       redef fun stmt(v)
-       do
-               v.add_abort("Aborted")
-       end
-end
+               var types = self.collect_types(recv)
+               var res = self.new_var(bool_type)
 
-redef class AIfExpr
-       redef fun stmt(v)
-       do
-               var cond = v.expr_bool(self.n_expr)
-               v.add("if ({cond})\{")
-               v.stmt(self.n_then)
-               v.add("\} else \{")
-               v.stmt(self.n_else)
-               v.add("\}")
-       end
-end
+               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("\}")
 
-redef class AIfexprExpr
-       redef fun expr(v)
-       do
-               var res = v.new_var(self.mtype.as(not null))
-               var cond = v.expr_bool(self.n_expr)
-               v.add("if ({cond})\{")
-               v.assign(res, v.expr(self.n_then, null))
-               v.add("\} else \{")
-               v.assign(res, v.expr(self.n_else, null))
-               v.add("\}")
                return res
        end
-end
 
-redef class ADoExpr
-       redef fun stmt(v)
+       redef fun read_attribute(a, recv)
        do
-               v.stmt(self.n_block)
-               var escapemark = self.escapemark
-               if escapemark != null then
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-               end
-       end
-end
+               check_recv_notnull(recv)
 
-redef class AWhileExpr
-       redef fun stmt(v)
-       do
-               v.add("for(;;) \{")
-               var cond = v.expr_bool(self.n_expr)
-               v.add("if (!{cond}) break;")
-               v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
-               v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-       end
-end
+               var types = self.collect_types(recv)
 
-redef class ALoopExpr
-       redef fun stmt(v)
-       do
-               v.add("for(;;) \{")
-               v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
-               v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-       end
-end
+               var ret = a.intro.static_mtype.as(not null)
+               ret = self.resolve_for(ret, recv)
+               var res = self.new_var(ret)
 
-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
+               if types.is_empty then
+                       self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+                       self.bugtype(recv)
+                       return res
                end
-
-               var cl = v.expr(self.n_expr, null)
-               var it_meth = self.method_iterator
-               assert it_meth != null
-               var it = v.send(it_meth, [cl])
-               assert it != null
-               v.add("for(;;) \{")
-               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;")
-               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
+               self.add("/* read {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 res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
+                       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}")
+                                       self.add("\}")
+                               else
+                                       self.add("/*NOTYET isset on primitive attributes*/")
+                               end
+                       end
+                       self.assign(res, res2)
+                       self.add("break;")
                end
-               v.stmt(self.n_block)
-               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_{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)
-               var nid = self.n_id
-               if nid != null then
-                       v.add_abort("Assert '{nid.text}' failed")
-               else
-                       v.add_abort("Assert failed")
+               if self.compiler.hardening then
+                       self.add("default: /* Bug */")
+                       self.bugtype(recv)
                end
-               v.add("\}")
-       end
-end
-
-redef class AOrExpr
-       redef fun expr(v)
-       do
-               var res = v.new_var(self.mtype.as(not null))
-               var i1 = v.expr_bool(self.n_expr)
-               v.add("if ({i1}) \{")
-               v.add("{res} = 1;")
-               v.add("\} else \{")
-               var i2 = v.expr_bool(self.n_expr2)
-               v.add("{res} = {i2};")
-               v.add("\}")
-               return res
-       end
-end
+               self.add("\}")
 
-redef class AAndExpr
-       redef fun expr(v)
-       do
-               var res = v.new_var(self.mtype.as(not null))
-               var i1 = v.expr_bool(self.n_expr)
-               v.add("if (!{i1}) \{")
-               v.add("{res} = 0;")
-               v.add("\} else \{")
-               var i2 = v.expr_bool(self.n_expr2)
-               v.add("{res} = {i2};")
-               v.add("\}")
                return res
        end
-end
 
-redef class ANotExpr
-       redef fun expr(v)
+       redef fun write_attribute(a, recv, value)
        do
-               var cond = v.expr_bool(self.n_expr)
-               return v.new_expr("!{cond}", self.mtype.as(not null))
-       end
-end
+               check_recv_notnull(recv)
 
-redef class AOrElseExpr
-       redef fun expr(v)
-       do
-               var res = v.new_var(self.mtype.as(not null))
-               var i1 = v.expr(self.n_expr, null)
-               v.add("if ({i1}!=NULL) \{")
-               v.assign(res, i1)
-               v.add("\} else \{")
-               var i2 = v.expr(self.n_expr2, null)
-               v.assign(res, i2)
-               v.add("\}")
-               return res
-       end
-end
+               var types = self.collect_types(recv)
 
-redef class AEeExpr
-       redef fun expr(v)
-       do
-               var value1 = v.expr(self.n_expr, null)
-               var value2 = v.expr(self.n_expr2, null)
-               return v.equal_test(value1, value2)
+               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} */")
+               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)
+                       self.add("((struct {t.c_name}*){recv})->{a.intro.c_name} = {self.autobox(value, ta)};")
+                       self.add("break;")
+               end
+               if self.compiler.hardening then
+                       self.add("default: /* Bug*/")
+                       self.bugtype(recv)
+               end
+               self.add("\}")
        end
-end
 
-redef class AIntExpr
-       redef fun expr(v)
+       redef fun init_instance(mtype)
        do
-               return v.new_expr("{self.n_number.text}", self.mtype.as(not null))
+               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
+               var res = self.new_expr("NEW_{mtype.c_name}()", mtype)
+               res.is_exact = true
+               return res
        end
-end
 
-redef class AFloatExpr
-       redef fun expr(v)
+       redef fun type_test(value, mtype, tag)
        do
-               return v.new_expr("{self.n_float.text}", self.mtype.as(not null))
-       end
-end
+               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
 
-redef class ACharExpr
-       redef fun expr(v)
-       do
-               return v.new_expr("{self.n_char.text}", self.mtype.as(not null))
-       end
-end
+               var types = self.collect_types(value)
+               var res = self.new_var(bool_type)
 
-redef class AArrayExpr
-       redef fun expr(v)
-       do
-               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)
-                       array.add(i)
+               self.add("/* isa {mtype} on {value.inspect} */")
+               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
-               return v.array_instance(array, mtype)
-       end
-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} */")
+                       else
+                               self.add("{res} = 0; /* not isa {mtype} */")
+                       end
+                       self.add("\} else ")
+               end
+               self.add("switch({value}->classid) \{")
+               for t in types do
+                       if t.is_subtype(self.compiler.mainmodule, null, mtype) then
+                               self.add("case {self.compiler.classid(t)}: /* {t} */")
+                       end
+               end
+               self.add("{res} = 1;")
+               self.add("break;")
+               self.add("default:")
+               self.add("{res} = 0;")
+               self.add("\}")
 
-redef class AStringFormExpr
-       redef fun expr(v)
-       do
-               return v.string_instance(self.value.as(not null))
+               return res
        end
-end
 
-redef class ASuperstringExpr
-       redef fun expr(v)
+       redef fun is_same_type_test(value1, value2)
        do
-               var array = new Array[RuntimeVariable]
-               for ne in self.n_exprs do
-                       if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
-                       var i = v.expr(ne, null)
-                       array.add(i)
+               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
-               var a = v.array_instance(array, v.object_type)
-               var res = v.send(v.get_property("to_s", a.mtype), [a])
                return res
        end
-end
 
-redef class ACrangeExpr
-       redef fun expr(v)
+       redef fun class_name_string(value)
        do
-               var i1 = v.expr(self.n_expr, null)
-               var i2 = v.expr(self.n_expr2, null)
-               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, mtype)
+               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
-end
 
-redef class AOrangeExpr
-       redef fun expr(v)
+       redef fun equal_test(value1, value2)
        do
-               var i1 = v.expr(self.n_expr, null)
-               var i2 = v.expr(self.n_expr2, null)
-               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, mtype)
+               var res = self.new_var(bool_type)
+               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+                       var tmp = value1
+                       value1 = value2
+                       value2 = tmp
+               end
+               if value1.mtype.ctype != "val*" 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}*/")
+                       else
+                               var mtype1 = value1.mtype.as(MClassType)
+                               self.add("{res} = ({value2} != NULL) && ({value2}->classid == {self.compiler.classid(mtype1)});")
+                               self.add("if ({res}) \{")
+                               self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
+                               self.add("\}")
+                       end
+               else
+                       var s = new Array[String]
+                       for t in self.compiler.live_primitive_types do
+                               if not t.is_subtype(self.compiler.mainmodule, null, value1.mcasttype) then continue
+                               if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue
+                               s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)"
+                       end
+                       if s.is_empty then
+                               self.add("{res} = {value1} == {value2};")
+                       else
+                               self.add("{res} = {value1} == {value2} || ({value1} != NULL && {value2} != NULL && {value1}->classid == {value2}->classid && ({s.join(" || ")}));")
+                       end
+               end
                return res
        end
-end
 
-redef class ATrueExpr
-       redef fun expr(v)
+       redef fun check_init_instance(recv, mtype)
        do
-               return v.new_expr("1", self.mtype.as(not null))
-       end
-end
+               if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return
 
-redef class AFalseExpr
-       redef fun expr(v)
-       do
-               return v.new_expr("0", self.mtype.as(not null))
-       end
-end
+               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
 
-redef class ANullExpr
-       redef fun expr(v)
-       do
-               var res = v.new_expr("NULL", self.mtype.as(not null))
-               return res
+               self.add("CHECK_NEW_{mtype.c_name}({recv});")
        end
-end
 
-redef class AIsaExpr
-       redef fun expr(v)
+       redef fun array_instance(array, elttype)
        do
-               var i = v.expr(self.n_expr, null)
-               return v.type_test(i, self.cast_type.as(not null), "isa")
+               elttype = self.anchor(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} = NEW_{nat.mtype.c_name}({array.length});")
+               for i in [0..array.length[ do
+                       var r = self.autobox(array[i], elttype)
+                       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", arraytype), [res, nat, length])
+               self.check_init_instance(res, arraytype)
+               self.add("\}")
+               return res
        end
 end
 
-redef class AAsCastExpr
-       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
-
-               var cond = v.type_test(i, self.mtype.as(not null), "as")
-               v.add("if (!{cond}) \{")
-               v.add_abort("Cast failed")
-               v.add("\}")
-               return i
-       end
-end
+# A runtime function customized on a specific monomrph receiver type
+private class CustomizedRuntimeFunction
+       super AbstractRuntimeFunction
 
-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
+       redef type COMPILER: GlobalCompiler
+       redef type VISITOR: GlobalCompilerVisitor
 
-               v.add("if ({i} == NULL) \{")
-               v.add_abort("Cast failed")
-               v.add("\}")
-               return i
-       end
-end
+       # The considered reciever
+       # (usually is a live type but no strong guarantee)
+       var recv: MClassType
 
-redef class AParExpr
-       redef fun expr(v)
+       init(mmethoddef: MMethodDef, recv: MClassType)
        do
-               return v.expr(self.n_expr, null)
+               super(mmethoddef)
+               self.recv = recv
        end
-end
 
-redef class AOnceExpr
-       redef fun expr(v)
+       redef fun build_c_name
        do
-               var mtype = self.mtype.as(not null)
-               var name = v.get_name("varonce")
-               var guard = v.get_name(name + "_guard")
-               v.add_decl("static {mtype.ctype} {name};")
-               v.add_decl("static int {guard};")
-               var res = v.new_var(mtype)
-               v.add("if ({guard}) \{")
-               v.add("{res} = {name};")
-               v.add("\} else \{")
-               var i = v.expr(self.n_expr, mtype)
-               v.add("{res} = {i};")
-               v.add("{name} = {res};")
-               v.add("{guard} = 1;")
-               v.add("\}")
+               var res = self.c_name_cache
+               if res != null then return res
+               if self.mmethoddef.mclassdef.bound_mtype == self.recv then
+                       res = self.mmethoddef.c_name
+               else
+                       res = "{mmethoddef.c_name}__{recv.c_name}"
+               end
+               self.c_name_cache = res
                return res
        end
-end
 
-redef class ASendExpr
-       redef fun expr(v)
+       # used in the compiler worklist
+       redef fun ==(o)
        do
-               var recv = v.expr(self.n_expr, null)
-               var args = [recv]
-               for a in self.raw_arguments.as(not null) do
-                       args.add(v.expr(a, null))
-               end
-               return v.compile_callsite(self.callsite.as(not null), args)
+               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
        end
-end
 
-redef class ASendReassignFormExpr
-       redef fun stmt(v)
+       # used in the compiler work-list
+       redef fun hash do return self.mmethoddef.hash + self.recv.hash
+
+       redef fun to_s
        do
-               var recv = v.expr(self.n_expr, null)
-               var args = [recv]
-               for a in self.raw_arguments.as(not null) do
-                       args.add(v.expr(a, null))
+               if self.mmethoddef.mclassdef.bound_mtype == self.recv then
+                       return self.mmethoddef.to_s
+               else
+                       return "{self.mmethoddef}@{self.recv}"
                end
-               var value = v.expr(self.n_value, null)
-
-               var left = v.compile_callsite(self.callsite.as(not null), args)
-               assert left != null
-
-               var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
-               assert res != null
-
-               args.add(res)
-               v.compile_callsite(self.write_callsite.as(not null), args)
        end
-end
 
-redef class ASuperExpr
-       redef fun expr(v)
+       # compile the code customized for the reciever
+       redef fun compile_to_c(compiler)
        do
-               var recv = v.frame.arguments.first
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       args.add(v.expr(a, null))
-               end
-               if args.length == 1 then
-                       args = v.frame.arguments
-               end
-
-               var mproperty = self.mproperty
-               if mproperty != null then
-                       if mproperty.intro.msignature.arity == 0 then
-                               args = [recv]
-                       end
-                       # Super init call
-                       var res = v.send(mproperty, args)
-                       return res
+               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
 
-               # stantard call-next-method
-               var mpropdef = v.frame.mpropdef
-               # 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(", ")}")
+               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
-               mpropdef = mpropdefs.first
-               assert mpropdef isa MMethodDef
-               var res = v.call(mpropdef, recv.mtype.as(MClassType), args)
-               return res
-       end
-end
+               var arguments = new Array[RuntimeVariable]
+               var frame = new Frame(v, mmethoddef, recv, arguments)
+               v.frame = frame
 
-redef class ANewExpr
-       redef fun expr(v)
-       do
-               var mtype = self.mtype.as(MClassType)
-               var recv
-               var ctype = mtype.ctype
-               if ctype == "val*" then
-                       recv = v.init_instance(mtype)
-               else if ctype == "void*" then
-                       recv = v.new_expr("NULL/*special!*/", mtype)
+               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
-                       debug("cannot new {mtype}")
-                       abort
+                       sig.append("void ")
                end
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       args.add(v.expr(a, null))
+               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
-               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
+               sig.append(")")
+               comment.append(")")
+               if ret != null then
+                       comment.append(": {ret}")
                end
-               v.check_init_instance(recv, mtype)
-               return recv
-       end
-end
-
-redef class AAttrExpr
-       redef fun expr(v)
-       do
-               var recv = v.expr(self.n_expr, null)
-               var mproperty = self.mproperty.as(not null)
-               return v.read_attribute(mproperty, recv)
-       end
-end
+               compiler.header.add_decl("{sig};")
 
-redef class AAttrAssignExpr
-       redef fun stmt(v)
-       do
-               var recv = v.expr(self.n_expr, null)
-               var i = v.expr(self.n_value, null)
-               var mproperty = self.mproperty.as(not null)
-               v.write_attribute(mproperty, recv, i)
-       end
-end
+               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")
 
-redef class AAttrReassignExpr
-       redef fun stmt(v)
-       do
-               var recv = v.expr(self.n_expr, null)
-               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.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
-               assert res != null
-               v.write_attribute(mproperty, recv, res)
-       end
-end
+               mmethoddef.compile_inside_to_c(v, arguments)
 
-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)
+               v.add("{frame.returnlabel.as(not null)}:;")
+               if ret != null then
+                       v.add("return {frame.returnvar.as(not null)};")
+               end
+               v.add("\}")
        end
-end
 
-redef class ADebugTypeExpr
-       redef fun stmt(v)
+       redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
-               # do nothing
+               var ret = self.mmethoddef.msignature.return_mtype
+               if self.mmethoddef.mproperty.is_new then
+                       ret = recv
+               end
+               if ret != null then
+                       ret = v.resolve_for(ret, arguments.first)
+               end
+               if self.mmethoddef.can_inline(v) then
+                       var frame = new Frame(v, self.mmethoddef, self.recv, arguments)
+                       frame.returnlabel = v.get_name("RET_LABEL")
+                       if ret != null then
+                               frame.returnvar = v.new_var(ret)
+                       end
+                       var old_frame = v.frame
+                       v.frame = frame
+                       v.add("\{ /* Inline {self} ({arguments.join(",")}) */")
+                       self.mmethoddef.compile_inside_to_c(v, arguments)
+                       v.add("{frame.returnlabel.as(not null)}:(void)0;")
+                       v.add("\}")
+                       v.frame = old_frame
+                       return frame.returnvar
+               end
+               v.adapt_signature(self.mmethoddef, arguments)
+               v.compiler.todo(self)
+               if ret == null then
+                       v.add("{self.c_name}({arguments.join(",")});")
+                       return null
+               else
+                       var res = v.new_var(ret)
+                       v.add("{res} = {self.c_name}({arguments.join(",")});")
+                       return res
+               end
        end
-end
+end
\ No newline at end of file
index 4f5e920..da11aaf 100644 (file)
 # Separate compilation of a Nit program
 module separate_compiler
 
-
-import global_compiler # TODO better separation of concerns
+import abstract_compiler
 import coloring
 
+# Add separate compiler specific options
 redef class ToolContext
        # --separate
        var opt_separate: OptionBool = new OptionBool("Use separate compilation", "--separate")
-
        # --no-inline-intern
        var opt_no_inline_intern: OptionBool = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
-
        # --no-union-attribute
        var opt_no_union_attribute: OptionBool = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
-
        # --no-shortcut-equate
        var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
-
        # --inline-coloring-numbers
        var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids", "--inline-coloring-numbers")
-
        # --use-naive-coloring
        var opt_bm_typing: OptionBool = new OptionBool("Colorize items incrementaly, used to simulate binary matrix typing", "--bm-typing")
-
        # --use-mod-perfect-hashing
        var opt_phmod_typing: OptionBool = new OptionBool("Replace coloration by perfect hashing (with mod operator)", "--phmod-typing")
-
        # --use-and-perfect-hashing
        var opt_phand_typing: OptionBool = new OptionBool("Replace coloration by perfect hashing (with and operator)", "--phand-typing")
-
        # --generic-resolution-tree
        var opt_generic_tree: OptionBool = new OptionBool("Use tree representation for live generic types instead of flattened representation", "--generic-resolution-tree")
-
        # --generic-resolution-tree
        var opt_typing_table_metrics: OptionBool = new OptionBool("Enable static size measuring of tables used for typing and resolution", "--typing-table-metrics")
 
@@ -72,7 +63,7 @@ redef class ModelBuilder
                var time0 = get_time
                self.toolcontext.info("*** COMPILING TO C ***", 1)
 
-               var compiler = new SeparateCompiler(mainmodule, runtime_type_analysis, self)
+               var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis)
                compiler.compile_header
 
                # compile class structures
@@ -114,7 +105,13 @@ end
 
 # Singleton that store the knowledge about the separate compilation process
 class SeparateCompiler
-       super GlobalCompiler # TODO better separation of concerns
+       super AbstractCompiler
+
+       # Cache for classid
+       protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
+
+       # The result of the RTA (used to know live types and methods)
+       var runtime_type_analysis: RapidTypeAnalysis
 
        private var undead_types: Set[MType] = new HashSet[MType]
        private var partial_types: Set[MType] = new HashSet[MType]
@@ -148,7 +145,10 @@ class SeparateCompiler
        private var ft_tables: nullable Map[MClass, Array[nullable MParameterType]]
        private var ft_masks: nullable Map[MClass, Int]
 
-       init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, mmbuilder: ModelBuilder) do
+       init(mainmodule: MModule, mmbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis) do
+               super
+               self.header = new_visitor
+               self.runtime_type_analysis = runtime_type_analysis
                self.do_property_coloring
                self.compile_box_kinds
        end
@@ -191,10 +191,6 @@ class SeparateCompiler
                end
        end
 
-       redef fun compile_class_names do
-               abort # There is no class name compilation since the name is stored in the type structure
-       end
-
        fun compile_box_kinds
        do
                # Collect all bas box class
@@ -895,6 +891,8 @@ class SeparateCompiler
 
        redef fun new_visitor do return new SeparateCompilerVisitor(self)
 
+       # Stats
+
        redef fun display_stats
        do
                super
@@ -930,172 +928,13 @@ class SeparateCompiler
        end
 end
 
-# The C function associated to a methoddef separately compiled
-class SeparateRuntimeFunction
-       super AbstractRuntimeFunction
-
-       redef fun build_c_name: String
-       do
-               return "{mmethoddef.c_name}"
-       end
-
-       redef fun to_s do return self.mmethoddef.to_s
-
-       redef fun compile_to_c(compiler)
-       do
-               var mmethoddef = self.mmethoddef
-
-               var recv = self.mmethoddef.mclassdef.bound_mtype
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
-               v.frame = frame
-
-               var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
-
-               var sig = new Buffer
-               var comment = new Buffer
-               var ret = msignature.return_mtype
-               if ret != null then
-                       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("({selfvar.mtype.ctype} {selfvar}")
-               comment.append("(self: {selfvar}")
-               arguments.add(selfvar)
-               for i in [0..msignature.arity[ do
-                       var mtype = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
-                       end
-                       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} \{")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
-
-               if recv != arguments.first.mtype then
-                       #print "{self} {recv} {arguments.first}"
-               end
-               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
-end
-
-# The C function associated to a methoddef on a primitive type, stored into a VFT of a class
-# The first parameter (the reciever) is always typed by val* in order to accept an object value
-class VirtualRuntimeFunction
-       super AbstractRuntimeFunction
-
-       redef fun build_c_name: String
-       do
-               return "VIRTUAL_{mmethoddef.c_name}"
-       end
-
-       redef fun to_s do return self.mmethoddef.to_s
-
-       redef fun compile_to_c(compiler)
-       do
-               var mmethoddef = self.mmethoddef
-
-               var recv = self.mmethoddef.mclassdef.bound_mtype
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", v.object_type, recv)
-               var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
-               v.frame = frame
-
-               var sig = new Buffer
-               var comment = new Buffer
-
-               # Because the function is virtual, the signature must match the one of the original class
-               var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef
-               var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
-               var ret = msignature.return_mtype
-               if ret != null then
-                       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("({selfvar.mtype.ctype} {selfvar}")
-               comment.append("(self: {selfvar}")
-               arguments.add(selfvar)
-               for i in [0..msignature.arity[ do
-                       var mtype = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
-                       end
-                       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} \{")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
-
-               if recv != arguments.first.mtype then
-                       #print "{self} {recv} {arguments.first}"
-               end
-               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, arguments)
-       do
-               abort
-               # TODO ?
-       end
-end
-
 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
 class SeparateCompilerVisitor
-       super GlobalCompilerVisitor # TODO better separation of concerns
+       super AbstractCompilerVisitor
+
+       redef type COMPILER: SeparateCompiler
 
-       redef fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable])
+       redef fun adapt_signature(m, args)
        do
                var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
                var recv = args.first
@@ -1111,9 +950,7 @@ class SeparateCompilerVisitor
                end
        end
 
-       # Box or unbox a value to another type iff a C type conversion is needed
-       # ENSURE: result.mtype.ctype == mtype.ctype
-       redef fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable
+       redef fun autobox(value, mtype)
        do
                if value.mtype == mtype then
                        return value
@@ -1416,11 +1253,11 @@ class SeparateCompilerVisitor
        end
 
        # Build livetype structure retrieving
-       #ENSURE: mtype.need_anchor
+       # ENSURE: mtype.need_anchor
        fun retrieve_anchored_livetype(mtype: MGenericType, buffer: Buffer) do
                assert mtype.need_anchor
 
-               var compiler = self.compiler.as(SeparateCompiler)
+               var compiler = self.compiler
                for ft in mtype.arguments do
 
                        var ntype = ft
@@ -1458,7 +1295,7 @@ class SeparateCompilerVisitor
 
        redef fun init_instance(mtype)
        do
-               var compiler = self.compiler.as(SeparateCompiler)
+               var compiler = self.compiler
                if mtype isa MGenericType and mtype.need_anchor then
                        if compiler.modelbuilder.toolcontext.opt_generic_tree.value then
                                var buff = new Buffer
@@ -1486,11 +1323,10 @@ class SeparateCompilerVisitor
                self.add("CHECK_NEW_{mtype.mclass.c_name}({value});")
        end
 
-
        redef fun type_test(value, mtype, tag)
        do
                self.add("/* {value.inspect} isa {mtype} */")
-               var compiler = self.compiler.as(SeparateCompiler)
+               var compiler = self.compiler
 
                var recv = self.frame.arguments.first
                var recv_type_info = self.type_info(recv)
@@ -1712,7 +1548,7 @@ class SeparateCompilerVisitor
                else if can_be_primitive(value1) and can_be_primitive(value2) then
                        test.add("{value1}->class == {value2}->class")
                        var s = new Array[String]
-                       for t, v in self.compiler.as(SeparateCompiler).box_kinds do
+                       for t, v in self.compiler.box_kinds do
                                s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
                        end
                        test.add("({s.join(" || ")})")
@@ -1762,7 +1598,7 @@ class SeparateCompilerVisitor
        do
                var mtype = self.get_class("NativeArray").get_mtype([elttype])
                assert mtype isa MGenericType
-               var compiler = self.compiler.as(SeparateCompiler)
+               var compiler = self.compiler
                if mtype.need_anchor then
                        if compiler.modelbuilder.toolcontext.opt_generic_tree.value then
                                var buff = new Buffer
@@ -1812,7 +1648,7 @@ class SeparateCompilerVisitor
 
        fun link_unanchored_type(mclassdef: MClassDef, mtype: MType) do
                assert mtype.need_anchor
-               var compiler = self.compiler.as(SeparateCompiler)
+               var compiler = self.compiler
                if not compiler.live_unanchored_types.has_key(self.frame.mpropdef.mclassdef) then
                        compiler.live_unanchored_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
                end
@@ -1820,64 +1656,162 @@ class SeparateCompilerVisitor
        end
 end
 
-redef class MClass
-       # Return the name of the C structure associated to a Nit class
-       fun c_name: String do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
-       private var c_name_cache: nullable String
-end
+# The C function associated to a methoddef separately compiled
+class SeparateRuntimeFunction
+       super AbstractRuntimeFunction
 
-redef class MType
-       fun const_color: String do return "COLOR_{c_name}"
-end
+       redef fun build_c_name: String do return "{mmethoddef.c_name}"
 
-redef class MParameterType
-       redef fun c_name
+       redef fun to_s do return self.mmethoddef.to_s
+
+       redef fun compile_to_c(compiler)
        do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{self.mclass.c_name}_FT{self.rank}"
-               self.c_name_cache = res
-               return res
+               var mmethoddef = self.mmethoddef
+
+               var recv = self.mmethoddef.mclassdef.bound_mtype
+               var v = compiler.new_visitor
+               var selfvar = new RuntimeVariable("self", recv, recv)
+               var arguments = new Array[RuntimeVariable]
+               var frame = new Frame(v, mmethoddef, recv, arguments)
+               v.frame = frame
+
+               var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
+
+               var sig = new Buffer
+               var comment = new Buffer
+               var ret = msignature.return_mtype
+               if ret != null then
+                       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("({selfvar.mtype.ctype} {selfvar}")
+               comment.append("(self: {selfvar}")
+               arguments.add(selfvar)
+               for i in [0..msignature.arity[ do
+                       var mtype = msignature.mparameters[i].mtype
+                       if i == msignature.vararg_rank then
+                               mtype = v.get_class("Array").get_mtype([mtype])
+                       end
+                       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} \{")
+               if ret != null then
+                       frame.returnvar = v.new_var(ret)
+               end
+               frame.returnlabel = v.get_name("RET_LABEL")
+
+               if recv != arguments.first.mtype then
+                       #print "{self} {recv} {arguments.first}"
+               end
+               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
 end
 
-redef class MVirtualType
-       redef fun c_name
+# The C function associated to a methoddef on a primitive type, stored into a VFT of a class
+# The first parameter (the reciever) is always typed by val* in order to accept an object value
+class VirtualRuntimeFunction
+       super AbstractRuntimeFunction
+
+       redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}"
+
+       redef fun to_s do return self.mmethoddef.to_s
+
+       redef fun compile_to_c(compiler)
        do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}"
-               self.c_name_cache = res
-               return res
+               var mmethoddef = self.mmethoddef
+
+               var recv = self.mmethoddef.mclassdef.bound_mtype
+               var v = compiler.new_visitor
+               var selfvar = new RuntimeVariable("self", v.object_type, recv)
+               var arguments = new Array[RuntimeVariable]
+               var frame = new Frame(v, mmethoddef, recv, arguments)
+               v.frame = frame
+
+               var sig = new Buffer
+               var comment = new Buffer
+
+               # Because the function is virtual, the signature must match the one of the original class
+               var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef
+               var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+               var ret = msignature.return_mtype
+               if ret != null then
+                       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("({selfvar.mtype.ctype} {selfvar}")
+               comment.append("(self: {selfvar}")
+               arguments.add(selfvar)
+               for i in [0..msignature.arity[ do
+                       var mtype = msignature.mparameters[i].mtype
+                       if i == msignature.vararg_rank then
+                               mtype = v.get_class("Array").get_mtype([mtype])
+                       end
+                       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} \{")
+               if ret != null then
+                       frame.returnvar = v.new_var(ret)
+               end
+               frame.returnlabel = v.get_name("RET_LABEL")
+
+               if recv != arguments.first.mtype then
+                       #print "{self} {recv} {arguments.first}"
+               end
+               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
+
+       # TODO ?
+       redef fun call(v, arguments) do abort
 end
 
-redef class MNullableType
-       redef fun c_name
-       do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "nullable_{self.mtype.c_name}"
-               self.c_name_cache = res
-               return res
-       end
+redef class MType
+       fun const_color: String do return "COLOR_{c_name}"
 end
 
 redef class MProperty
-       fun c_name: String do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{self.intro.c_name}"
-               self.c_name_cache = res
-               return res
-       end
-       private var c_name_cache: nullable String
-
        fun const_color: String do return "COLOR_{c_name}"
 end
index 9ccc108..2528258 100644 (file)
 # Separate compilation of a Nit program with generic type erasure
 module separate_erasure_compiler
 
-
 import separate_compiler
 
+# Add separate erased compiler specific options
 redef class ToolContext
        # --erasure
        var opt_erasure: OptionBool = new OptionBool("Erase generic types", "--erasure")
-
        # --no-check-erasure-cast
        var opt_no_check_erasure_cast: OptionBool = new OptionBool("Disable implicit casts on unsafe return with erasure-typing policy (dangerous)", "--no-check-erasure-cast")
 
@@ -38,7 +37,7 @@ redef class ModelBuilder
                var time0 = get_time
                self.toolcontext.info("*** COMPILING TO C ***", 1)
 
-               var compiler = new SeparateErasureCompiler(mainmodule, runtime_type_analysis, self)
+               var compiler = new SeparateErasureCompiler(mainmodule, self, runtime_type_analysis)
                compiler.compile_header
 
                # compile class structures
@@ -72,7 +71,9 @@ class SeparateErasureCompiler
        private var class_colors: Map[MClass, Int]
        private var class_tables: Map[MClass, Array[nullable MClass]]
 
-       init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, mmbuilder: ModelBuilder) do
+       init(mainmodule: MModule, mmbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis) do
+               super
+
                var mclasses = new HashSet[MClass]
                mclasses.add_all(mmbuilder.model.mclasses)
 
@@ -317,6 +318,8 @@ class SeparateErasureCompiler
 
        redef fun new_visitor do return new SeparateErasureCompilerVisitor(self)
 
+       # Stats
+
        redef fun display_sizes
        do
                print "# size of tables"
@@ -362,10 +365,7 @@ class SeparateErasureCompilerVisitor
                return res
        end
 
-       redef fun init_instance(mtype)
-       do
-               return self.new_expr("NEW_{mtype.mclass.c_name}()", mtype)
-       end
+       redef fun init_instance(mtype) do return self.new_expr("NEW_{mtype.mclass.c_name}()", mtype)
 
        redef fun type_test(value, mtype, tag)
        do