abstract_compiler: Add import contract
[nit.git] / src / compiler / abstract_compiler.nit
index 00a226c..e10be39 100644 (file)
@@ -23,19 +23,27 @@ import platform
 import c_tools
 private import annotation
 import mixin
+import counter
+import pkgconfig
+private import explain_assert_api
+import contracts
 
 # Add compiling options
 redef class ToolContext
        # --output
-       var opt_output = new OptionString("Output file", "-o", "--output")
+       var opt_output = new OptionString("Filename of the generated executable", "-o", "--output")
        # --dir
        var opt_dir = new OptionString("Output directory", "--dir")
+       # --run
+       var opt_run = new OptionBool("Execute the binary after the compilation", "--run")
        # --no-cc
-       var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
+       var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
        # --no-main
        var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
+       # --shared-lib
+       var opt_shared_lib = new OptionBool("Compile to a native shared library", "--shared-lib")
        # --make-flags
-       var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
+       var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags")
        # --max-c-lines
        var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
        # --group-c-files
@@ -49,7 +57,7 @@ redef class ToolContext
        # --no-check-attr-isset
        var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
        # --no-check-assert
-       var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
+       var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit `assert` and `as` (dangerous)", "--no-check-assert")
        # --no-check-autocast
        var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
        # --no-check-null
@@ -62,44 +70,40 @@ redef class ToolContext
        var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
        # --isset-checks-metrics
        var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
-       # --stacktrace
-       var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
+       # --no-stacktrace
+       var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
        # --no-gcc-directives
-       var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
+       var opt_no_gcc_directive = new OptionArray("Disable advanced gcc directives for optimization", "--no-gcc-directive")
        # --release
        var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
+       # -g
+       var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug")
+       # --trace
+       var opt_trace = new OptionBool("Compile with lttng's instrumentation", "--trace")
 
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
+               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_run, self.opt_no_cc, self.opt_no_main, self.opt_shared_lib, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
-               self.option_context.add_option(self.opt_stacktrace)
+               self.option_context.add_option(self.opt_no_stacktrace)
                self.option_context.add_option(self.opt_no_gcc_directive)
                self.option_context.add_option(self.opt_release)
                self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
+               self.option_context.add_option(self.opt_debug)
+               self.option_context.add_option(self.opt_trace)
 
                opt_no_main.hidden = true
+               opt_shared_lib.hidden = true
        end
 
        redef fun process_options(args)
        do
                super
 
-               var st = opt_stacktrace.value
-               if st == "none" or st == "libunwind" or st == "nitstack" then
-                       # Fine, do nothing
-               else if st == "auto" or st == null then
-                       # Default is nitstack
-                       opt_stacktrace.value = "nitstack"
-               else
-                       print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
-                       exit(1)
-               end
-
                if opt_output.value != null and opt_dir.value != null then
-                       print "Error: cannot use both --dir and --output"
+                       print "Option Error: cannot use both --dir and --output"
                        exit(1)
                end
 
@@ -114,66 +118,87 @@ redef class ToolContext
 end
 
 redef class ModelBuilder
-       # The compilation directory
-       var compile_dir: String
-
        # Simple indirection to `Toolchain::write_and_make`
        protected fun write_and_make(compiler: AbstractCompiler)
        do
-               var platform = compiler.mainmodule.target_platform
-               var toolchain
-               if platform == null then
-                       toolchain = new MakefileToolchain(toolcontext)
-               else
-                       toolchain = platform.toolchain(toolcontext)
-               end
-               compile_dir = toolchain.compile_dir
-               toolchain.write_and_make compiler
+               var platform = compiler.target_platform
+               var toolchain = platform.toolchain(toolcontext, compiler)
+               compiler.toolchain = toolchain
+               toolchain.write_and_make
        end
 end
 
 redef class Platform
-       fun toolchain(toolcontext: ToolContext): Toolchain is abstract
+       # The specific tool-chain associated to the platform
+       fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
+       do
+               return new MakefileToolchain(toolcontext, compiler)
+       end
 end
 
+# Build toolchain for a specific target program, varies per `Platform`
 class Toolchain
+
+       # Toolcontext
        var toolcontext: ToolContext
 
-       fun compile_dir: String
+       # Compiler of the target program
+       var compiler: AbstractCompiler
+
+       # Directory where to generate all files
+       #
+       # The option `--compile_dir` change this directory.
+       fun root_compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
-               if compile_dir == null then compile_dir = ".nit_compile"
+               if compile_dir == null then compile_dir = "nit_compile"
                return compile_dir
        end
 
-       fun write_and_make(compiler: AbstractCompiler) is abstract
+       # Directory where to generate all C files
+       #
+       # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
+       fun compile_dir: String do return root_compile_dir
+
+       # Write all C files and compile them
+       fun write_and_make is abstract
 end
 
+# Default toolchain using a Makefile
 class MakefileToolchain
        super Toolchain
 
-       redef fun write_and_make(compiler)
+       redef fun write_and_make
        do
+               var debug = toolcontext.opt_debug.value
                var compile_dir = compile_dir
 
+               # Remove the compilation directory unless explicitly set
+               var auto_remove = toolcontext.opt_compile_dir.value == null
+               # If debug flag is set, do not remove sources
+               if debug then auto_remove = false
+
                # 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
                self.toolcontext.info("*** WRITING C ***", 1)
 
+               root_compile_dir.mkdir
                compile_dir.mkdir
 
                var cfiles = new Array[String]
-               write_files(compiler, compile_dir, cfiles)
+               write_files(compile_dir, cfiles)
 
                # Generate the Makefile
 
-               write_makefile(compiler, compile_dir, cfiles)
+               write_makefile(compile_dir, cfiles)
 
                var time1 = get_time
                self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
 
+               toolcontext.check_errors
+
                # Execute the Makefile
 
                if self.toolcontext.opt_no_cc.value then return
@@ -181,18 +206,31 @@ class MakefileToolchain
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
 
-               compile_c_code(compiler, compile_dir)
+               compile_c_code(compile_dir)
+
+               if auto_remove then
+                       sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
+               end
+
+               if toolcontext.opt_run.value then
+                       var mainmodule = compiler.mainmodule
+                       var out = outfile(mainmodule)
+                       var cmd = ["."/out]
+                       cmd.append toolcontext.option_context.rest
+                       toolcontext.exec_and_check(cmd, "--run")
+               end
 
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
 
-       fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write all source files to the `compile_dir`
+       fun write_files(compile_dir: String, cfiles: Array[String])
        do
-               var platform = compiler.mainmodule.target_platform
-               if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings
+               var platform = compiler.target_platform
+               if platform.supports_libunwind then compiler.build_c_to_nit_bindings
                var cc_opt_with_libgc = "-DWITH_LIBGC"
-               if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
+               if not platform.supports_libgc then cc_opt_with_libgc = ""
 
                # Add gc_choser.h to aditionnal bodies
                var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
@@ -202,6 +240,16 @@ class MakefileToolchain
                compiler.files_to_copy.add "{clib}/gc_chooser.c"
                compiler.files_to_copy.add "{clib}/gc_chooser.h"
 
+               # Add lttng traces provider to external bodies
+               if toolcontext.opt_trace.value then
+                       #-I. is there in order to make the TRACEPOINT_INCLUDE directive in clib/traces.h refer to the directory in which gcc was invoked.
+                       var traces = new ExternCFile("traces.c", "-I.")
+                       traces.pkgconfigs.add "lttng-ust"
+                       compiler.extern_bodies.add(traces)
+                       compiler.files_to_copy.add "{clib}/traces.c"
+                       compiler.files_to_copy.add "{clib}/traces.h"
+               end
+
                # FFI
                for m in compiler.mainmodule.in_importation.greaters do
                        compiler.finalize_ffi_for_module(m)
@@ -209,14 +257,14 @@ class MakefileToolchain
 
                # Copy original .[ch] files to compile_dir
                for src in compiler.files_to_copy do
-                       var basename = src.basename("")
+                       var basename = src.basename
                        var dst = "{compile_dir}/{basename}"
                        src.file_copy_to dst
                end
 
                var hfilename = compiler.header.file.name + ".h"
                var hfilepath = "{compile_dir}/{hfilename}"
-               var h = new OFStream.open(hfilepath)
+               var h = new FileWriter.open(hfilepath)
                for l in compiler.header.decl_lines do
                        h.write l
                        h.write "\n"
@@ -231,7 +279,7 @@ class MakefileToolchain
                for f in compiler.files do
                        var i = 0
                        var count = 0
-                       var file: nullable OFStream = null
+                       var file: nullable FileWriter = null
                        for vis in f.writers do
                                if vis == compiler.header then continue
                                var total_lines = vis.lines.length + vis.decl_lines.length
@@ -244,7 +292,7 @@ class MakefileToolchain
                                        var cfilepath = "{compile_dir}/{cfilename}"
                                        self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
                                        cfiles.add(cfilename)
-                                       file = new OFStream.open(cfilepath)
+                                       file = new FileWriter.open(cfilepath)
                                        file.write "#include \"{f.name}.0.h\"\n"
                                        count = total_lines
                                end
@@ -262,8 +310,8 @@ class MakefileToolchain
 
                        var cfilename = "{f.name}.0.h"
                        var cfilepath = "{compile_dir}/{cfilename}"
-                       var hfile: nullable OFStream = null
-                       hfile = new OFStream.open(cfilepath)
+                       var hfile: nullable FileWriter = null
+                       hfile = new FileWriter.open(cfilepath)
                        hfile.write "#include \"{hfilename}\"\n"
                        for key in f.required_declarations do
                                if not compiler.provided_declarations.has_key(key) then
@@ -284,17 +332,14 @@ class MakefileToolchain
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
 
-       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
+       # Get the name of the Makefile to use
+       fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
 
-       fun default_outname(mainmodule: MModule): String
+       # Get the default name of the executable to produce
+       fun default_outname: String
        do
-               # Search a non fictive module
-               var res = mainmodule.name
-               while mainmodule.is_fictive do
-                       mainmodule = mainmodule.in_importation.direct_greaters.first
-                       res = mainmodule.name
-               end
-               return res
+               var mainmodule = compiler.mainmodule.first_real_mmodule
+               return mainmodule.name
        end
 
        # Combine options and platform informations to get the final path of the outfile
@@ -302,16 +347,17 @@ class MakefileToolchain
        do
                var res = self.toolcontext.opt_output.value
                if res != null then return res
-               res = default_outname(mainmodule)
+               res = default_outname
                var dir = self.toolcontext.opt_dir.value
                if dir != null then return dir.join_path(res)
                return res
        end
 
-       fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write the Makefile
+       fun write_makefile(compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
-               var platform = compiler.mainmodule.target_platform
+               var platform = compiler.target_platform
 
                var outname = outfile(mainmodule)
 
@@ -319,41 +365,115 @@ class MakefileToolchain
                var outpath = real_outpath.escape_to_mk
                if outpath != real_outpath then
                        # If the name is crazy and need escaping, we will do an indirection
-                       # 1. generate the binary in the .nit_compile dir under an escaped name
+                       # 1. generate the binary in the nit_compile dir under an escaped name
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
-               var makename = makefile_name(mainmodule)
+               var makename = makefile_name
                var makepath = "{compile_dir}/{makename}"
-               var makefile = new OFStream.open(makepath)
+               var makefile = new FileWriter.open(makepath)
 
                var linker_options = new HashSet[String]
                for m in mainmodule.in_importation.greaters do
                        var libs = m.collect_linker_libs
                        if libs != null then linker_options.add_all(libs)
                end
+               var debug = toolcontext.opt_debug.value
 
-               makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS  ?= -lm {linker_options.join(" ")}\n\n")
+               makefile.write """
+ifeq ($(origin CC), default)
+        CC = ccache cc
+endif
+ifeq ($(origin CXX), default)
+        CXX = ccache c++
+endif
+CFLAGS ?= -g {{{if not debug then "-O2" else ""}}} -Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs
+CINCL =
+LDFLAGS ?=
+LDLIBS  ?= -lm {{{linker_options.join(" ")}}}
+\n"""
+
+               if self.toolcontext.opt_trace.value then makefile.write "LDLIBS += -llttng-ust -ldl\n"
+
+               if toolcontext.opt_shared_lib.value then
+                       makefile.write """
+CFLAGS += -fPIC
+LDFLAGS += -shared -Wl,-soname,{{{outname}}}
+"""
+               end
 
-               var ost = toolcontext.opt_stacktrace.value
-               if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
+               makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
+               if platform.supports_libunwind then
+                       if toolcontext.opt_no_stacktrace.value then
+                               makefile.write "NO_STACKTRACE ?= True"
+                       else
+                               makefile.write "NO_STACKTRACE ?= # Set to `True` to enable"
+                       end
+               end
 
                # Dynamic adaptations
                # While `platform` enable complex toolchains, they are statically applied
                # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
+               makefile.write "\n\n"
 
                # Check and adapt the targeted system
                makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
-               makefile.write("ifeq ($(uname_S),Darwin)\n")
-               # remove -lunwind since it is already included on macosx
-               makefile.write("\tNEED_LIBUNWIND :=\n")
-               makefile.write("endif\n\n")
 
                # Check and adapt for the compiler used
                # clang need an additionnal `-Qunused-arguments`
                makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
 
-               makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
+               if platform.supports_libunwind then
+                       makefile.write """
+ifneq ($(NO_STACKTRACE), True)
+  # Check and include lib-unwind in a portable way
+  ifneq ($(uname_S),Darwin)
+    # already included on macosx, but need to get the correct flags in other supported platforms.
+    ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
+      LDLIBS += `pkg-config --libs libunwind`
+      CFLAGS += `pkg-config --cflags libunwind`
+    else
+      $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
+      CFLAGS += -D NO_STACKTRACE
+    endif
+  endif
+else
+  # Stacktraces disabled
+  CFLAGS += -D NO_STACKTRACE
+endif
+
+"""
+               else
+                       makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
+               end
+
+               makefile.write """
+# Special configuration for Darwin
+ifeq ($(uname_S),Darwin)
+       # Remove POSIX flag -lrt
+       LDLIBS := $(filter-out -lrt,$(LDLIBS))
+endif
+
+# Special configuration for Windows under mingw64
+ifneq ($(findstring MINGW64,$(uname_S)),)
+       # Use the pcreposix regex library
+       LDLIBS += -lpcreposix
+
+       # Remove POSIX flag -lrt
+       LDLIBS := $(filter-out -lrt,$(LDLIBS))
+
+       # Silence warnings when storing Int, Char and Bool as pointer address
+       CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast
+endif
+
+# Add the compilation dir to the Java CLASSPATH
+ifeq ($(CLASSPATH),)
+       CLASSPATH := .
+else
+       CLASSPATH := $(CLASSPATH):.
+endif
+
+"""
 
                makefile.write("all: {outpath}\n")
                if outpath != real_outpath then
@@ -371,14 +491,31 @@ class MakefileToolchain
                        dep_rules.add(o)
                end
 
-               var java_files = new Array[ExternFile]
+               # Generate linker script, if any
+               if not compiler.linker_script.is_empty then
+                       var linker_script_path = "{compile_dir}/linker_script"
+                       ofiles.add "linker_script"
+                       var f = new FileWriter.open(linker_script_path)
+                       for l in compiler.linker_script do
+                               f.write l
+                               f.write "\n"
+                       end
+                       f.close
+               end
 
+               # pkg-config annotation support
                var pkgconfigs = new Array[String]
                for f in compiler.extern_bodies do
                        pkgconfigs.add_all f.pkgconfigs
                end
-               # Protect pkg-config
+
+               # Only test if pkg-config is used
                if not pkgconfigs.is_empty then
+
+                       # Check availability of pkg-config, silence the proc output
+                       toolcontext.check_pkgconfig_packages pkgconfigs
+
+                       # Double the check in the Makefile in case it's distributed
                        makefile.write """
 # does pkg-config exists?
 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
@@ -396,10 +533,10 @@ endif
                end
 
                # Compile each required extern body into a specific .o
+               var java_files = new Array[ExternFile]
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
-                       var ff = f.filename.basename("")
-                       makefile.write("{o}: {ff}\n")
+                       makefile.write("{o}: {f.filename}\n")
                        makefile.write("\t{f.makefile_rule_content}\n\n")
                        dep_rules.add(f.makefile_rule_name)
 
@@ -426,9 +563,9 @@ endif
                end
                makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
                # Clean
-               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+               makefile.write("clean:\n\trm -f {ofiles.join(" ")} 2>/dev/null\n")
                if outpath != real_outpath then
-                       makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+                       makefile.write("\trm -f -- {outpath.escape_to_sh} 2>/dev/null\n")
                end
                makefile.close
                self.toolcontext.info("Generated makefile: {makepath}", 2)
@@ -436,22 +573,27 @@ endif
                makepath.file_copy_to "{compile_dir}/Makefile"
        end
 
-       fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
+       # The C code is generated, compile it to an executable
+       fun compile_c_code(compile_dir: String)
        do
-               var makename = makefile_name(compiler.mainmodule)
+               var makename = makefile_name
 
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
-               self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
+
+               var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
+               self.toolcontext.info(command, 2)
 
                var res
                if self.toolcontext.verbose_level >= 3 then
-                       res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
+                       res = sys.system("{command} 2>&1")
+               else if is_windows then
+                       res = sys.system("{command} 2>&1 >nul")
                else
-                       res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
+                       res = sys.system("{command} 2>&1 >/dev/null")
                end
                if res != 0 then
-                       toolcontext.error(null, "make failed! Error code: {res}.")
+                       toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
                end
        end
 end
@@ -473,14 +615,41 @@ abstract class AbstractCompiler
        # The modelbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder is protected writable
 
+       # The associated toolchain
+       #
+       # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
+       var toolchain: Toolchain is noinit
+
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
+       # The targeted specific platform
+       var target_platform: Platform is noinit
+
+       # All methods who already has a callref_thunk generated for
+       var compiled_callref_thunk = new HashSet[MMethodDef]
+
+       var all_routine_types_name: Set[String] do
+               var res = new HashSet[String]
+               for name in ["Fun", "Proc", "FunRef", "ProcRef"] do
+                       # Currently there's 20 arity per func type
+                       for i in [0..20[ do
+                               res.add("{name}{i}")
+                       end
+               end
+               return res
+       end
+
        init
        do
                self.realmainmodule = mainmodule
+               target_platform = mainmodule.target_platform or else new Platform
        end
 
+       # Do the full code generation of the program `mainmodule`
+       # It is the main method usually called after the instantiation
+       fun do_compilation is abstract
+
        # Force the creation of a new file
        # The point is to avoid contamination between must-be-compiled-separately files
        fun new_file(name: String): CodeFile
@@ -499,7 +668,7 @@ abstract class AbstractCompiler
 
        # The list of all associated files
        # Used to generate .c files
-       var files = new List[CodeFile]
+       var files = new Array[CodeFile]
 
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
@@ -507,6 +676,10 @@ abstract class AbstractCompiler
        # Where global declaration are stored (the main .h)
        var header: CodeWriter is writable, noinit
 
+       # Additionnal linker script for `ld`.
+       # Mainly used to do specific link-time symbol resolution
+       var linker_script = new Array[String]
+
        # Provide a declaration that can be requested (before or latter) by a visitor
        fun provide_declaration(key: String, s: String)
        do
@@ -524,9 +697,9 @@ abstract class AbstractCompiler
        # Binds the generated C function names to Nit function names
        fun build_c_to_nit_bindings
        do
-               var compile_dir = modelbuilder.compile_dir
+               var compile_dir = toolchain.compile_dir
 
-               var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
+               var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
                stream.write("#include <string.h>\n")
                stream.write("#include <stdlib.h>\n")
                stream.write("#include \"c_functions_hash.h\"\n")
@@ -556,11 +729,11 @@ abstract class AbstractCompiler
                stream.write("\}\n")
                stream.close
 
-               stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
+               stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
                stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
                stream.close
 
-               extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
+               extern_bodies.add(new ExternCFile("c_functions_hash.c", ""))
        end
 
        # Compile C headers
@@ -569,23 +742,48 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
+               # longjmp !
+               self.header.add_decl("#include <setjmp.h>\n")
+               self.header.add_decl("#include <sys/types.h>\n")
+               self.header.add_decl("#include <unistd.h>\n")
+               self.header.add_decl("#include <stdint.h>\n")
+               self.header.add_decl("#ifdef __linux__")
+               self.header.add_decl("  #include <endian.h>")
+               self.header.add_decl("#endif")
+               self.header.add_decl("#include <inttypes.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
+               if modelbuilder.toolcontext.opt_trace.value then self.header.add_decl("#include \"traces.h\"")
+               self.header.add_decl("#ifdef __APPLE__")
+               self.header.add_decl("  #include <TargetConditionals.h>")
+               self.header.add_decl("  #include <syslog.h>")
+               self.header.add_decl("  #include <libkern/OSByteOrder.h>")
+               self.header.add_decl("  #define be32toh(x) OSSwapBigToHostInt32(x)")
+               self.header.add_decl("#endif")
+               self.header.add_decl("#ifdef _WIN32")
+               self.header.add_decl("  #define be32toh(val) _byteswap_ulong(val)")
+               self.header.add_decl("#endif")
                self.header.add_decl("#ifdef ANDROID")
+               self.header.add_decl("  #ifndef be32toh")
+               self.header.add_decl("          #define be32toh(val) betoh32(val)")
+               self.header.add_decl("  #endif")
                self.header.add_decl("  #include <android/log.h>")
                self.header.add_decl("  #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
+               self.header.add_decl("#elif TARGET_OS_IPHONE")
+               self.header.add_decl("  #define PRINT_ERROR(...) syslog(LOG_ERR, __VA_ARGS__)")
                self.header.add_decl("#else")
                self.header.add_decl("  #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
                self.header.add_decl("#endif")
 
                compile_header_structs
                compile_nitni_structs
+               compile_catch_stack
 
                var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
                if gccd_disable.has("noreturn") or gccd_disable.has("all") then
                        # Signal handler function prototype
-                       self.header.add_decl("void show_backtrace(int);")
+                       self.header.add_decl("void fatal_exit(int);")
                else
-                       self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
+                       self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
                end
 
                if gccd_disable.has("likely") or gccd_disable.has("all") then
@@ -607,6 +805,18 @@ abstract class AbstractCompiler
                self.header.add_decl("extern val *glob_sys;")
        end
 
+       # Stack stocking environment for longjumps
+       protected fun compile_catch_stack do
+               self.header.add_decl """
+struct catch_stack_t {
+       int cursor;
+       int currentSize;
+       jmp_buf *envs;
+};
+extern struct catch_stack_t *getCatchStack();
+"""
+       end
+
        # Declaration of structures for live Nit types
        protected fun compile_header_structs is abstract
 
@@ -654,7 +864,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
 
                if finalize_meth == null then
-                       modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
+                       modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
                        return
                end
 
@@ -665,6 +875,20 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add "\}"
        end
 
+       # Hook to add specif piece of code before the the main C function.
+       #
+       # Is called by `compile_main_function`
+       fun compile_before_main(v: VISITOR)
+       do
+       end
+
+       # Hook to add specif piece of code at the begin on the main C function.
+       #
+       # Is called by `compile_main_function`
+       fun compile_begin_main(v: VISITOR)
+       do
+       end
+
        # Generate the main C function.
        #
        # This function:
@@ -676,24 +900,59 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
        do
                var v = self.new_visitor
                v.add_decl("#include <signal.h>")
-               var ost = modelbuilder.toolcontext.opt_stacktrace.value
-               var platform = mainmodule.target_platform
-
-               if platform != null and not platform.supports_libunwind then ost = "none"
+               var platform = target_platform
 
-               var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
+               var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
 
-               if ost == "nitstack" or ost == "libunwind" then
+               if platform.supports_libunwind then
+                       v.add_decl("#ifndef NO_STACKTRACE")
                        v.add_decl("#define UNW_LOCAL_ONLY")
                        v.add_decl("#include <libunwind.h>")
-                       if ost == "nitstack" then
-                               v.add_decl("#include \"c_functions_hash.h\"")
-                       end
+                       v.add_decl("#include \"c_functions_hash.h\"")
+                       v.add_decl("#endif")
                end
                v.add_decl("int glob_argc;")
                v.add_decl("char **glob_argv;")
                v.add_decl("val *glob_sys;")
 
+               # Store catch stack in thread local storage
+               v.add_decl """
+#if defined(TARGET_OS_IPHONE)
+       // Use pthread_key_create and others for iOS
+       #include <pthread.h>
+
+       static pthread_key_t catch_stack_key;
+       static pthread_once_t catch_stack_key_created = PTHREAD_ONCE_INIT;
+
+       static void create_catch_stack()
+       {
+               pthread_key_create(&catch_stack_key, NULL);
+       }
+
+       struct catch_stack_t *getCatchStack()
+       {
+               pthread_once(&catch_stack_key_created, &create_catch_stack);
+               struct catch_stack_t *data = pthread_getspecific(catch_stack_key);
+               if (data == NULL) {
+                       data = malloc(sizeof(struct catch_stack_t));
+                       data->cursor = -1;
+                       data->currentSize = 0;
+                       data->envs = NULL;
+                       pthread_setspecific(catch_stack_key, data);
+               }
+               return data;
+       }
+#else
+       // Use __thread when available
+       __thread struct catch_stack_t catchStack = {-1, 0, NULL};
+
+       struct catch_stack_t *getCatchStack()
+       {
+               return &catchStack;
+       }
+#endif
+"""
+
                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};")
@@ -721,13 +980,9 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                        v.compiler.header.add_decl("extern long count_isset_checks;")
                end
 
-               v.add_decl("void sig_handler(int signo)\{")
-               v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
-               v.add_decl("show_backtrace(signo);")
-               v.add_decl("\}")
-
-               v.add_decl("void show_backtrace (int signo) \{")
-               if ost == "nitstack" or ost == "libunwind" then
+               v.add_decl("static void show_backtrace(void) \{")
+               if platform.supports_libunwind then
+                       v.add_decl("#ifndef NO_STACKTRACE")
                        v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
                        v.add_decl("unw_cursor_t cursor;")
                        v.add_decl("if(opt==NULL)\{")
@@ -741,37 +996,60 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                        v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
                        v.add_decl("while (unw_step(&cursor) > 0) \{")
                        v.add_decl("    unw_get_proc_name(&cursor, procname, 100, &ip);")
-                       if ost == "nitstack" then
                        v.add_decl("    const char* recv = get_nit_name(procname, strlen(procname));")
                        v.add_decl("    if (recv != NULL)\{")
                        v.add_decl("            PRINT_ERROR(\"` %s\\n\", recv);")
                        v.add_decl("    \}else\{")
                        v.add_decl("            PRINT_ERROR(\"` %s\\n\", procname);")
                        v.add_decl("    \}")
-                       else
-                       v.add_decl("    PRINT_ERROR(\"` %s \\n\",procname);")
-                       end
                        v.add_decl("\}")
                        v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
                        v.add_decl("free(procname);")
                        v.add_decl("\}")
+                       v.add_decl("#endif /* NO_STACKTRACE */")
                end
-               v.add_decl("exit(signo);")
                v.add_decl("\}")
 
+               v.add_decl("void sig_handler(int signo)\{")
+               v.add_decl "#ifdef _WIN32"
+               v.add_decl "PRINT_ERROR(\"Caught signal : %s\\n\", signo);"
+               v.add_decl "#else"
+               v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
+               v.add_decl "#endif"
+               v.add_decl("show_backtrace();")
+               # rethrows
+               v.add_decl("signal(signo, SIG_DFL);")
+               v.add_decl "#ifndef _WIN32"
+               v.add_decl("kill(getpid(), signo);")
+               v.add_decl "#endif"
+               v.add_decl("\}")
+
+               v.add_decl("void fatal_exit(int status) \{")
+               v.add_decl("show_backtrace();")
+               v.add_decl("exit(status);")
+               v.add_decl("\}")
+
+               compile_before_main(v)
+
                if no_main then
                        v.add_decl("int nit_main(int argc, char** argv) \{")
                else
                        v.add_decl("int main(int argc, char** argv) \{")
                end
 
+               compile_begin_main(v)
+
+               v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
                v.add("signal(SIGABRT, sig_handler);")
                v.add("signal(SIGFPE, sig_handler);")
                v.add("signal(SIGILL, sig_handler);")
                v.add("signal(SIGINT, sig_handler);")
                v.add("signal(SIGTERM, sig_handler);")
                v.add("signal(SIGSEGV, sig_handler);")
-               v.add("signal(SIGPIPE, sig_handler);")
+               v.add "#endif"
+               v.add "#ifndef _WIN32"
+               v.add("signal(SIGPIPE, SIG_IGN);")
+               v.add "#endif"
 
                v.add("glob_argc = argc; glob_argv = argv;")
                v.add("initialize_gc_option();")
@@ -836,6 +1114,12 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
 
                v.add("return 0;")
                v.add("\}")
+
+               for m in mainmodule.in_importation.greaters do
+                       var f = "FILE_"+m.c_name
+                       v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
+                       provide_declaration(f, "extern const char {f}[];")
+               end
        end
 
        # Copile all C functions related to the [incr|decr]_ref features of the FFI
@@ -988,35 +1272,43 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
        end
 
        fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
-
-       # 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 file unit (may be more than one file if
-# A file unit aim to be autonomous and is made or one or more `CodeWriter`s
+# C code file generated from the `writers` and the `required_declarations`
+#
+# A file unit aims to be autonomous and is made or one or more `CodeWriter`s.
 class CodeFile
+       # Basename of the file, will be appended by `.h` and `.c`
        var name: String
+
+       # `CodeWriter` used in sequence to fill the top of the body, then the bottom
        var writers = new Array[CodeWriter]
+
+       # Required declarations keys
+       #
+       # See: `provide_declaration`
        var required_declarations = new HashSet[String]
 end
 
-# Where to store generated lines
+# Store generated lines
+#
+# Instances are added to `file.writers` at construction.
 class CodeWriter
+       # Parent `CodeFile`
        var file: CodeFile
-       var lines: List[String] = new List[String]
-       var decl_lines: List[String] = new List[String]
 
-       # Add a line in the main part of the generated C
+       # Main lines of code (written at the bottom of the body)
+       var lines = new Array[String]
+
+       # Lines of code for declarations (written at the top of the body)
+       var decl_lines = new Array[String]
+
+       # Add a line in the main lines of code (to `lines`)
        fun add(s: String) do self.lines.add(s)
 
-       # Add a line in the
-       # (used for local or global declaration)
+       # Add a declaration line (to `decl_lines`)
+       #
+       # Used for local and global declaration.
        fun add_decl(s: String) do self.decl_lines.add(s)
 
        init
@@ -1036,8 +1328,8 @@ abstract class AbstractCompilerVisitor
        # The current visited AST node
        var current_node: nullable ANode = null is writable
 
-       # The current `Frame`
-       var frame: nullable Frame = null is writable
+       # The current `StaticFrame`
+       var frame: nullable StaticFrame = null is writable
 
        # Alias for self.compiler.mainmodule.object_type
        fun object_type: MClassType do return self.compiler.mainmodule.object_type
@@ -1052,9 +1344,6 @@ abstract class AbstractCompilerVisitor
                self.writer = new CodeWriter(compiler.files.last)
        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
@@ -1064,75 +1353,80 @@ abstract class AbstractCompilerVisitor
 
        fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
-               var initializers = callsite.mpropdef.initializers
-               if not initializers.is_empty then
-                       var recv = arguments.first
-
-                       var i = 1
-                       for p in initializers do
-                               if p isa MMethod then
-                                       var args = [recv]
-                                       for x in p.intro.msignature.mparameters do
-                                               args.add arguments[i]
-                                               i += 1
-                                       end
-                                       self.send(p, args)
-                               else if p isa MAttribute then
-                                       self.write_attribute(p, recv, arguments[i])
-                                       i += 1
-                               else abort
-                       end
-                       assert i == arguments.length
-
-                       return self.send(callsite.mproperty, [recv])
-               end
-
+               if callsite.is_broken then return null
                return self.send(callsite.mproperty, arguments)
        end
 
        fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
 
-       fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
+       fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
+
+       # Return an element of a native array.
+       # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+       fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
+
+       # Store an element in a native array.
+       # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+       fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
+
+       # Instantiate a new routine pointer
+       fun routine_ref_instance(routine_mclass_type: MClassType, recv: RuntimeVariable, callsite: CallSite): RuntimeVariable is abstract
+
+       # Call the underlying referenced function
+       fun routine_ref_call(mmethoddef: MMethodDef, args: Array[RuntimeVariable]) is abstract
 
-       fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
+       # Allocate `size` bytes with the low_level `nit_alloc` C function
+       #
+       # This method can be redefined to inject statistic or tracing code.
+       #
+       # `tag` if any, is used to mark the class of the allocated object.
+       fun nit_alloc(size: String, tag: nullable String): String
+       do
+               return "nit_alloc({size})"
+       end
 
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of runtime variables to use in the call.
-       fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
+       fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
        do
-               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var msignature = mpropdef.msignature.as(not null)
                var res = new Array[RuntimeVariable]
                res.add(recv)
 
-               if args.is_empty then return res
+               if msignature.arity == 0 then return res
+
+               if map == null then
+                       assert args.length == msignature.arity
+                       for ne in args do
+                               res.add self.expr(ne, null)
+                       end
+                       return res
+               end
 
-               var vararg_rank = msignature.vararg_rank
-               var vararg_len = args.length - msignature.arity
-               if vararg_len < 0 then vararg_len = 0
+               # Eval in order of arguments, not parameters
+               var exprs = new Array[RuntimeVariable].with_capacity(args.length)
+               for ne in args do
+                       exprs.add self.expr(ne, null)
+               end
 
+               # Fill `res` with the result of the evaluation according to the mapping
                for i in [0..msignature.arity[ do
-                       if i == vararg_rank then
-                               var ne = args[i]
-                               if ne isa AVarargExpr then
-                                       var e = self.expr(ne.n_expr, null)
-                                       res.add(e)
-                                       continue
-                               end
-                               var vararg = new Array[RuntimeVariable]
-                               for j in [vararg_rank..vararg_rank+vararg_len] do
-                                       var e = self.expr(args[j], null)
-                                       vararg.add(e)
-                               end
-                               var elttype = msignature.mparameters[vararg_rank].mtype
+                       var param = msignature.mparameters[i]
+                       var j = map.map.get_or_null(i)
+                       if j == null then
+                               # default value
+                               res.add(null_instance)
+                               continue
+                       end
+                       if param.is_vararg and args[i].vararg_decl > 0 then
+                               var vararg = exprs.sub(j, args[i].vararg_decl)
+                               var elttype = param.mtype
                                var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
                                res.add(arg)
-                       else
-                               var j = i
-                               if i > vararg_rank then j += vararg_len
-                               var e = self.expr(args[j], null)
-                               res.add(e)
+                               continue
                        end
+                       res.add exprs[j]
                end
                return res
        end
@@ -1160,17 +1454,26 @@ abstract class AbstractCompilerVisitor
        do
                mtype = self.anchor(mtype)
                var valmtype = value.mcasttype
+
+               # CPrimitive is the best you can do
+               if valmtype.is_c_primitive then
+                       return value
+               end
+
+               # Do nothing if useless autocast
                if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                        return value
                end
 
+               # Just as_not_null if the target is not nullable.
+               #
+               # eg `nullable PreciseType` adapted to `Object` gives precisetype.
                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
+                       mtype = valmtype.mtype
                end
+
+               var res = new RuntimeVariable(value.name, value.mtype, mtype)
+               return res
        end
 
        # Generate a super call from a method definition
@@ -1238,13 +1541,18 @@ abstract class AbstractCompilerVisitor
 
        # Checks
 
+       # Can value be null? (according to current knowledge)
+       fun maybe_null(value: RuntimeVariable): Bool
+       do
+               return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
+       end
+
        # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
 
-               var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
-               if maybenull then
+               if maybe_null(recv) then
                        self.add("if (unlikely({recv} == NULL)) \{")
                        self.add_abort("Receiver is null")
                        self.add("\}")
@@ -1296,7 +1604,7 @@ abstract class AbstractCompilerVisitor
        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
+       # NOTE: we do not return a `RuntimeVariable` "CString" as the class may not exist in the module/program
        fun class_name_string(value: RuntimeVariable): String is abstract
 
        # Variables handling
@@ -1361,6 +1669,24 @@ abstract class AbstractCompilerVisitor
        # Generate a alloc-instance + init-attributes
        fun init_instance(mtype: MClassType): RuntimeVariable is abstract
 
+       # Allocate and init attributes of an instance of a standard or extern class
+       #
+       # Does not support universals and the pseudo-internal `NativeArray` class.
+       fun init_instance_or_extern(mtype: MClassType): RuntimeVariable
+       do
+               var recv
+               var ctype = mtype.ctype
+               assert mtype.mclass.name != "NativeArray"
+               if not mtype.is_c_primitive then
+                       recv = init_instance(mtype)
+               else if ctype == "char*" then
+                       recv = new_expr("NULL/*special!*/", mtype)
+               else
+                       recv = new_expr("({ctype})0/*special!*/", mtype)
+               end
+               return recv
+       end
+
        # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
        fun set_finalizer(recv: RuntimeVariable)
        do
@@ -1372,41 +1698,133 @@ abstract class AbstractCompilerVisitor
                end
        end
 
+       # The currently processed module
+       #
+       # alias for `compiler.mainmodule`
+       fun mmodule: MModule do return compiler.mainmodule
+
        # 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};")
+               var t = mmodule.int_type
+               var res = new RuntimeVariable("{value.to_s}l", t, t)
                return res
        end
 
-       # Generate an integer value
-       fun bool_instance(value: Bool): RuntimeVariable
+       # Generate a byte value
+       fun byte_instance(value: Byte): RuntimeVariable
+       do
+               var t = mmodule.byte_type
+               var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
+               return res
+       end
+
+       # Generate an int8 value
+       fun int8_instance(value: Int8): RuntimeVariable
+       do
+               var t = mmodule.int8_type
+               var res = new RuntimeVariable("INT8_C({value.to_s})", t, t)
+               return res
+       end
+
+       # Generate an int16 value
+       fun int16_instance(value: Int16): RuntimeVariable
+       do
+               var t = mmodule.int16_type
+               var res = new RuntimeVariable("INT16_C({value.to_s})", t, t)
+               return res
+       end
+
+       # Generate a uint16 value
+       fun uint16_instance(value: UInt16): RuntimeVariable
+       do
+               var t = mmodule.uint16_type
+               var res = new RuntimeVariable("UINT16_C({value.to_s})", t, t)
+               return res
+       end
+
+       # Generate an int32 value
+       fun int32_instance(value: Int32): RuntimeVariable
+       do
+               var t = mmodule.int32_type
+               var res = new RuntimeVariable("INT32_C({value.to_s})", t, t)
+               return res
+       end
+
+       # Generate a uint32 value
+       fun uint32_instance(value: UInt32): RuntimeVariable
+       do
+               var t = mmodule.uint32_type
+               var res = new RuntimeVariable("UINT32_C({value.to_s})", t, t)
+               return res
+       end
+
+       # Generate a char value
+       fun char_instance(value: Char): RuntimeVariable
        do
-               var res = self.new_var(self.get_class("Bool").mclass_type)
-               if value then
-                       self.add("{res} = 1;")
+               var t = mmodule.char_type
+
+               if value.code_point < 128 then
+                       return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
                else
-                       self.add("{res} = 0;")
+                       return new RuntimeVariable("{value.code_point}", t, t)
                end
+       end
+
+       # Generate a float value
+       #
+       # FIXME pass a Float, not a string
+       fun float_instance(value: Float): RuntimeVariable
+       do
+               var t = mmodule.float_type
+               var res = new RuntimeVariable("{value.to_hexa_exponential_notation}", t, t)
+               return res
+       end
+
+       # Generate an integer value
+       fun bool_instance(value: Bool): RuntimeVariable
+       do
+               var s = if value then "1" else "0"
+               var res = new RuntimeVariable(s, bool_type, bool_type)
+               return res
+       end
+
+       # Generate the `null` value
+       fun null_instance: RuntimeVariable
+       do
+               var t = compiler.mainmodule.model.null_type
+               var res = new RuntimeVariable("((val*)NULL)", t, t)
                return res
        end
 
+       # Generates a CString instance fully escaped in C-style \xHH fashion
+       fun c_string_instance(ns: CString, len: Int): RuntimeVariable do
+               var mtype = mmodule.c_string_type
+               var nat = new_var(mtype)
+               var byte_esc = new Buffer.with_cap(len * 4)
+               for i in [0 .. len[ do
+                       byte_esc.append("\\x{ns[i].to_hex}")
+               end
+               self.add("{nat} = \"{byte_esc}\";")
+               return nat
+       end
+
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
-               var mtype = self.get_class("String").mclass_type
+               var mtype = mmodule.string_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("if (likely({name}!=NULL)) \{")
                self.add("{res} = {name};")
                self.add("\} else \{")
-               var native_mtype = self.get_class("NativeString").mclass_type
+               var native_mtype = mmodule.c_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
-               var length = self.int_instance(string.length)
-               self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
+               var byte_length = self.int_instance(string.byte_length)
+               var unilen = self.int_instance(string.length)
+               self.add("{res} = {self.send(self.get_property("to_s_unsafe", native_mtype), [nat, byte_length, unilen, value_instance(false), value_instance(false)]).as(not null)};")
                self.add("{name} = {res};")
                self.add("\}")
                return res
@@ -1458,19 +1876,20 @@ abstract class AbstractCompilerVisitor
                self.require_declaration(s)
        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)
+       # Look for a needed .h and .c file for a given module
+       # This is used for the legacy FFI
+       fun add_extern(mmodule: MModule)
        do
+               var file = mmodule.filepath
                file = file.strip_extension(".nit")
                var tryfile = file + ".nit.h"
                if tryfile.file_exists then
-                       self.declare_once("#include \"{tryfile.basename("")}\"")
+                       self.declare_once("#include \"{tryfile.basename}\"")
                        self.compiler.files_to_copy.add(tryfile)
                end
                tryfile = file + "_nit.h"
                if tryfile.file_exists then
-                       self.declare_once("#include \"{tryfile.basename("")}\"")
+                       self.declare_once("#include \"{tryfile.basename}\"")
                        self.compiler.files_to_copy.add(tryfile)
                end
 
@@ -1481,7 +1900,7 @@ abstract class AbstractCompilerVisitor
                        tryfile = file + "_nit.c"
                        if not tryfile.file_exists then return
                end
-               var f = new ExternCFile(tryfile.basename(""), "")
+               var f = new ExternCFile(tryfile.basename, "")
                self.compiler.extern_bodies.add(f)
                self.compiler.files_to_copy.add(tryfile)
        end
@@ -1498,18 +1917,40 @@ abstract class AbstractCompilerVisitor
        # used by aborts, asserts, casts, etc.
        fun add_abort(message: String)
        do
+               add_raw_throw
                self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
                add_raw_abort
        end
 
+       # Generate a long jump if there is a catch block.
+       #
+       # This method should be called before the error messages and before a `add_raw_abort`.
+       fun add_raw_throw
+       do
+               self.add("\{")
+               self.add("struct catch_stack_t *catchStack = getCatchStack();")
+               self.add("if(catchStack->cursor >= 0)\{")
+               self.add("      longjmp(catchStack->envs[catchStack->cursor], 1);")
+               self.add("\}")
+               self.add("\}")
+       end
+
+       # Generate abort without a message.
+       #
+       # Used when one need a more complex message.
+       # Do not forget to call `add_raw_abort` before the display of a custom user message.
        fun add_raw_abort
        do
-               if self.current_node != null and self.current_node.location.file != null then
-                       self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+               var current_node = self.current_node
+               if current_node != null and current_node.location.file != null and
+                               current_node.location.file.mmodule != null then
+                       var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
+                       self.require_declaration(f)
+                       self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
                else
                        self.add("PRINT_ERROR(\"\\n\");")
                end
-               self.add("show_backtrace(1);")
+               self.add("fatal_exit(1);")
        end
 
        # Add a dynamic cast
@@ -1517,6 +1958,7 @@ abstract class AbstractCompilerVisitor
        do
                var res = self.type_test(value, mtype, tag)
                self.add("if (unlikely(!{res})) \{")
+               self.add_raw_throw
                var cn = self.class_name_string(value)
                self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
                self.add_raw_abort
@@ -1534,6 +1976,22 @@ abstract class AbstractCompilerVisitor
        fun stmt(nexpr: nullable AExpr)
        do
                if nexpr == null then return
+               if nexpr.is_broken then
+                       # Untyped expression.
+                       # Might mean dead code or invalid code
+                       # so aborts
+                       add_abort("FATAL: bad statement executed.")
+                       return
+               end
+
+               var narray = nexpr.comprehension
+               if narray != null then
+                       var recv = frame.comprehension.as(not null)
+                       var val = expr(nexpr, narray.element_mtype)
+                       compile_callsite(narray.push_callsite.as(not null), [recv, val])
+                       return
+               end
+
                var old = self.current_node
                self.current_node = nexpr
                nexpr.stmt(self)
@@ -1546,7 +2004,25 @@ abstract class AbstractCompilerVisitor
        do
                var old = self.current_node
                self.current_node = nexpr
-               var res = nexpr.expr(self).as(not null)
+
+               var res = null
+               if nexpr.mtype != null then
+                       res = nexpr.expr(self)
+               end
+
+               if res == null then
+                       # Untyped expression.
+                       # Might mean dead code or invalid code.
+                       # so aborts
+                       add_abort("FATAL: bad expression executed.")
+                       # and return a placebo result to please the C compiler
+                       if mtype == null then mtype = compiler.mainmodule.object_type
+                       res = new_var(mtype)
+
+                       self.current_node = old
+                       return res
+               end
+
                if mtype != null then
                        mtype = self.anchor(mtype)
                        res = self.autobox(res, mtype)
@@ -1578,7 +2054,26 @@ abstract class AbstractCompilerVisitor
 end
 
 # A C function associated to a Nit method
-# Because of customization, a given Nit method can be compiler more that once
+# This is the base class for all runtime representation of a nit method.
+# It implements the Template Design Pattern whose steps are :
+#       1. create the receiver `RuntimeVariable` (selfvar)
+#       2. create a `StaticFrame`
+#       3. resolve the return type.
+#       4. write the function signature
+#       5. Declare the function signature (for C header files)
+#       6. writer the function body
+#       7. post-compiler hook (optional)
+# Each step is called in `AbstractRuntimeFunction::compile_to_c`. A default
+# body is provided foreach step except for compilation hooks. Subclasses may
+# redefine any of the above mentioned steps. However, it's not recommanded
+# to override `compile_to_c` since there's an ordering of the compilation steps.
+# Any information between steps must be saved in the visitor. Currently most
+# of the future runtime info are stored in the `StaticFrame` of the visitor,
+# e.g. the receiver (selfvar), the arguments, etc.
+#
+# Moreover, any subclass may redefine : the receiver type, the return type and
+# the signature. This allow for a better customization for the final implementation.
+# Because of customization, a given Nit method can be compile more that once.
 abstract class AbstractRuntimeFunction
 
        type COMPILER: AbstractCompiler
@@ -1587,6 +2082,8 @@ abstract class AbstractRuntimeFunction
        # The associated Nit method
        var mmethoddef: MMethodDef
 
+       protected var return_mtype: nullable MType = null
+
        # The mangled c name of the runtime_function
        # Subclasses should redefine `build_c_name` instead
        fun c_name: String
@@ -1598,6 +2095,8 @@ abstract class AbstractRuntimeFunction
                return res
        end
 
+       fun c_ref: String do return "&{c_name}"
+
        # Non cached version of `c_name`
        protected fun build_c_name: String is abstract
 
@@ -1607,16 +2106,251 @@ abstract class AbstractRuntimeFunction
        # 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 `AbstractRuntimeFunction`
-       # Warning: compile more than once compilation makes CC unhappy
-       fun compile_to_c(compiler: COMPILER) is abstract
-end
+       # Returns `true` if the associated `mmethoddef`'s return type isn't null,
+       # otherwise `false`.
+       fun has_return: Bool
+       do
+               return mmethoddef.msignature.return_mtype != null
+       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 current msignature to use when compiling : `signature_to_c` and `body_to_c`.
+       # This method is useful since most concrete implementation doesn't use the
+       # mmethoddef's signature. By providing a definition in the abstract class,
+       # subclasses can use any msignature.
+       fun msignature: MSignature
+       do
+               return mmethoddef.msignature.as(not null)
+       end
+
+       # The current receiver type to compile : `signature_to_c` and `body_to_c`.
+       # See `msignature` method for more information.
+       protected fun recv_mtype: MType
+       do
+               return mmethoddef.mclassdef.bound_mtype
+       end
+
+       # Prepare the `self` runtime variable to be used by the rest of
+       # compilation steps.
+       # Step 1
+       protected fun resolve_receiver(v: VISITOR): RuntimeVariable
+       do
+               var casttype = mmethoddef.mclassdef.bound_mtype
+               return new RuntimeVariable("self", recv_mtype, casttype)
+       end
+
+       # Builds the static frame for current runtime method
+       # Step 2
+       protected fun build_frame(v: VISITOR, arguments: Array[RuntimeVariable]): StaticFrame
+       do
+               return new StaticFrame(v, mmethoddef, recv_mtype.as(MClassType), arguments)
+       end
+
+       # Step 3 : Returns the return type used by the runtime function.
+       protected fun resolve_return_mtype(v: VISITOR)
+       do
+               return_mtype = msignature.return_mtype
+       end
+
+       # Fills the argument array inside v.frame.arguments, calling `resolve_ith_parameter`
+       # for each parameter.
+       private fun fill_parameters(v: VISITOR)
+       do
+               assert v.frame != null
+               for i in [0..msignature.arity[ do
+                       var arg = resolve_ith_parameter(v, i)
+                       v.frame.arguments.add(arg)
+               end
+       end
+
+       # Step 4 : Creates `RuntimeVariable` for each method argument.
+       protected fun resolve_ith_parameter(v: VISITOR, i: Int): RuntimeVariable
+       do
+               var mp = msignature.mparameters[i]
+               var mtype = mp.mtype
+               if mp.is_vararg then
+                       mtype = v.mmodule.array_type(mtype)
+               end
+               return new RuntimeVariable("p{i}", mtype, mtype)
+       end
+
+       # Generate the code for the signature with an open curly brace
+       #
+       # Returns the generated signature without a semicolon and curly brace,
+       # e.g `RES f(T0 p0, T1 p1, ..., TN pN)`
+       # Step 5
+       protected fun signature_to_c(v: VISITOR): String
+       do
+               assert v.frame != null
+               var arguments = v.frame.arguments
+               var comment = new FlatBuffer
+               var selfvar = v.frame.selfvar
+               var c_ret = "void"
+               if has_return then
+                       c_ret = "{return_mtype.ctype}"
+               end
+               var sig = new FlatBuffer
+               sig.append("{c_ret} {c_name}({recv_mtype.ctype} self")
+               comment.append("({selfvar}: {selfvar.mtype}")
+
+               for i in [0..arguments.length-1[ do
+                       # Skip the receiver
+                       var arg = arguments[i+1]
+                       comment.append(", {arg.mtype}")
+                       sig.append(", {arg.mtype.ctype} p{i}")
+               end
+               sig.append(")")
+               comment.append(")")
+               if has_return then
+                       comment.append(": {return_mtype.as(not null)}")
+               end
+               v.add_decl("/* method {self} for {comment} */")
+               v.add_decl("{sig} \{")
+               return sig.to_s
+       end
+
+       # How the concrete compiler will declare the method, e.g inside a global header file,
+       # extern signature, etc.
+       # Step 6
+       protected fun declare_signature(v: VISITOR, signature: String) is abstract
+
+       # Generate the code for the body without return statement at the end and
+       # no curly brace.
+       # Step 7
+       protected fun body_to_c(v: VISITOR)
+       do
+               mmethoddef.compile_inside_to_c(v, v.frame.arguments)
+       end
+
+       # Hook called at the end of `compile_to_c` function. This function
+       # is useful if you need to register any function compiled to c.
+       # Step 8 (optional).
+       protected fun end_compile_to_c(v: VISITOR)
+       do
+               # Nothing to do by default
+       end
+
+       # Generate the code
+       fun compile_to_c(compiler: COMPILER)
+       do
+               var v = compiler.new_visitor
+               var selfvar = resolve_receiver(v)
+               var arguments = [selfvar]
+               var frame = build_frame(v, arguments)
+               v.frame = frame
+
+               resolve_return_mtype(v)
+               fill_parameters(v)
+
+               # WARNING: the signature must be declared before creating
+               # any returnlabel and returnvar (`StaticFrame`). Otherwise,
+               # you could end up with variable outside the function.
+               var sig = signature_to_c(v)
+               declare_signature(v, sig)
+
+               frame.returnlabel = v.get_name("RET_LABEL")
+               if has_return then
+                       var ret_mtype = return_mtype
+                       assert ret_mtype != null
+                       frame.returnvar = v.new_var(ret_mtype)
+               end
+
+               body_to_c(v)
+               v.add("{frame.returnlabel.as(not null)}:;")
+               if has_return then
+                       v.add("return {frame.returnvar.as(not null)};")
+               end
+               v.add "\}"
+               end_compile_to_c(v)
+       end
+end
+
+# Base class for all thunk-like function. A thunk is a function whose purpose
+# is to call another function. Like a class wrapper or decorator, a thunk is used
+# to computer things (conversions, log, etc) before or after a function call. It's
+# an intermediary between the caller and the callee.
+#
+# The most basic use of a thunk is to unbox its argument before invoking the real callee.
+# Virtual functions are a use case of thunk function:
+#
+# ~~~~nitish
+# redef class Object
+#       fun toto(x: Int): Int do return 1 + x
+# end
+# redef class Int
+#       redef fun toto(x) do return x + self
+# end
+#
+# ```generated C code
+# long Object__toto(val* self, long x) { return 1 + x }
+# long Int__toto(long self, long x) { return x + self }
+# long THUNK_Int__toto(val* self, long x) {
+#       long self2 = (long)(self)>>2    // Unboxing from Object to Int
+#       return Int_toto(self2, x)
+# }
+#
+# ```
+# ~~~~
+#
+# A thunk has its OWN SIGNATURE and is considered like any other `AbstractRuntimeFunction`.
+# Thus, one might need to be careful when overriding parent's method. Since most usage of
+# thunks in Nit is to unbox and box things between a caller and a callee, the default
+# implementation is doing just that. In other words, a thunk takes a signature and a method
+# and tries to cast its signature to the underlying method's signature.
+#
+# A virtual function has the same signature as its `mmethoddef` field, except for
+# its receiver is of type `Object`.
+# In the same vibe, a call reference has all of its argument boxed as `Object`.
+abstract class ThunkFunction
+       super AbstractRuntimeFunction
+
+       # Determines if the callsite should be polymorphic or static.
+       var polymorph_call_flag = false is writable
+
+       # The type expected by the callee. Used to resolve `mmethoddef`'s formal
+       # parameters and virtual type. This type must NOT need anchor.
+       fun target_recv: MType is abstract
+
+       redef fun body_to_c(v)
+       do
+               assert not target_recv.need_anchor
+               var frame = v.frame
+               assert frame != null
+               var selfvar = frame.selfvar
+               var arguments = frame.arguments
+               var arguments2 = new Array[RuntimeVariable]
+               arguments2.push(v.autobox(selfvar, target_recv))
+               var resolved_sig = msignature.resolve_for(target_recv, target_recv.as(MClassType), v.mmodule, true)
+               for i in [0..resolved_sig.arity[ do
+               var param = resolved_sig.mparameters[i]
+                       var mtype = param.mtype
+                       if param.is_vararg then
+                               mtype = v.mmodule.array_type(mtype)
+                       end
+                       var temp = v.autobox(arguments[i+1], mtype)
+                       arguments2.push(temp)
+               end
+               v.add("/* {mmethoddef}, {recv_mtype.ctype} */")
+               var subret: nullable RuntimeVariable = null
+               if polymorph_call_flag then
+                       subret = v.send(mmethoddef.mproperty, arguments2)
+               else
+                       subret = v.call(mmethoddef, arguments2[0].mcasttype.as(MClassType), arguments2)
+               end
+               if has_return then
+                       assert subret != null
+                       var subret2 = v.autobox(subret, return_mtype.as(not null))
+                       v.assign(frame.returnvar.as(not null), subret2)
+               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
 
@@ -1656,8 +2390,8 @@ class RuntimeVariable
        end
 end
 
-# A frame correspond to a visited property in a `GlobalCompilerVisitor`
-class Frame
+# The static context of a visited property in a `AbstractCompilerVisitor`
+class StaticFrame
 
        type VISITOR: AbstractCompilerVisitor
 
@@ -1683,6 +2417,17 @@ class Frame
        # Labels associated to a each escapemarks.
        # Because of inlinings, escape-marks must be associated to their context (the frame)
        private var escapemark_names = new HashMap[EscapeMark, String]
+
+       # The array comprehension currently filled, if any
+       private var comprehension: nullable RuntimeVariable = null
+
+       # Returns the first argument (the receiver) of a frame.
+       # REQUIRE: arguments.length >= 1
+       fun selfvar: RuntimeVariable
+       do
+               assert arguments.length >= 1
+               return arguments.first
+       end
 end
 
 redef class MType
@@ -1695,32 +2440,36 @@ redef class MType
        # Short name of the `ctype` to use in unions
        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 is protected writable
+       # Is the associated C type a primitive one?
+       #
+       # ENSURE `result == (ctype != "val*")`
+       fun is_c_primitive: Bool do return false
 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.c_name}__{mclass.name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
 
-       redef fun ctype: String
-       do
+       redef var ctype is lazy 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"
+                       return "uint32_t"
                else if mclass.name == "Float" then
                        return "double"
-               else if mclass.name == "NativeString" then
+               else if mclass.name == "Int8" then
+                       return "int8_t"
+               else if mclass.name == "Byte" then
+                       return "unsigned char"
+               else if mclass.name == "Int16" then
+                       return "int16_t"
+               else if mclass.name == "UInt16" then
+                       return "uint16_t"
+               else if mclass.name == "Int32" then
+                       return "int32_t"
+               else if mclass.name == "UInt32" then
+                       return "uint32_t"
+               else if mclass.name == "CString" then
                        return "char*"
                else if mclass.name == "NativeArray" then
                        return "val*"
@@ -1729,6 +2478,8 @@ redef class MClassType
                end
        end
 
+       redef var is_c_primitive is lazy do return ctype != "val*"
+
        redef fun ctype_extern: String
        do
                if mclass.kind == extern_kind then
@@ -1748,7 +2499,19 @@ redef class MClassType
                        return "c"
                else if mclass.name == "Float" then
                        return "d"
-               else if mclass.name == "NativeString" then
+               else if mclass.name == "Int8" then
+                       return "i8"
+               else if mclass.name == "Byte" then
+                       return "b"
+               else if mclass.name == "Int16" then
+                       return "i16"
+               else if mclass.name == "UInt16" then
+                       return "u16"
+               else if mclass.name == "Int32" then
+                       return "i32"
+               else if mclass.name == "UInt32" then
+                       return "u32"
+               else if mclass.name == "CString" then
                        return "str"
                else if mclass.name == "NativeArray" then
                        #return "{self.arguments.first.ctype}*"
@@ -1759,90 +2522,8 @@ redef class MClassType
        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.c_name}__{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.c_name}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
 end
 
 redef class MMethodDef
@@ -1850,6 +2531,7 @@ redef class MMethodDef
        fun can_inline(v: VISITOR): Bool
        do
                if is_abstract then return true
+               if constant_value != null then return true
                var modelbuilder = v.compiler.modelbuilder
                var node = modelbuilder.mpropdef2node(self)
                if node isa APropdef then
@@ -1857,6 +2539,8 @@ redef class MMethodDef
                else if node isa AClassdef then
                        # Automatic free init is always inlined since it is empty or contains only attribtes assigments
                        return true
+               else if node == null then
+                       return true
                else
                        abort
                end
@@ -1868,6 +2552,16 @@ redef class MMethodDef
                var modelbuilder = v.compiler.modelbuilder
                var val = constant_value
                var node = modelbuilder.mpropdef2node(self)
+
+               if is_abstract then
+                       v.add_raw_throw
+                       var cn = v.class_name_string(arguments.first)
+                       v.current_node = node
+                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
+                       v.add_raw_abort
+                       return null
+               end
+
                if node isa APropdef then
                        var oldnode = v.current_node
                        v.current_node = node
@@ -1893,20 +2587,23 @@ redef class MMethodDef
        do
                if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
 
+               var msignature = self.msignature.as(not null)
+
                for i in [0..msignature.arity[ do
+                       var mp = msignature.mparameters[i]
                        # skip test for vararg since the array is instantiated with the correct polymorphic type
-                       if msignature.vararg_rank == i then continue
+                       if mp.is_vararg 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
+                       var mtype = mp.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} */")
+                       v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
                        v.add_cast(arguments[i+1], mtype, "covariance")
                end
        end
@@ -1927,13 +2624,6 @@ end
 redef class AMethPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               if mpropdef.is_abstract then
-                       var cn = v.class_name_string(arguments.first)
-                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
-                       v.add_raw_abort
-                       return
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
@@ -1955,7 +2645,8 @@ redef class AMethPropdef
                # Try special compilation
                if mpropdef.is_intern then
                        if compile_intern_to_c(v, mpropdef, arguments) then return
-               else if mpropdef.is_extern then
+               end
+               if mpropdef.is_extern then
                        if mpropdef.mproperty.is_init then
                                if compile_externinit_to_c(v, mpropdef, arguments) then return
                        else
@@ -1975,6 +2666,7 @@ redef class AMethPropdef
                end
 
                # We have a problem
+               v.add_raw_throw
                var cn = v.class_name_string(arguments.first)
                v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
                v.add_raw_abort
@@ -1995,19 +2687,524 @@ redef class AMethPropdef
                var pname = mpropdef.mproperty.name
                var cname = mpropdef.mclassdef.mclass.name
                var ret = mpropdef.msignature.return_mtype
-               if ret != null then
+               var compiler = v.compiler
+
+               # WARNING: we must not resolve the return type when it's a functional type.
+               # Otherwise, we get a compile error exactly here. Moreover, `routine_ref_call`
+               # already handles the return type. NOTE: this warning only apply when compiling
+               # in `semi-global`.
+               if ret != null and not compiler.all_routine_types_name.has(cname) then
                        ret = v.resolve_for(ret, arguments.first)
                end
+
                if pname != "==" and pname != "!=" then
                        v.adapt_signature(mpropdef, arguments)
                        v.unbox_signature_extern(mpropdef, arguments)
                end
                if cname == "Int" then
                        if pname == "output" then
-                               v.add("printf(\"%ld\\n\", {arguments.first});")
+                               v.add("printf(\"%ld\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(arguments.first)
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "code_point" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Char" then
+                       if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "successor" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "predecessor" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
+                               return true
+                       else if pname == "code_point" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Byte" then
+                       if pname == "output" then
+                               v.add("printf(\"%x\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Bool" then
+                       if pname == "output" then
+                               v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Float" then
+                       if pname == "output" then
+                               v.add("printf(\"%f\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "succ" then
+                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
+                               return true
+                       else if pname == "prec" then
+                               v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "CString" then
+                       if pname == "[]" then
+                               v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
+                               return true
+                       else if pname == "[]=" then
+                               v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
+                               return true
+                       else if pname == "copy_to" then
+                               v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
+                               return true
+                       else if pname == "atoi" then
+                               v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
+                               return true
+                       else if pname == "fast_cstring" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "new" then
+                               var alloc = v.nit_alloc(arguments[1].to_s, "CString")
+                               v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
+                               return true
+                       else if pname == "fetch_4_chars" then
+                               v.ret(v.new_expr("*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
+                               return true
+                       else if pname == "fetch_4_hchars" then
+                               v.ret(v.new_expr("(uint32_t)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "NativeArray" then
+                       return v.native_array_def(pname, ret, arguments)
+               else if cname == "Int8" then
+                       if pname == "output" then
+                               v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "^" then
+                               v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary ~" then
+                               v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Int16" then
+                       if pname == "output" then
+                               v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "^" then
+                               v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary ~" then
+                               v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "UInt16" then
+                       if pname == "output" then
+                               v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});")
                                return true
                        else if pname == "object_id" then
-                               v.ret(arguments.first)
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                                return true
                        else if pname == "+" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
@@ -2018,6 +3215,9 @@ redef class AMethPropdef
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
                        else if pname == "*" then
                                v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
                                return true
@@ -2027,10 +3227,10 @@ redef class AMethPropdef
                        else if pname == "%" then
                                v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
                                return true
-                       else if pname == "lshift" then
+                       else if pname == "<<" then
                                v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
                                return true
-                       else if pname == "rshift" then
+                       else if pname == ">>" then
                                v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
                                return true
                        else if pname == "==" then
@@ -2052,26 +3252,74 @@ redef class AMethPropdef
                        else if pname == ">=" then
                                v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
                                return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
                        else if pname == "to_f" then
                                v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "ascii" then
-                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "^" then
+                               v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary ~" then
+                               v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
                                return true
                        end
-               else if cname == "Char" then
+               else if cname == "Int32" then
                        if pname == "output" then
-                               v.add("printf(\"%c\", {arguments.first});")
+                               v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});")
                                return true
                        else if pname == "object_id" then
                                v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                                return true
-                       else if pname == "successor" then
+                       else if pname == "+" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
                                return true
-                       else if pname == "predecessor" then
+                       else if pname == "-" then
                                v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
                                return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
                                return true
@@ -2092,33 +3340,45 @@ redef class AMethPropdef
                                v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
                                return true
                        else if pname == "to_i" then
-                               v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "ascii" then
+                       else if pname == "to_b" then
                                v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
                                return true
-                       end
-               else if cname == "Bool" then
-                       if pname == "output" then
-                               v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "object_id" then
-                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "==" then
-                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "!=" then
-                               var res = v.equal_test(arguments[0], arguments[1])
-                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                       else if pname == "to_u32" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "^" then
+                               v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary ~" then
+                               v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
                                return true
                        end
-               else if cname == "Float" then
+               else if cname == "UInt32" then
                        if pname == "output" then
-                               v.add("printf(\"%f\\n\", {arguments.first});")
+                               v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});")
                                return true
                        else if pname == "object_id" then
-                               v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                                return true
                        else if pname == "+" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
@@ -2129,11 +3389,8 @@ redef class AMethPropdef
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "succ" then
-                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return true
-                       else if pname == "prec" then
-                               v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
                                return true
                        else if pname == "*" then
                                v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
@@ -2141,6 +3398,15 @@ redef class AMethPropdef
                        else if pname == "/" then
                                v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
                                return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
                                return true
@@ -2163,40 +3429,47 @@ redef class AMethPropdef
                        else if pname == "to_i" then
                                v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
                                return true
-                       end
-               else if cname == "NativeString" then
-                       if pname == "[]" then
-                               v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "[]=" then
-                               v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
+                       else if pname == "to_i8" then
+                               v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "copy_to" then
-                               v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
+                       else if pname == "to_i16" then
+                               v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "atoi" then
-                               v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
+                       else if pname == "to_u16" then
+                               v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null)))
                                return true
-                       else if pname == "new" then
-                               v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+                       else if pname == "to_i32" then
+                               v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "^" then
+                               v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary ~" then
+                               v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
                                return true
                        end
-               else if cname == "NativeArray" then
-                       v.native_array_def(pname, ret, arguments)
+               else if compiler.all_routine_types_name.has(cname) then
+                       v.routine_ref_call(mpropdef, arguments)
                        return true
                end
                if pname == "exit" then
-                       v.add("exit({arguments[1]});")
+                       v.add("exit((int){arguments[1]});")
                        return true
                else if pname == "sys" then
                        v.ret(v.new_expr("glob_sys", ret.as(not null)))
                        return true
-               else if pname == "calloc_string" then
-                       v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
-                       return true
-               else if pname == "calloc_array" then
-                       v.calloc_array(ret.as(not null), arguments)
-                       return true
                else if pname == "object_id" then
                        v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                        return true
@@ -2233,16 +3506,13 @@ redef class AMethPropdef
        do
                var externname
                var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
-               if at != null then
+               if at != null and at.n_args.length == 1 then
                        externname = at.arg_as_string(v.compiler.modelbuilder)
                        if externname == null then return false
                else
                        return false
                end
-               if location.file != null then
-                       var file = location.file.filename
-                       v.add_extern(file)
-               end
+               v.add_extern(mpropdef.mclassdef.mmodule)
                var res: nullable RuntimeVariable = null
                var ret = mpropdef.msignature.return_mtype
                if ret != null then
@@ -2274,10 +3544,7 @@ redef class AMethPropdef
                else
                        return false
                end
-               if location.file != null then
-                       var file = location.file.filename
-                       v.add_extern(file)
-               end
+               v.add_extern(mpropdef.mclassdef.mmodule)
                v.adapt_signature(mpropdef, arguments)
                v.unbox_signature_extern(mpropdef, arguments)
                var ret = arguments.first.mtype
@@ -2293,6 +3560,8 @@ redef class AMethPropdef
 end
 
 redef class AAttrPropdef
+       redef fun can_inline: Bool do return not is_lazy
+
        redef fun compile_to_c(v, mpropdef, arguments)
        do
                if mpropdef == mreadpropdef then
@@ -2301,8 +3570,8 @@ redef class AAttrPropdef
                        var res
                        if is_lazy then
                                var set
-                               var ret = self.mpropdef.static_mtype
-                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var ret = self.mtype
+                               var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                var guard = self.mlazypropdef.mproperty
                                if useiset then
                                        set = v.isset_attribute(self.mpropdef.mproperty, recv)
@@ -2317,7 +3586,7 @@ redef class AAttrPropdef
 
                                v.assign(res, value)
                                if not useiset then
-                                       var true_v = v.new_expr("1", v.bool_type)
+                                       var true_v = v.bool_instance(true)
                                        v.write_attribute(guard, arguments.first, true_v)
                                end
                                v.add("\}")
@@ -2327,12 +3596,23 @@ redef class AAttrPropdef
                        v.assign(v.frame.returnvar.as(not null), res)
                else if mpropdef == mwritepropdef then
                        assert arguments.length == 2
-                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+                       var recv = arguments.first
+                       var arg = arguments[1]
+                       if is_optional and v.maybe_null(arg) then
+                               var value = v.new_var(self.mpropdef.static_mtype.as(not null))
+                               v.add("if ({arg} == NULL) \{")
+                               v.assign(value, evaluate_expr(v, recv))
+                               v.add("\} else \{")
+                               v.assign(value, arg)
+                               v.add("\}")
+                               arg = value
+                       end
+                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
                        if is_lazy then
-                               var ret = self.mpropdef.static_mtype
-                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var ret = self.mtype
+                               var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                if not useiset then
-                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
+                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
                                end
                        end
                else
@@ -2342,7 +3622,7 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
-               if has_value and not is_lazy then evaluate_expr(v, recv)
+               if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
        end
 
        # Evaluate, store and return the default value of the attribute
@@ -2351,11 +3631,11 @@ redef class AAttrPropdef
                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.mcasttype.as(MClassType), [recv])
+               var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
                v.frame = frame
 
                var value
-               var mtype = self.mpropdef.static_mtype
+               var mtype = self.mtype
                assert mtype != null
 
                var nexpr = self.n_expr
@@ -2390,7 +3670,7 @@ redef class AAttrPropdef
                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])
+               var frame = new StaticFrame(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)
@@ -2402,13 +3682,37 @@ end
 redef class AClassdef
        private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
-               if mpropdef == self.mfree_init then
-                       assert mpropdef.mproperty.is_root_init
+               if mpropdef.mproperty.is_root_init then
                        assert arguments.length == 1
                        if not mpropdef.is_intro then
                                v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
                        end
                        return
+               else if mclassdef.default_init == mpropdef then
+                       var recv = arguments.first
+                       var initializers = mpropdef.initializers
+                       var no_init = false
+                       if not initializers.is_empty and not mpropdef.is_old_style_init then
+                               var i = 1
+                               for p in initializers do
+                                       if p isa MMethod then
+                                               var args = [recv]
+                                               for x in p.intro.msignature.mparameters do
+                                                       args.add arguments[i]
+                                                       i += 1
+                                               end
+                                               v.send(p, args)
+                                               if p.intro.is_calling_init then no_init = true
+                                       else if p isa MAttribute then
+                                               v.write_attribute(p, recv, arguments[i])
+                                               i += 1
+                                       else abort
+                               end
+                               assert i == arguments.length
+
+                       end
+                       if not no_init then v.send(mclass.the_root_init_mmethod.as(not null), [recv])
+                       return
                else
                        abort
                end
@@ -2502,6 +3806,13 @@ redef class ASelfExpr
        redef fun expr(v) do return v.frame.arguments.first
 end
 
+redef class AImplicitSelfExpr
+       redef fun expr(v) do
+               if not is_sys then return super
+               return v.new_expr("glob_sys", mtype.as(not null))
+       end
+end
+
 redef class AEscapeExpr
        redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
 end
@@ -2564,8 +3875,30 @@ end
 redef class ADoExpr
        redef fun stmt(v)
        do
-               v.stmt(self.n_block)
-               v.add_escape_label(break_mark)
+               if self.n_catch != null then
+                       v.add("\{")
+                       v.add("struct catch_stack_t *catchStack = getCatchStack();")
+                       v.add("if(catchStack->currentSize == 0) \{")
+                       v.add("         catchStack->cursor = -1;")
+                       v.add("         catchStack->currentSize = 100;")
+                       v.add("         catchStack->envs = malloc(sizeof(jmp_buf)*100);")
+                       v.add("\} else if(catchStack->cursor == catchStack->currentSize - 1) \{")
+                       v.add("         catchStack->currentSize *= 2;")
+                       v.add("         catchStack->envs = realloc(catchStack->envs, sizeof(jmp_buf)*catchStack->currentSize);")
+                       v.add("\}")
+                       v.add("catchStack->cursor += 1;")
+                       v.add("if(!setjmp(catchStack->envs[catchStack->cursor]))\{")
+                       v.stmt(self.n_block)
+                       v.add("catchStack->cursor -= 1;")
+                       v.add("\}else \{")
+                       v.add("catchStack->cursor -= 1;")
+                       v.stmt(self.n_catch)
+                       v.add("\}")
+                       v.add("\}")
+               else
+                       v.stmt(self.n_block)
+               end
+                       v.add_escape_label(break_mark)
        end
 end
 
@@ -2596,53 +3929,68 @@ end
 redef class AForExpr
        redef fun stmt(v)
        do
-               var cl = v.expr(self.n_expr, null)
-               var it_meth = self.method_iterator
-               assert it_meth != null
-               var it = v.compile_callsite(it_meth, [cl])
-               assert it != null
+               for g in n_groups do
+                       var cl = v.expr(g.n_expr, null)
+                       var it_meth = g.method_iterator
+                       assert it_meth != null
+                       var it = v.compile_callsite(it_meth, [cl])
+                       assert it != null
+                       g.it = it
+               end
                v.add("for(;;) \{")
-               var isok_meth = self.method_is_ok
-               assert isok_meth != null
-               var ok = v.compile_callsite(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.compile_callsite(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.compile_callsite(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.compile_callsite(item_meth, [it])
-                       assert i != null
-                       v.assign(v.variable(variables[1]), i)
-               else
-                       abort
+               for g in n_groups do
+                       var it = g.it
+                       var isok_meth = g.method_is_ok
+                       assert isok_meth != null
+                       var ok = v.compile_callsite(isok_meth, [it])
+                       assert ok != null
+                       v.add("if(!{ok}) break;")
+                       if g.variables.length == 1 then
+                               var item_meth = g.method_item
+                               assert item_meth != null
+                               var i = v.compile_callsite(item_meth, [it])
+                               assert i != null
+                               v.assign(v.variable(g.variables.first), i)
+                       else if g.variables.length == 2 then
+                               var key_meth = g.method_key
+                               assert key_meth != null
+                               var i = v.compile_callsite(key_meth, [it])
+                               assert i != null
+                               v.assign(v.variable(g.variables[0]), i)
+                               var item_meth = g.method_item
+                               assert item_meth != null
+                               i = v.compile_callsite(item_meth, [it])
+                               assert i != null
+                               v.assign(v.variable(g.variables[1]), i)
+                       else
+                               abort
+                       end
                end
                v.stmt(self.n_block)
                v.add_escape_label(continue_mark)
-               var next_meth = self.method_next
-               assert next_meth != null
-               v.compile_callsite(next_meth, [it])
+               for g in n_groups do
+                       var next_meth = g.method_next
+                       assert next_meth != null
+                       v.compile_callsite(next_meth, [g.it])
+               end
                v.add("\}")
                v.add_escape_label(break_mark)
 
-               var method_finish = self.method_finish
-               if method_finish != null then
-                       # TODO: Find a way to call this also in long escape (e.g. return)
-                       v.compile_callsite(method_finish, [it])
+               for g in n_groups do
+                       var method_finish = g.method_finish
+                       if method_finish != null then
+                               # TODO: Find a way to call this also in long escape (e.g. return)
+                               v.compile_callsite(method_finish, [g.it])
+                       end
                end
        end
 end
 
+redef class AForGroup
+       # C variable representing the iterator
+       private var it: RuntimeVariable is noinit
+end
+
 redef class AAssertExpr
        redef fun stmt(v)
        do
@@ -2651,6 +3999,9 @@ redef class AAssertExpr
                var cond = v.expr_bool(self.n_expr)
                v.add("if (unlikely(!{cond})) \{")
                v.stmt(self.n_else)
+
+               explain_assert v
+
                var nid = self.n_id
                if nid != null then
                        v.add_abort("Assert '{nid.text}' failed")
@@ -2659,6 +4010,27 @@ redef class AAssertExpr
                end
                v.add("\}")
        end
+
+       # Explain assert if it fails
+       private fun explain_assert(v: AbstractCompilerVisitor)
+       do
+               var explain_assert_str = explain_assert_str
+               if explain_assert_str == null then return
+
+               var nas = v.compiler.modelbuilder.model.get_mclasses_by_name("NativeArray")
+               if nas == null then return
+
+               nas = v.compiler.modelbuilder.model.get_mclasses_by_name("Array")
+               if nas == null or nas.is_empty then return
+
+               var expr = explain_assert_str.expr(v)
+               if expr == null then return
+
+               var cstr = v.send(v.get_property("to_cstring", expr.mtype), [expr])
+               if cstr == null then return
+
+               v.add "PRINT_ERROR(\"Runtime assert: %s\\n\", {cstr});"
+       end
 end
 
 redef class AOrExpr
@@ -2719,6 +4091,9 @@ redef class AOrElseExpr
        do
                var res = v.new_var(self.mtype.as(not null))
                var i1 = v.expr(self.n_expr, null)
+
+               if not v.maybe_null(i1) then return i1
+
                v.add("if ({i1}!=NULL) \{")
                v.assign(res, i1)
                v.add("\} else \{")
@@ -2729,46 +4104,181 @@ redef class AOrElseExpr
        end
 end
 
-redef class AIntExpr
-       redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
+redef class AIntegerExpr
+       redef fun expr(v) do
+               if value isa Int then return v.int_instance(value.as(Int))
+               if value isa Byte then return v.byte_instance(value.as(Byte))
+               if value isa Int8 then return v.int8_instance(value.as(Int8))
+               if value isa Int16 then return v.int16_instance(value.as(Int16))
+               if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
+               if value isa Int32 then return v.int32_instance(value.as(Int32))
+               if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
+               # Should never happen
+               abort
+       end
 end
 
 redef class AFloatExpr
-       redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
+       redef fun expr(v) do return v.float_instance(self.value.as(Float))
 end
 
 redef class ACharExpr
-       redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
+       redef fun expr(v) do
+               if is_code_point then
+                       return v.int_instance(value.as(not null).code_point)
+               end
+               return v.char_instance(self.value.as(not null))
+       end
 end
 
 redef class AArrayExpr
        redef fun expr(v)
        do
-               var mtype = self.mtype.as(MClassType).arguments.first
+               var mtype = self.element_mtype.as(not null)
                var array = new Array[RuntimeVariable]
-               for nexpr in self.n_exprs.n_exprs do
-                       var i = v.expr(nexpr, mtype)
-                       array.add(i)
+               var res = v.array_instance(array, mtype)
+
+               var old_comprehension = v.frame.comprehension
+               v.frame.comprehension = res
+               for nexpr in self.n_exprs do
+                       v.stmt(nexpr)
+               end
+               v.frame.comprehension = old_comprehension
+
+               return res
+       end
+end
+
+redef class AugmentedStringFormExpr
+       # Factorize the making of a `Regex` object from a literal prefixed string
+       protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
+               var re = to_re
+               assert re != null
+               var res = v.compile_callsite(re, [rs])
+               if res == null then
+                       print "Cannot call property `to_re` on {self}"
+                       abort
+               end
+               for i in suffix.chars do
+                       if i == 'i' then
+                               var ign = ignore_case
+                               assert ign != null
+                               v.compile_callsite(ign, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if i == 'm' then
+                               var nl = newline
+                               assert nl != null
+                               v.compile_callsite(nl, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if i == 'b' then
+                               var ext = extended
+                               assert ext != null
+                               v.compile_callsite(ext, [res, v.bool_instance(false)])
+                               continue
+                       end
+                       # Should not happen, this needs to be updated
+                       # along with the addition of new suffixes
+                       abort
                end
-               return v.array_instance(array, mtype)
+               return res
        end
 end
 
 redef class AStringFormExpr
-       redef fun expr(v) do return v.string_instance(self.value.as(not null))
+       redef fun expr(v) do return v.string_instance(value)
+end
+
+redef class AStringExpr
+       redef fun expr(v) do
+               var s = v.string_instance(value)
+               if is_string then return s
+               if is_bytestring then
+                       var ns = v.c_string_instance(bytes.items, bytes.length)
+                       var ln = v.int_instance(bytes.length)
+                       var cs = to_bytes_with_copy
+                       assert cs != null
+                       var res = v.compile_callsite(cs, [ns, ln])
+                       assert res != null
+                       s = res
+               else if is_re then
+                       var res = make_re(v, s)
+                       assert res != null
+                       s = res
+               else
+                       print "Unimplemented prefix or suffix for {self}"
+                       abort
+               end
+               return s
+       end
 end
 
 redef class ASuperstringExpr
        redef fun expr(v)
        do
-               var array = new Array[RuntimeVariable]
+               var type_string = v.mmodule.string_type
+
+               # Collect elements of the superstring
+               var array = new Array[AExpr]
                for ne in self.n_exprs do
+                       # Drop literal empty string.
+                       # They appears in things like "{a}" that is ["", a, ""]
                        if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
-                       var i = v.expr(ne, null)
-                       array.add(i)
+                       array.add(ne)
+               end
+
+               # Store the allocated native array in a static variable
+               # For reusing later
+               var varonce = v.get_name("varonce")
+               v.add("if (unlikely({varonce}==NULL)) \{")
+
+               # The native array that will contains the elements to_s-ized.
+               # For fast concatenation.
+               var a = v.native_array_instance(type_string, v.int_instance(array.length))
+
+               v.add_decl("static {a.mtype.ctype} {varonce};")
+
+               # Pre-fill the array with the literal string parts.
+               # So they do not need to be filled again when reused
+               for i in [0..array.length[ do
+                       var ne = array[i]
+                       if not ne isa AStringFormExpr then continue
+                       var e = v.expr(ne, null)
+                       v.native_array_set(a, i, e)
+               end
+
+               v.add("\} else \{")
+               # Take the native-array from the store.
+               # The point is to prevent that some recursive execution use (and corrupt) the same native array
+               # WARNING: not thread safe! (FIXME?)
+               v.add("{a} = {varonce};")
+               v.add("{varonce} = NULL;")
+               v.add("\}")
+
+               # Stringify the elements and put them in the native array
+               var to_s_method = v.get_property("to_s", v.object_type)
+               for i in [0..array.length[ do
+                       var ne = array[i]
+                       if ne isa AStringFormExpr then continue
+                       var e = v.expr(ne, null)
+                       # Skip the `to_s` if the element is already a String
+                       if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
+                               e = v.send(to_s_method, [e]).as(not null)
+                       end
+                       v.native_array_set(a, i, e)
                end
-               var a = v.array_instance(array, v.object_type)
-               var res = v.send(v.get_property("to_s", a.mtype), [a])
+
+               # Fast join the C string to get the result
+               var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+               assert res != null
+
+               if is_re then res = make_re(v, res)
+
+               # We finish to work with the native array,
+               # so store it so that it can be reused
+               v.add("{varonce} = {a};")
+
                return res
        end
 end
@@ -2798,22 +4308,24 @@ redef class AOrangeExpr
 end
 
 redef class ATrueExpr
-       redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
+       redef fun expr(v) do return v.bool_instance(true)
 end
 
 redef class AFalseExpr
-       redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
+       redef fun expr(v) do return v.bool_instance(false)
 end
 
 redef class ANullExpr
-       redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
+       redef fun expr(v) do return v.null_instance
 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")
+               var cast_type = self.cast_type
+               if cast_type == null then return null # no-no on broken node
+               return v.type_test(i, cast_type, "isa")
        end
 end
 
@@ -2834,7 +4346,7 @@ redef class AAsNotnullExpr
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
 
-               if i.mtype.ctype != "val*" then return i
+               if not v.maybe_null(i) then return i
 
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
@@ -2856,7 +4368,7 @@ redef class AOnceExpr
                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("if (likely({guard})) \{")
                v.add("{res} = {name};")
                v.add("\} else \{")
                var i = v.expr(self.n_expr, mtype)
@@ -2872,9 +4384,49 @@ redef class ASendExpr
        redef fun expr(v)
        do
                var recv = v.expr(self.n_expr, null)
+               if is_safe then
+                       v.add "if ({recv}!=NULL) \{"
+               end
                var callsite = self.callsite.as(not null)
-               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
-               return v.compile_callsite(callsite, args)
+               if callsite.is_broken then return null
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
+               var res = v.compile_callsite(callsite, args)
+               if is_safe then
+                       if res != null then
+                               # `callsite.mpropdef` may reference a method whose
+                               # return type is a primitive type in C. If it is
+                               # the case, we must convert the primitive type to
+                               # a `val*` to support nullable assignment.
+                               # Autobox's job is to convert primitive type to
+                               # nullable type, eg from `Int` to `nullable `Int`.
+                               # The target type reside in `self.mtype`.
+                               var original_res = v.autobox(res, self.mtype.as(not null))
+
+                               # Here we must create a new_var in case the original
+                               # type is not nullable. We can't call `autobox` to
+                               # convert a complex type to its nullable version.
+                               # eg if we have a type `A`, calling autobox like
+                               # `autobox(A, nullable A)` will return `A` since
+                               # `A` and `nullable A` have the same primitive
+                               # type. The nullable qualifier is only used at
+                               # compile time to add appropriate null checks.
+                               res = v.new_var(self.mtype.as(not null))
+                               v.add("{res} = {original_res};")
+                               v.add("\} else \{")
+                               v.add("{res} = NULL;")
+                       end
+                       v.add("\}")
+               end
+               return res
+       end
+end
+
+redef class ACallrefExpr
+       redef fun expr(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var res = v.routine_ref_instance(mtype.as(MClassType), recv, callsite.as(not null))
+               return res
        end
 end
 
@@ -2883,7 +4435,8 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr, null)
                var callsite = self.callsite.as(not null)
-               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               if callsite.is_broken then return
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
 
                var value = v.expr(self.n_value, null)
 
@@ -2901,30 +4454,39 @@ end
 redef class ASuperExpr
        redef fun expr(v)
        do
-               var recv = v.frame.arguments.first
+               var frame = v.frame.as(not null)
+               var recv = frame.arguments.first
 
                var callsite = self.callsite
                if callsite != null then
-                       var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+                       if callsite.is_broken then return null
+                       var args
 
-                       # Add additional arguments for the super init call
-                       if args.length == 1 then
+                       if self.n_args.n_exprs.is_empty then
+                               # Add automatic arguments for the super init call
+                               args = [recv]
                                for i in [0..callsite.msignature.arity[ do
-                                       args.add(v.frame.arguments[i+1])
+                                       args.add(frame.arguments[i+1])
                                end
+                       else
+                               args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
                        end
+
                        # Super init call
                        var res = v.compile_callsite(callsite, args)
                        return res
                end
 
                var mpropdef = self.mpropdef.as(not null)
-               var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
-               if args.length == 1 then
-                       args = v.frame.arguments
+
+               var args
+               if self.n_args.n_exprs.is_empty then
+                       args = frame.arguments
+               else
+                       args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
                end
 
-               # stantard call-next-method
+               # Standard call-next-method
                return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
        end
 end
@@ -2934,24 +4496,46 @@ redef class ANewExpr
        do
                var mtype = self.recvtype
                assert mtype != null
-               var recv
-               var ctype = mtype.ctype
+
                if mtype.mclass.name == "NativeArray" then
                        assert self.n_args.n_exprs.length == 1
                        var l = v.expr(self.n_args.n_exprs.first, null)
                        assert mtype isa MGenericType
                        var elttype = mtype.arguments.first
                        return v.native_array_instance(elttype, l)
-               else if ctype == "val*" then
-                       recv = v.init_instance(mtype)
-               else if ctype == "char*" then
-                       recv = v.new_expr("NULL/*special!*/", mtype)
+               end
+
+               var callsite = self.callsite
+               if callsite == null then return v.init_instance_or_extern(mtype)
+               if callsite.is_broken then return null
+
+               var recv
+               # new factories are badly implemented.
+               # They assume a stub temporary receiver exists.
+               # This temporary receiver is required because it
+               # currently holds the method and the formal types.
+               #
+               # However, this object could be reused if the formal types are the same.
+               # Therefore, the following code will `once` it in these case
+               if callsite.mproperty.is_new and not mtype.need_anchor then
+                       var name = v.get_name("varoncenew")
+                       var guard = v.get_name(name + "_guard")
+                       v.add_decl("static {mtype.ctype} {name};")
+                       v.add_decl("static int {guard};")
+                       recv = v.new_var(mtype)
+                       v.add("if (likely({guard})) \{")
+                       v.add("{recv} = {name};")
+                       v.add("\} else \{")
+                       var i = v.init_instance_or_extern(mtype)
+                       v.add("{recv} = {i};")
+                       v.add("{name} = {recv};")
+                       v.add("{guard} = 1;")
+                       v.add("\}")
                else
-                       recv = v.new_expr("({ctype})0/*special!*/", mtype)
+                       recv = v.init_instance_or_extern(mtype)
                end
 
-               var callsite = self.callsite.as(not null)
-               var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
                var res2 = v.compile_callsite(callsite, args)
                if res2 != null then
                        #self.debug("got {res2} from {mproperty}. drop {recv}")
@@ -3003,6 +4587,27 @@ redef class AIssetAttrExpr
        end
 end
 
+redef class AVarargExpr
+       redef fun expr(v)
+       do
+               return v.expr(self.n_expr, null)
+       end
+end
+
+redef class ASafeExpr
+       redef fun expr(v)
+       do
+               return v.expr(self.n_expr, null)
+       end
+end
+
+redef class ANamedargExpr
+       redef fun expr(v)
+       do
+               return v.expr(self.n_expr, null)
+       end
+end
+
 redef class ADebugTypeExpr
        redef fun stmt(v)
        do
@@ -3022,19 +4627,6 @@ redef class Array[E]
 end
 
 redef class MModule
-       # Return the name of the global C identifier associated to `self`.
-       # This name is used to prefix files and other C identifiers associated with `self`.
-       var c_name: String is lazy do
-               var g = mgroup
-               var res
-               if g != null and g.mproject.name != name then
-                       res = g.mproject.name.to_cmangle + "__" + name.to_cmangle
-               else
-                       res = name.to_cmangle
-               end
-               return res
-       end
-
        # All `MProperty` associated to all `MClassDef` of `mclass`
        fun properties(mclass: MClass): Set[MProperty] do
                if not self.properties_cache.has_key(mclass) then
@@ -3063,7 +4655,7 @@ redef class MModule
 
        # Give requided addinional system libraries (as given to LD_LIBS)
        # Note: can return null instead of an empty set
-       fun collect_linker_libs: nullable Set[String] do return null
+       fun collect_linker_libs: nullable Array[String] do return null
 end
 
 # Create a tool context to handle options and paths
@@ -3080,15 +4672,20 @@ var model = new Model
 var modelbuilder = new ModelBuilder(model, toolcontext)
 
 var arguments = toolcontext.option_context.rest
+if toolcontext.opt_run.value then
+       # When --run, only the first is the program, the rest is the run arguments
+       arguments = [toolcontext.option_context.rest.shift]
+end
 if arguments.length > 1 and toolcontext.opt_output.value != null then
-       print "Error: --output needs a single source file. Do you prefer --dir?"
+       print "Option Error: --output needs a single source file. Do you prefer --dir?"
        exit 1
 end
 
 # Here we load an process all modules passed on the command line
 var mmodules = modelbuilder.parse(arguments)
 
-if mmodules.is_empty then return
+if mmodules.is_empty then toolcontext.quit
+
 modelbuilder.run_phases
 
 for mmodule in mmodules do