src: update most tools to new constructors
[nit.git] / src / compiler / abstract_compiler.nit
index 52bc599..62c0987 100644 (file)
@@ -21,47 +21,51 @@ import literal
 import semantize
 import platform
 import c_tools
+private import annotation
+import mixin
 
 # Add compiling options
 redef class ToolContext
        # --output
-       var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
+       var opt_output = new OptionString("Output file", "-o", "--output")
        # --dir
-       var opt_dir: OptionString = new OptionString("Output directory", "--dir")
+       var opt_dir = new OptionString("Output directory", "--dir")
        # --no-cc
-       var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
+       var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
        # --no-main
-       var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main")
+       var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
        # --cc-paths
-       var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
+       var opt_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
-       var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
+       var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
+       # --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
+       var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files")
        # --compile-dir
-       var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir")
+       var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
        # --hardening
-       var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
-       # --no-shortcut-range
-       var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
+       var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
        # --no-check-covariance
-       var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
+       var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
        # --no-check-attr-isset
-       var opt_no_check_attr_isset: OptionBool = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
+       var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
        # --no-check-assert
-       var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
+       var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
        # --no-check-autocast
-       var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
+       var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
        # --no-check-null
-       var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
+       var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
        # --no-check-all
-       var opt_no_check_all: OptionBool = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
+       var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
        # --typing-test-metrics
-       var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
+       var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
        # --invocation-metrics
-       var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
+       var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
        # --isset-checks-metrics
-       var opt_isset_checks_metrics: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
+       var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
        # --stacktrace
-       var opt_stacktrace: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace")
+       var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
        # --no-gcc-directives
        var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
        # --release
@@ -70,12 +74,13 @@ redef class ToolContext
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
+               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
                self.option_context.add_option(self.opt_stacktrace)
                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)
        end
 
        redef fun process_options(args)
@@ -154,17 +159,20 @@ class MakefileToolchain
        # Path can be added (or removed) by the client
        var cc_paths = new Array[String]
 
+       # The clib directory of Nit
+       # Used to found some common runtime
+       var clib: String is noinit
+
        protected fun gather_cc_paths
        do
                # Look for the the Nit clib path
                var path_env = toolcontext.nit_dir
                if path_env != null then
                        var libname = "{path_env}/clib"
-                       if libname.file_exists then cc_paths.add(libname)
-               end
-
-               if cc_paths.is_empty then
-                       toolcontext.error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.")
+                       if not libname.file_exists then
+                               toolcontext.fatal_error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.")
+                       end
+                       clib = libname
                end
 
                # Add user defined cc_paths
@@ -180,7 +188,6 @@ class MakefileToolchain
        do
                gather_cc_paths
 
-               var mainmodule = compiler.mainmodule
                var compile_dir = compile_dir
 
                # Generate the .h and .c files
@@ -224,11 +231,10 @@ class MakefileToolchain
                # Add gc_choser.h to aditionnal bodies
                var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
                compiler.extern_bodies.add(gc_chooser)
-               compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.c"
-               compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h"
+               compiler.files_to_copy.add "{clib}/gc_chooser.c"
+               compiler.files_to_copy.add "{clib}/gc_chooser.h"
 
                # FFI
-               var m2m = toolcontext.modelbuilder.mmodule2nmodule
                for m in compiler.mainmodule.in_importation.greaters do
                        compiler.finalize_ffi_for_module(m)
                end
@@ -253,39 +259,21 @@ class MakefileToolchain
                end
                h.close
 
+               var max_c_lines = toolcontext.opt_max_c_lines.value
                for f in compiler.files do
                        var i = 0
-                       var hfile: nullable OFStream = null
                        var count = 0
-                       var cfilename = "{f.name}.0.h"
-                       var cfilepath = "{compile_dir}/{cfilename}"
-                       hfile = new OFStream.open(cfilepath)
-                       hfile.write "#include \"{hfilename}\"\n"
-                       for key in f.required_declarations do
-                               if not compiler.provided_declarations.has_key(key) then
-                                       var node = compiler.requirers_of_declarations.get_or_null(key)
-                                       if node != null then
-                                               node.debug "No provided declaration for {key}"
-                                       else
-                                               print "No provided declaration for {key}"
-                                       end
-                                       abort
-                               end
-                               hfile.write compiler.provided_declarations[key]
-                               hfile.write "\n"
-                       end
-                       hfile.close
                        var file: nullable OFStream = null
                        for vis in f.writers do
                                if vis == compiler.header then continue
                                var total_lines = vis.lines.length + vis.decl_lines.length
                                if total_lines == 0 then continue
                                count += total_lines
-                               if file == null or count > 10000  then
+                               if file == null or (count > max_c_lines and max_c_lines > 0) then
                                        i += 1
                                        if file != null then file.close
-                                       cfilename = "{f.name}.{i}.c"
-                                       cfilepath = "{compile_dir}/{cfilename}"
+                                       var cfilename = "{f.name}.{i}.c"
+                                       var cfilepath = "{compile_dir}/{cfilename}"
                                        self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
                                        cfiles.add(cfilename)
                                        file = new OFStream.open(cfilepath)
@@ -301,7 +289,28 @@ class MakefileToolchain
                                        file.write "\n"
                                end
                        end
-                       if file != null then file.close
+                       if file == null then continue
+                       file.close
+
+                       var cfilename = "{f.name}.0.h"
+                       var cfilepath = "{compile_dir}/{cfilename}"
+                       var hfile: nullable OFStream = null
+                       hfile = new OFStream.open(cfilepath)
+                       hfile.write "#include \"{hfilename}\"\n"
+                       for key in f.required_declarations do
+                               if not compiler.provided_declarations.has_key(key) then
+                                       var node = compiler.requirers_of_declarations.get_or_null(key)
+                                       if node != null then
+                                               node.debug "No provided declaration for {key}"
+                                       else
+                                               print "No provided declaration for {key}"
+                                       end
+                                       abort
+                               end
+                               hfile.write compiler.provided_declarations[key]
+                               hfile.write "\n"
+                       end
+                       hfile.close
                end
 
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
@@ -309,7 +318,16 @@ class MakefileToolchain
 
        fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
 
-       fun default_outname(mainmodule: MModule): String do return mainmodule.name
+       fun default_outname(mainmodule: MModule): String
+       do
+               # Search a non fictive module
+               var res = mainmodule.name
+               while mainmodule.is_fictive do
+                       mainmodule = mainmodule.in_importation.direct_greaters.first
+                       res = mainmodule.name
+               end
+               return res
+       end
 
        # Combine options and platform informations to get the final path of the outfile
        fun outfile(mainmodule: MModule): String
@@ -329,8 +347,7 @@ class MakefileToolchain
 
                var outname = outfile(mainmodule)
 
-               var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd
-               var outpath = orig_dir.join_path(outname).simplify_path
+               var outpath = compile_dir.relpath(outname)
                var makename = makefile_name(mainmodule)
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
@@ -341,7 +358,6 @@ class MakefileToolchain
                end
 
                var linker_options = new HashSet[String]
-               var m2m = toolcontext.modelbuilder.mmodule2nmodule
                for m in mainmodule.in_importation.greaters do
                        var libs = m.collect_linker_libs
                        if libs != null then linker_options.add_all(libs)
@@ -413,6 +429,8 @@ class MakefileToolchain
                makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
                makefile.close
                self.toolcontext.info("Generated makefile: {makepath}", 2)
+
+               makepath.file_copy_to "{compile_dir}/Makefile"
        end
 
        fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
@@ -444,28 +462,33 @@ abstract class AbstractCompiler
 
        # The main module of the program currently compiled
        # Is assigned during the separate compilation
-       var mainmodule: MModule writable
+       var mainmodule: MModule is writable
 
        # The real main module of the program
-       var realmainmodule: MModule
+       var realmainmodule: MModule is noinit
 
-       # The modeulbuilder used to know the model and the AST
-       var modelbuilder: ModelBuilder protected writable
+       # The modelbuilder used to know the model and the AST
+       var modelbuilder: ModelBuilder is protected writable
 
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
-       init(mainmodule: MModule, modelbuilder: ModelBuilder)
+       init
        do
-               self.mainmodule = mainmodule
                self.realmainmodule = mainmodule
-               self.modelbuilder = modelbuilder
        end
 
        # Force the creation of a new file
        # The point is to avoid contamination between must-be-compiled-separately files
        fun new_file(name: String): CodeFile
        do
+               if modelbuilder.toolcontext.opt_group_c_files.value then
+                       if self.files.is_empty then
+                               var f = new CodeFile(mainmodule.name)
+                               self.files.add(f)
+                       end
+                       return self.files.first
+               end
                var f = new CodeFile(name)
                self.files.add(f)
                return f
@@ -473,13 +496,13 @@ abstract class AbstractCompiler
 
        # The list of all associated files
        # Used to generate .c files
-       var files: List[CodeFile] = new List[CodeFile]
+       var files = new List[CodeFile]
 
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
 
        # Where global declaration are stored (the main .h)
-       var header: CodeWriter writable
+       var header: CodeWriter is writable, noinit
 
        # Provide a declaration that can be requested (before or latter) by a visitor
        fun provide_declaration(key: String, s: String)
@@ -540,8 +563,6 @@ abstract class AbstractCompiler
        # Compile C headers
        # This method call compile_header_strucs method that has to be refined
        fun compile_header do
-               var v = self.header
-               var toolctx = modelbuilder.toolcontext
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
@@ -890,6 +911,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@ -905,6 +927,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@ -1001,9 +1024,8 @@ class CodeWriter
        # (used for local or global declaration)
        fun add_decl(s: String) do self.decl_lines.add(s)
 
-       init(file: CodeFile)
+       init
        do
-               self.file = file
                file.writers.add(self)
        end
 end
@@ -1017,10 +1039,10 @@ abstract class AbstractCompilerVisitor
        var compiler: COMPILER
 
        # The current visited AST node
-       var current_node: nullable ANode writable = null
+       var current_node: nullable ANode = null is writable
 
        # The current `Frame`
-       var frame: nullable Frame writable
+       var frame: nullable Frame = null is writable
 
        # Alias for self.compiler.mainmodule.object_type
        fun object_type: MClassType do return self.compiler.mainmodule.object_type
@@ -1028,11 +1050,10 @@ abstract class AbstractCompilerVisitor
        # Alias for self.compiler.mainmodule.bool_type
        fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
 
-       var writer: CodeWriter
+       var writer: CodeWriter is noinit
 
-       init(compiler: COMPILER)
+       init
        do
-               self.compiler = compiler
                self.writer = new CodeWriter(compiler.files.last)
        end
 
@@ -1043,7 +1064,7 @@ abstract class AbstractCompilerVisitor
        fun get_property(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
-               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
+               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
        end
 
        fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
@@ -1052,16 +1073,21 @@ abstract class AbstractCompilerVisitor
                if not initializers.is_empty then
                        var recv = arguments.first
 
-                       assert initializers.length == arguments.length - 1 else debug("expected {initializers.length}, got {arguments.length - 1}")
                        var i = 1
                        for p in initializers do
                                if p isa MMethod then
-                                       self.send(p, [recv, arguments[i]])
+                                       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
-                               i += 1
                        end
+                       assert i == arguments.length
 
                        return self.send(callsite.mproperty, [recv])
                end
@@ -1075,39 +1101,45 @@ abstract class AbstractCompilerVisitor
 
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
 
-       # Transform varargs, in raw arguments, into a single argument of type `Array`
-       # Note: this method modify the given `args`
-       # If there is no vararg, then `args` is not modified.
-       fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
+       # 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]
        do
-               var recv = args.first
-               var vararg_rank = msignature.vararg_rank
-               if vararg_rank >= 0 then
-                       assert args.length >= msignature.arity + 1 # because of self
-                       var rawargs = args
-                       args = new Array[RuntimeVariable]
+               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var res = new Array[RuntimeVariable]
+               res.add(recv)
 
-                       args.add(rawargs.first) # recv
+               if args.is_empty then return res
 
-                       for i in [0..vararg_rank[ do
-                               args.add(rawargs[i+1])
-                       end
-
-                       var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
-                       var vararg = new Array[RuntimeVariable]
-                       for i in [vararg_rank..vararg_lastrank] do
-                               vararg.add(rawargs[i+1])
-                       end
-
-                       var elttype = msignature.mparameters[vararg_rank].mtype
-                       args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
+               var vararg_rank = msignature.vararg_rank
+               var vararg_len = args.length - msignature.arity
+               if vararg_len < 0 then vararg_len = 0
 
-                       for i in [vararg_lastrank+1..rawargs.length-1[ do
-                               args.add(rawargs[i+1])
+               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 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)
                        end
-                       rawargs.clear
-                       rawargs.add_all(args)
                end
+               return res
        end
 
        # Type handling
@@ -1149,12 +1181,22 @@ abstract class AbstractCompilerVisitor
        # Generate a super call from a method definition
        fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
 
+       # Adapt the arguments of a method according to targetted `MMethodDef`
        fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
 
+       # Unbox all the arguments of a method when implemented `extern` or `intern`
+       fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
+
        # Box or unbox a value to another type iff a C type conversion is needed
        # ENSURE: `result.mtype.ctype == mtype.ctype`
        fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
 
+       # Box extern classes to be used in the generated code
+       fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
+
+       # Unbox extern classes to be used in extern code (legacy NI and FFI)
+       fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
+
        #  Generate a polymorphic subtype test
        fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
 
@@ -1201,7 +1243,7 @@ abstract class AbstractCompilerVisitor
 
        # Checks
 
-       # Add a check and an abort for a null reciever if needed
+       # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
@@ -1216,7 +1258,7 @@ abstract class AbstractCompilerVisitor
 
        # Names handling
 
-       private var names: HashSet[String] = new HashSet[String]
+       private var names = new HashSet[String]
        private var last: Int = 0
 
        # Return a new name based on `s` and unique in the visitor
@@ -1250,6 +1292,14 @@ abstract class AbstractCompilerVisitor
                return name
        end
 
+       # Insert a C label for associated with an escapemark
+       fun add_escape_label(e: nullable EscapeMark)
+       do
+               if e == null then return
+               if e.escapes.is_empty then return
+               add("BREAK_{escapemark_name(e)}: (void)0;")
+       end
+
        private var escapemark_names = new HashMap[EscapeMark, String]
 
        # Return a "const char*" variable associated to the classname of the dynamic type of an object
@@ -1258,7 +1308,7 @@ abstract class AbstractCompilerVisitor
 
        # Variables handling
 
-       protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
+       protected var variables = new HashMap[Variable, RuntimeVariable]
 
        # Return the local runtime_variable associated to a Nit local variable
        fun variable(variable: Variable): RuntimeVariable
@@ -1286,6 +1336,16 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # The difference with `new_var` is the C static type of the local variable
+       fun new_var_extern(mtype: MType): RuntimeVariable
+       do
+               mtype = self.anchor(mtype)
+               var name = self.get_name("var")
+               var res = new RuntimeVariable(name, mtype, mtype)
+               self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
+               return res
+       end
+
        # Return a new uninitialized named runtime_variable
        fun new_named_var(mtype: MType, name: String): RuntimeVariable
        do
@@ -1327,6 +1387,18 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # Generate an integer value
+       fun bool_instance(value: Bool): RuntimeVariable
+       do
+               var res = self.new_var(self.get_class("Bool").mclass_type)
+               if value then
+                       self.add("{res} = 1;")
+               else
+                       self.add("{res} = 0;")
+               end
+               return res
+       end
+
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
@@ -1347,6 +1419,19 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       fun value_instance(object: Object): RuntimeVariable
+       do
+               if object isa Int then
+                       return int_instance(object)
+               else if object isa Bool then
+                       return bool_instance(object)
+               else if object isa String then
+                       return string_instance(object)
+               else
+                       abort
+               end
+       end
+
        # Generate an array value
        fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
 
@@ -1523,7 +1608,7 @@ abstract class AbstractRuntimeFunction
        # Non cached version of `c_name`
        protected fun build_c_name: String is abstract
 
-       protected var c_name_cache: nullable String writable = null
+       protected var c_name_cache: nullable String = null is writable
 
        # Implements a call of the runtime_function
        # May inline the body or generate a C function call
@@ -1546,17 +1631,14 @@ class RuntimeVariable
        var mtype: MType
 
        # The current casted type of the variable (as known in Nit)
-       var mcasttype: MType writable
+       var mcasttype: MType is writable
 
        # If the variable exaclty a mcasttype?
        # false (usual value) means that the variable is a mcasttype or a subtype.
-       var is_exact: Bool writable = false
+       var is_exact: Bool = false is writable
 
-       init(name: String, mtype: MType, mcasttype: MType)
+       init
        do
-               self.name = name
-               self.mtype = mtype
-               self.mcasttype = mcasttype
                assert not mtype.need_anchor
                assert not mcasttype.need_anchor
        end
@@ -1600,21 +1682,25 @@ class Frame
        var arguments: Array[RuntimeVariable]
 
        # The runtime_variable associated to the return (in a function)
-       var returnvar: nullable RuntimeVariable writable = null
+       var returnvar: nullable RuntimeVariable = null is writable
 
        # The label at the end of the property
-       var returnlabel: nullable String writable = null
+       var returnlabel: nullable String = null is writable
 end
 
 redef class MType
        # Return the C type associated to a given Nit static type
        fun ctype: String do return "val*"
 
+       # C type outside of the compiler code and in boxes
+       fun ctype_extern: String do return "val*"
+
+       # 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 protected writable
+       protected var c_name_cache: nullable String is protected writable
 end
 
 redef class MClassType
@@ -1641,13 +1727,20 @@ redef class MClassType
                        return "char*"
                else if mclass.name == "NativeArray" then
                        return "val*"
-               else if mclass.kind == extern_kind then
-                       return "void*"
                else
                        return "val*"
                end
        end
 
+       redef fun ctype_extern: String
+       do
+               if mclass.kind == extern_kind then
+                       return "void*"
+               else
+                       return ctype
+               end
+       end
+
        redef fun ctypename: String
        do
                if mclass.name == "Int" then
@@ -1663,8 +1756,6 @@ redef class MClassType
                else if mclass.name == "NativeArray" then
                        #return "{self.arguments.first.ctype}*"
                        return "val"
-               else if mclass.kind == extern_kind then
-                       return "ptr"
                else
                        return "val"
                end
@@ -1766,7 +1857,7 @@ redef class MMethodDef
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
                        return npropdef.can_inline
-               else if self.mproperty.name == "init" then
+               else if self.mproperty.is_root_init then
                        # Automatic free init is always inlined since it is empty or contains only attribtes assigments
                        return true
                else
@@ -1778,6 +1869,7 @@ redef class MMethodDef
        fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var modelbuilder = v.compiler.modelbuilder
+               var val = constant_value
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
                        var oldnode = v.current_node
@@ -1785,13 +1877,15 @@ redef class MMethodDef
                        self.compile_parameter_check(v, arguments)
                        npropdef.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
-               else if self.mproperty.name == "init" then
+               else if self.mproperty.is_root_init then
                        var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
                        var oldnode = v.current_node
                        v.current_node = nclassdef
                        self.compile_parameter_check(v, arguments)
                        nclassdef.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
+               else if val != null then
+                       v.ret(v.value_instance(val))
                else
                        abort
                end
@@ -1907,11 +2001,10 @@ redef class AMethPropdef
                var ret = mpropdef.msignature.return_mtype
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
-               else if mpropdef.mproperty.is_new then
-                       ret = arguments.first.mcasttype
                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
@@ -2088,7 +2181,7 @@ redef class AMethPropdef
                        else if pname == "atoi" then
                                v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
                                return true
-                       else if pname == "init" then
+                       else if pname == "new" then
                                v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
                                return true
                        end
@@ -2143,9 +2236,13 @@ redef class AMethPropdef
        fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
        do
                var externname
-               var nextern = self.n_extern
-               if nextern == null then return false
-               externname = nextern.text.substring(1, nextern.text.length-2)
+               var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
+               if at != null 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)
@@ -2154,14 +2251,16 @@ redef class AMethPropdef
                var ret = mpropdef.msignature.return_mtype
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
-                       res = v.new_var(ret)
+                       res = v.new_var_extern(ret)
                end
                v.adapt_signature(mpropdef, arguments)
+               v.unbox_signature_extern(mpropdef, arguments)
 
                if res == null then
                        v.add("{externname}({arguments.join(", ")});")
                else
                        v.add("{res} = {externname}({arguments.join(", ")});")
+                       res = v.box_extern(res, ret.as(not null))
                        v.ret(res)
                end
                return true
@@ -2172,20 +2271,26 @@ redef class AMethPropdef
        fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
        do
                var externname
-               var nextern = self.n_extern
-               if nextern == null then return false
-               externname = nextern.text.substring(1, nextern.text.length-2)
+               var at = self.get_single_annotation("extern", v.compiler.modelbuilder)
+               if at != null 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.adapt_signature(mpropdef, arguments)
+               v.unbox_signature_extern(mpropdef, arguments)
                var ret = arguments.first.mtype
-               var res = v.new_var(ret)
+               var res = v.new_var_extern(ret)
 
                arguments.shift
 
                v.add("{res} = {externname}({arguments.join(", ")});")
+               res = v.box_extern(res, ret)
                v.ret(res)
                return true
        end
@@ -2196,24 +2301,24 @@ redef class AAttrPropdef
        do
                if mpropdef == mreadpropdef then
                        assert arguments.length == 1
+                       var recv = arguments.first
                        var res
                        if is_lazy then
-                               var nexpr = n_expr
-                               assert nexpr != null
                                var set
                                var ret = self.mpropdef.static_mtype
                                var useiset = ret.ctype == "val*" and not ret isa MNullableType
                                var guard = self.mlazypropdef.mproperty
                                if useiset then
-                                       set = v.isset_attribute(self.mpropdef.mproperty, arguments.first)
+                                       set = v.isset_attribute(self.mpropdef.mproperty, recv)
                                else
-                                       set = v.read_attribute(guard, arguments.first)
+                                       set = v.read_attribute(guard, recv)
                                end
                                v.add("if(likely({set})) \{")
-                               res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
+                               res = v.read_attribute(self.mpropdef.mproperty, recv)
                                v.add("\} else \{")
-                               var value = v.expr(nexpr, self.mpropdef.static_mtype)
-                               v.write_attribute(self.mpropdef.mproperty, arguments.first, value)
+
+                               var value = evaluate_expr(v, recv)
+
                                v.assign(res, value)
                                if not useiset then
                                        var true_v = v.new_expr("1", v.bool_type)
@@ -2241,18 +2346,44 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
+               if has_value and not is_lazy then evaluate_expr(v, recv)
+       end
+
+       # Evaluate, store and return the default value of the attribute
+       private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable
+       do
+               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])
+               v.frame = frame
+
+               var value
+               var mtype = self.mpropdef.static_mtype
+               assert mtype != null
+
                var nexpr = self.n_expr
-               if nexpr != null and not is_lazy then
-                       var oldnode = v.current_node
-                       v.current_node = self
-                       var old_frame = v.frame
-                       var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv])
-                       v.frame = frame
-                       var value = v.expr(nexpr, self.mpropdef.static_mtype)
-                       v.write_attribute(self.mpropdef.mproperty, recv, value)
-                       v.frame = old_frame
-                       v.current_node = oldnode
+               var nblock = self.n_block
+               if nexpr != null then
+                       value = v.expr(nexpr, mtype)
+               else if nblock != null then
+                       value = v.new_var(mtype)
+                       frame.returnvar = value
+                       frame.returnlabel = v.get_name("RET_LABEL")
+                       v.add("\{")
+                       v.stmt(nblock)
+                       v.add("{frame.returnlabel.as(not null)}:(void)0;")
+                       v.add("\}")
+               else
+                       abort
                end
+
+               v.write_attribute(self.mpropdef.mproperty, recv, value)
+
+               v.frame = old_frame
+               v.current_node = oldnode
+
+               return value
        end
 
        fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
@@ -2276,33 +2407,12 @@ redef class AClassdef
        private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
                if mpropdef == self.mfree_init then
-                       if mpropdef.mproperty.is_root_init then
-                               assert self.super_inits == null
-                               assert arguments.length == 1
-                               if not mpropdef.is_intro then
-                                       v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
-                               end
-                               return
-                       end
-
-                       var super_inits = self.super_inits
-                       if super_inits != null then
-                               var args_of_super = arguments
-                               if arguments.length > 1 then args_of_super = [arguments.first]
-                               for su in super_inits do
-                                       v.send(su, args_of_super)
-                               end
-                       end
-
-                       var recv = arguments.first
-                       var i = 1
-                       # Collect undefined attributes
-                       for npropdef in self.n_propdefs do
-                               if npropdef isa AAttrPropdef and npropdef.n_expr == null and not npropdef.noinit then
-                                       v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
-                                       i += 1
-                               end
+                       assert mpropdef.mproperty.is_root_init
+                       assert arguments.length == 1
+                       if not mpropdef.is_intro then
+                               v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
                        end
+                       return
                else
                        abort
                end
@@ -2329,8 +2439,7 @@ redef class AExpr
        # Do not call this method directly, use `v.stmt` instead
        private fun stmt(v: AbstractCompilerVisitor)
        do
-               var res = expr(v)
-               if res != null then v.add("{res};")
+               expr(v)
        end
 end
 
@@ -2372,12 +2481,6 @@ redef class AVarExpr
 end
 
 redef class AVarAssignExpr
-       redef fun stmt(v)
-       do
-               var variable = self.variable.as(not null)
-               var i = v.expr(self.n_value, variable.declared_type)
-               v.assign(v.variable(variable), i)
-       end
        redef fun expr(v)
        do
                var variable = self.variable.as(not null)
@@ -2403,11 +2506,7 @@ redef class ASelfExpr
        redef fun expr(v) do return v.frame.arguments.first
 end
 
-redef class AContinueExpr
-       redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
-end
-
-redef class ABreakExpr
+redef class AEscapeExpr
        redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
 end
 
@@ -2470,10 +2569,7 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               var escapemark = self.escapemark
-               if escapemark != null then
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-               end
+               v.add_escape_label(break_mark)
        end
 end
 
@@ -2484,9 +2580,9 @@ redef class AWhileExpr
                var cond = v.expr_bool(self.n_expr)
                v.add("if (!{cond}) break;")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
        end
 end
 
@@ -2495,47 +2591,15 @@ redef class ALoopExpr
        do
                v.add("for(;;) \{")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
        end
 end
 
 redef class AForExpr
        redef fun stmt(v)
        do
-               # Shortcut on explicit range
-               # Avoid the instantiation of the range and the iterator
-               var nexpr = self.n_expr
-               if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
-                       var from = v.expr(nexpr.n_expr, null)
-                       var to = v.expr(nexpr.n_expr2, null)
-                       var variable = v.variable(variables.first)
-                       var one = v.new_expr("1", v.get_class("Int").mclass_type)
-
-                       v.assign(variable, from)
-                       v.add("for(;;) \{ /* shortcut range */")
-
-                       var ok
-                       if nexpr isa AOrangeExpr then
-                               ok = v.send(v.get_property("<", variable.mtype), [variable, to])
-                       else
-                               ok = v.send(v.get_property("<=", variable.mtype), [variable, to])
-                       end
-                       assert ok != null
-                       v.add("if(!{ok}) break;")
-
-                       v.stmt(self.n_block)
-
-                       v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
-                       var succ = v.send(v.get_property("successor", variable.mtype), [variable, one])
-                       assert succ != null
-                       v.assign(variable, succ)
-                       v.add("\}")
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-                       return
-               end
-
                var cl = v.expr(self.n_expr, null)
                var it_meth = self.method_iterator
                assert it_meth != null
@@ -2568,12 +2632,18 @@ redef class AForExpr
                        abort
                end
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                var next_meth = self.method_next
                assert next_meth != null
                v.compile_callsite(next_meth, [it])
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
+
+               var method_finish = self.method_finish
+               if method_finish != null then
+                       # TODO: Find a way to call this also in long escape (e.g. return)
+                       v.compile_callsite(method_finish, [it])
+               end
        end
 end
 
@@ -2714,7 +2784,7 @@ redef class ACrangeExpr
                var i2 = v.expr(self.n_expr2, null)
                var mtype = self.mtype.as(MClassType)
                var res = v.init_instance(mtype)
-               var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
+               v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
                return res
        end
 end
@@ -2726,7 +2796,7 @@ redef class AOrangeExpr
                var i2 = v.expr(self.n_expr2, null)
                var mtype = self.mtype.as(MClassType)
                var res = v.init_instance(mtype)
-               var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
+               v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
                return res
        end
 end
@@ -2806,11 +2876,9 @@ redef class ASendExpr
        redef fun expr(v)
        do
                var recv = v.expr(self.n_expr, null)
-               var args = [recv]
-               for a in self.raw_arguments do
-                       args.add(v.expr(a, null))
-               end
-               return v.compile_callsite(self.callsite.as(not null), args)
+               var callsite = self.callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               return v.compile_callsite(callsite, args)
        end
 end
 
@@ -2818,13 +2886,12 @@ redef class ASendReassignFormExpr
        redef fun stmt(v)
        do
                var recv = v.expr(self.n_expr, null)
-               var args = [recv]
-               for a in self.raw_arguments do
-                       args.add(v.expr(a, null))
-               end
+               var callsite = self.callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+
                var value = v.expr(self.n_value, null)
 
-               var left = v.compile_callsite(self.callsite.as(not null), args)
+               var left = v.compile_callsite(callsite, args)
                assert left != null
 
                var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
@@ -2839,14 +2906,12 @@ redef class ASuperExpr
        redef fun expr(v)
        do
                var recv = v.frame.arguments.first
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       args.add(v.expr(a, null))
-               end
 
                var callsite = self.callsite
                if callsite != null then
-                       # Add additionnals arguments for the super init call
+                       var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+
+                       # Add additional arguments for the super init call
                        if args.length == 1 then
                                for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
@@ -2857,19 +2922,22 @@ redef class ASuperExpr
                        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
                end
 
                # stantard call-next-method
-               return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
+               return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
        end
 end
 
 redef class ANewExpr
        redef fun expr(v)
        do
-               var mtype = self.mtype.as(MClassType)
+               var mtype = self.recvtype
+               assert mtype != null
                var recv
                var ctype = mtype.ctype
                if mtype.mclass.name == "NativeArray" then
@@ -2880,16 +2948,15 @@ redef class ANewExpr
                        return v.native_array_instance(elttype, l)
                else if ctype == "val*" then
                        recv = v.init_instance(mtype)
-               else if ctype == "void*" then
+               else if ctype == "char*" then
                        recv = v.new_expr("NULL/*special!*/", mtype)
                else
                        recv = v.new_expr("({ctype})0/*special!*/", mtype)
                end
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       args.add(v.expr(a, null))
-               end
-               var res2 = v.compile_callsite(self.callsite.as(not null), args)
+
+               var callsite = self.callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, 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}")
                        return res2
@@ -2908,12 +2975,13 @@ redef class AAttrExpr
 end
 
 redef class AAttrAssignExpr
-       redef fun stmt(v)
+       redef fun expr(v)
        do
                var recv = v.expr(self.n_expr, null)
                var i = v.expr(self.n_value, null)
                var mproperty = self.mproperty.as(not null)
                v.write_attribute(mproperty, recv, i)
+               return i
        end
 end
 
@@ -2992,9 +3060,6 @@ end
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
 
-var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
-toolcontext.option_context.add_option(opt_mixins)
-
 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
 
 # We do not add other options, so process them now!
@@ -3013,7 +3078,6 @@ end
 
 # Here we load an process all modules passed on the command line
 var mmodules = modelbuilder.parse(arguments)
-var mixins = modelbuilder.parse(opt_mixins.value)
 
 if mmodules.is_empty then return
 modelbuilder.run_phases
@@ -3021,8 +3085,5 @@ modelbuilder.run_phases
 for mmodule in mmodules do
        toolcontext.info("*** PROCESS {mmodule} ***", 1)
        var ms = [mmodule]
-       if not mixins.is_empty then
-               ms.add_all mixins
-       end
        toolcontext.run_global_phases(ms)
 end