nitc: fix calling extern constructors from extern code in separate compiler
[nit.git] / src / compiler / abstract_compiler.nit
index 94d5ada..07a8ede 100644 (file)
@@ -34,10 +34,12 @@ redef class ToolContext
        var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
        # --no-main
        var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
-       # --cc-paths
-       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 = 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 = new OptionString("Directory used to generate temporary files", "--compile-dir")
        # --hardening
@@ -76,6 +78,9 @@ redef class ToolContext
                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)
+
+               opt_no_main.hidden = true
        end
 
        redef fun process_options(args)
@@ -115,20 +120,16 @@ redef class ModelBuilder
        # 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
+               var platform = compiler.target_platform
+               var toolchain = platform.toolchain(toolcontext)
                compile_dir = toolchain.compile_dir
                toolchain.write_and_make compiler
        end
 end
 
 redef class Platform
-       fun toolchain(toolcontext: ToolContext): Toolchain is abstract
+       # The specific tool-chain associated to the platform
+       fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
 end
 
 class Toolchain
@@ -146,40 +147,9 @@ end
 
 class MakefileToolchain
        super Toolchain
-       # The list of directories to search for included C headers (-I for C compilers)
-       # The list is initially set with :
-       #   * the toolcontext --cc-path option
-       #   * the NIT_CC_PATH environment variable
-       #   * `toolcontext.nit_dir`
-       # Path can be added (or removed) by the client
-       var cc_paths = new Array[String]
-
-       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.")
-               end
-
-               # Add user defined cc_paths
-               cc_paths.append(toolcontext.opt_cc_path.value)
-
-               path_env = "NIT_CC_PATH".environ
-               if not path_env.is_empty then
-                       cc_paths.append(path_env.split_with(':'))
-               end
-       end
 
        redef fun write_and_make(compiler)
        do
-               gather_cc_paths
-
                var compile_dir = compile_dir
 
                # Generate the .h and .c files
@@ -215,16 +185,18 @@ class MakefileToolchain
 
        fun write_files(compiler: AbstractCompiler, 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 self.toolcontext.opt_stacktrace.value == "nitstack" and 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)
+               if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
                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"
+               var clib = toolcontext.nit_dir / "clib"
+               compiler.files_to_copy.add "{clib}/gc_chooser.c"
+               compiler.files_to_copy.add "{clib}/gc_chooser.h"
 
                # FFI
                for m in compiler.mainmodule.in_importation.greaters do
@@ -251,39 +223,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)
@@ -299,13 +253,34 @@ 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)
        end
 
-       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
+       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
 
        fun default_outname(mainmodule: MModule): String
        do
@@ -332,31 +307,32 @@ class MakefileToolchain
        fun write_makefile(compiler: AbstractCompiler, 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)
 
-               var orig_dir = compile_dir.relpath(".")
-               var outpath = orig_dir.join_path(outname).simplify_path
+               var real_outpath = compile_dir.relpath(outname)
+               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
+                       # 2. copy the binary at the right place in the `all` goal.
+                       outpath = mainmodule.c_name
+               end
                var makename = makefile_name(mainmodule)
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
 
-               var cc_includes = ""
-               for p in cc_paths do
-                       cc_includes += " -I \"" + p + "\""
-               end
-
                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
 
-               makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc {linker_options.join(" ")}\n\n")
+               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")
 
                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")
+               if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n")
 
                # Dynamic adaptations
                # While `platform` enable complex toolchains, they are statically applied
@@ -375,7 +351,11 @@ class MakefileToolchain
 
                makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
 
-               makefile.write("all: {outpath}\n\n")
+               makefile.write("all: {outpath}\n")
+               if outpath != real_outpath then
+                       makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
+               end
+               makefile.write("\n")
 
                var ofiles = new Array[String]
                var dep_rules = new Array[String]
@@ -387,8 +367,42 @@ class MakefileToolchain
                        dep_rules.add(o)
                end
 
+               # 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 OFStream.open(linker_script_path)
+                       for l in compiler.linker_script do
+                               f.write l
+                               f.write "\n"
+                       end
+                       f.close
+               end
+
                var java_files = new Array[ExternFile]
 
+               var pkgconfigs = new Array[String]
+               for f in compiler.extern_bodies do
+                       pkgconfigs.add_all f.pkgconfigs
+               end
+               # Protect pkg-config
+               if not pkgconfigs.is_empty then
+                       makefile.write """
+# does pkg-config exists?
+ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
+$(error "Command `pkg-config` not found. Please install it")
+endif
+"""
+                       for p in pkgconfigs do
+                               makefile.write """
+# Check for library {{{p}}}
+ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
+$(error "pkg-config: package {{{p}}} is not found.")
+endif
+"""
+                       end
+               end
+
                # Compile each required extern body into a specific .o
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
@@ -414,11 +428,20 @@ class MakefileToolchain
                end
 
                # Link edition
-               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n")
+               var pkg = ""
+               if not pkgconfigs.is_empty then
+                       pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
+               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\n")
+               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+               if outpath != real_outpath then
+                       makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+               end
                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)
@@ -453,7 +476,7 @@ abstract class AbstractCompiler
        var mainmodule: MModule is writable
 
        # The real main module of the program
-       var realmainmodule: MModule
+       var realmainmodule: MModule is noinit
 
        # The modelbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder is protected writable
@@ -461,17 +484,30 @@ abstract class AbstractCompiler
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
-       init(mainmodule: MModule, modelbuilder: ModelBuilder)
+       # The targeted specific platform
+       var target_platform: Platform is noinit
+
+       init
        do
-               self.mainmodule = mainmodule
                self.realmainmodule = mainmodule
-               self.modelbuilder = modelbuilder
+               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
        do
+               if modelbuilder.toolcontext.opt_group_c_files.value then
+                       if self.files.is_empty then
+                               var f = new CodeFile(mainmodule.c_name)
+                               self.files.add(f)
+                       end
+                       return self.files.first
+               end
                var f = new CodeFile(name)
                self.files.add(f)
                return f
@@ -485,7 +521,11 @@ abstract class AbstractCompiler
        fun new_visitor: VISITOR is abstract
 
        # Where global declaration are stored (the main .h)
-       var header: CodeWriter is writable
+       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)
@@ -518,9 +558,9 @@ abstract class AbstractCompiler
                stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
                for i in names.keys do
                        stream.write("\{\"")
-                       stream.write(i)
+                       stream.write(i.escape_to_c)
                        stream.write("\",\"")
-                       stream.write(names[i])
+                       stream.write(names[i].escape_to_c)
                        stream.write("\"\},\n")
                end
                stream.write("\};\n")
@@ -657,11 +697,11 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                var v = self.new_visitor
                v.add_decl("#include <signal.h>")
                var ost = modelbuilder.toolcontext.opt_stacktrace.value
-               var platform = mainmodule.target_platform
+               var platform = target_platform
 
-               if platform != null and not platform.supports_libunwind then ost = "none"
+               if not platform.supports_libunwind then ost = "none"
 
-               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
                        v.add_decl("#define UNW_LOCAL_ONLY")
@@ -894,12 +934,8 @@ 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
-                                       npropdef.init_expr(v, recv)
-                               end
+                       for npropdef in modelbuilder.collect_attr_propdef(cd) do
+                               npropdef.init_expr(v, recv)
                        end
                end
        end
@@ -910,12 +946,8 @@ 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
-                                       npropdef.check_expr(v, recv)
-                               end
+                       for npropdef in modelbuilder.collect_attr_propdef(cd) do
+                               npropdef.check_expr(v, recv)
                        end
                end
        end
@@ -1007,9 +1039,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
@@ -1025,8 +1056,8 @@ abstract class AbstractCompilerVisitor
        # The current visited AST node
        var current_node: nullable ANode = null is writable
 
-       # The current `Frame`
-       var frame: nullable Frame 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
@@ -1034,11 +1065,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
 
@@ -1269,11 +1299,11 @@ abstract class AbstractCompilerVisitor
        fun escapemark_name(e: nullable EscapeMark): String
        do
                assert e != null
-               if escapemark_names.has_key(e) then return escapemark_names[e]
+               if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
                var name = e.name
                if name == null then name = "label"
                name = get_name(name)
-               escapemark_names[e] = name
+               frame.escapemark_names[e] = name
                return name
        end
 
@@ -1285,8 +1315,6 @@ abstract class AbstractCompilerVisitor
                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
        # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
        fun class_name_string(value: RuntimeVariable): String is abstract
@@ -1353,6 +1381,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 ctype == "val*" 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
@@ -1450,10 +1496,11 @@ 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.location.file.filename
                file = file.strip_extension(".nit")
                var tryfile = file + ".nit.h"
                if tryfile.file_exists then
@@ -1526,6 +1573,15 @@ abstract class AbstractCompilerVisitor
        fun stmt(nexpr: nullable AExpr)
        do
                if nexpr == null then return
+
+               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)
@@ -1622,11 +1678,8 @@ class RuntimeVariable
        # false (usual value) means that the variable is a mcasttype or a subtype.
        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
@@ -1651,8 +1704,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
 
@@ -1674,6 +1727,13 @@ class Frame
 
        # The label at the end of the property
        var returnlabel: nullable String = null is writable
+
+       # 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
 end
 
 redef class MType
@@ -1685,21 +1745,9 @@ 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
 end
 
 redef class MClassType
-       redef fun c_name
-       do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
 
        redef fun ctype: String
        do
@@ -1750,90 +1798,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.name.to_cmangle}__{name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
-       private var c_name_cache: nullable String
-end
-
-redef class MProperty
-       fun c_name: String do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{self.intro.c_name}"
-               self.c_name_cache = res
-               return res
-       end
-       private var c_name_cache: nullable String
-end
-
 redef class MPropDef
        type VISITOR: AbstractCompilerVisitor
-
-       private var c_name_cache: nullable String
-
-       # The mangled name associated to the property
-       fun c_name: String
-       do
-               var res = self.c_name_cache
-               if res != null then return res
-               res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}"
-               self.c_name_cache = res
-               return res
-       end
 end
 
 redef class MMethodDef
@@ -1842,10 +1808,10 @@ redef class MMethodDef
        do
                if is_abstract then return true
                var modelbuilder = v.compiler.modelbuilder
-               if modelbuilder.mpropdef2npropdef.has_key(self) then
-                       var npropdef = modelbuilder.mpropdef2npropdef[self]
-                       return npropdef.can_inline
-               else if self.mproperty.is_root_init then
+               var node = modelbuilder.mpropdef2node(self)
+               if node isa APropdef then
+                       return node.can_inline
+               else if node isa AClassdef then
                        # Automatic free init is always inlined since it is empty or contains only attribtes assigments
                        return true
                else
@@ -1858,19 +1824,18 @@ redef class MMethodDef
        do
                var modelbuilder = v.compiler.modelbuilder
                var val = constant_value
-               if modelbuilder.mpropdef2npropdef.has_key(self) then
-                       var npropdef = modelbuilder.mpropdef2npropdef[self]
+               var node = modelbuilder.mpropdef2node(self)
+               if node isa APropdef then
                        var oldnode = v.current_node
-                       v.current_node = npropdef
+                       v.current_node = node
                        self.compile_parameter_check(v, arguments)
-                       npropdef.compile_to_c(v, self, arguments)
+                       node.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
-               else if self.mproperty.is_root_init then
-                       var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
+               else if node isa AClassdef then
                        var oldnode = v.current_node
-                       v.current_node = nclassdef
+                       v.current_node = node
                        self.compile_parameter_check(v, arguments)
-                       nclassdef.compile_to_c(v, self, arguments)
+                       node.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
                else if val != null then
                        v.ret(v.value_instance(val))
@@ -2225,16 +2190,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
@@ -2266,10 +2228,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
@@ -2285,6 +2244,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
@@ -2343,7 +2304,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.mcasttype.as(MClassType), [recv])
+               var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
                v.frame = frame
 
                var value
@@ -2351,8 +2312,17 @@ redef class AAttrPropdef
                assert mtype != null
 
                var nexpr = self.n_expr
+               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
@@ -2373,7 +2343,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)
@@ -2727,13 +2697,18 @@ 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
-               return v.array_instance(array, mtype)
+               v.frame.comprehension = old_comprehension
+
+               return res
        end
 end
 
@@ -2917,22 +2892,17 @@ 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)
-               else
-                       recv = v.new_expr("({ctype})0/*special!*/", mtype)
                end
 
+               var recv = v.init_instance_or_extern(mtype)
+
                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)
@@ -3033,13 +3003,13 @@ 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
 var toolcontext = new ToolContext
 
-toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
+toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
 
 # We do not add other options, so process them now!
 toolcontext.process_options(args)