Merge branch 'master' into polymorphic_extern_classes
[nit.git] / src / compiler / abstract_compiler.nit
similarity index 96%
rename from src/abstract_compiler.nit
rename to src/compiler/abstract_compiler.nit
index 9e67eae..ef02aec 100644 (file)
 module abstract_compiler
 
 import literal
-import typing
-import auto_super_init
+import semantize
 import platform
 import c_tools
+private import annotation
 
 # Add compiling options
 redef class ToolContext
@@ -395,7 +395,7 @@ class MakefileToolchain
                        if f.compiles_to_o_file then ofiles.add(o)
                        if f.add_to_jar then java_files.add(f)
                end
-               
+
                if not java_files.is_empty then
                        var jar_file = "{outpath}.jar"
 
@@ -445,13 +445,13 @@ 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
 
        # The modeulbuilder used to know the model and the AST
-       var modelbuilder: ModelBuilder protected writable
+       var modelbuilder: ModelBuilder is protected writable
 
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
@@ -480,7 +480,7 @@ abstract class AbstractCompiler
        fun new_visitor: VISITOR is abstract
 
        # Where global declaration are stored (the main .h)
-       var header: CodeWriter writable
+       var header: CodeWriter is writable
 
        # Provide a declaration that can be requested (before or latter) by a visitor
        fun provide_declaration(key: String, s: String)
@@ -643,10 +643,12 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
        end
 
        # Generate the main C function.
+       #
        # This function:
-       #       * allocate the Sys object if it exists
-       #       * call init if is exists
-       #       * call main if it exists
+       #
+       # * allocate the Sys object if it exists
+       # * call init if is exists
+       # * call main if it exists
        fun compile_main_function
        do
                var v = self.new_visitor
@@ -930,12 +932,13 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
        end
 
        # Display stats about compilation process
+       #
        # Metrics used:
-       #       * type tests against resolved types (`x isa Collection[Animal]`)
-       #       * type tests against unresolved types (`x isa Collection[E]`)
-       #       * type tests skipped
-       #       * type tests total
-       #       *
+       #
+       # * type tests against resolved types (`x isa Collection[Animal]`)
+       # * type tests against unresolved types (`x isa Collection[E]`)
+       # * type tests skipped
+       # * type tests total
        fun display_stats
        do
                if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
@@ -1015,10 +1018,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 is writable
 
        # Alias for self.compiler.mainmodule.object_type
        fun object_type: MClassType do return self.compiler.mainmodule.object_type
@@ -1261,7 +1264,7 @@ abstract class AbstractCompilerVisitor
        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
+       # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
        fun class_name_string(value: RuntimeVariable): String is abstract
 
        # Variables handling
@@ -1332,7 +1335,7 @@ abstract class AbstractCompilerVisitor
                var mtype = recv.mtype
                var finalizable_type = compiler.mainmodule.finalizable_type
                if finalizable_type != null and not mtype.need_anchor and
-                  mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
+                               mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
                        add "gc_register_finalizer({recv});"
                end
        end
@@ -1541,7 +1544,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
@@ -1564,11 +1567,11 @@ 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)
        do
@@ -1618,10 +1621,10 @@ 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
@@ -1636,7 +1639,7 @@ redef class MType
 
        # 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
@@ -1889,6 +1892,18 @@ redef class AMethPropdef
                        v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
                end
 
+               # Try special compilation
+               if mpropdef.is_intern then
+                       if compile_intern_to_c(v, mpropdef, arguments) then return
+               else if mpropdef.is_extern then
+                       if mpropdef.mproperty.is_init then
+                               if compile_externinit_to_c(v, mpropdef, arguments) then return
+                       else
+                               if compile_externmeth_to_c(v, mpropdef, arguments) then return
+                       end
+               end
+
+               # Compile block if any
                var n_block = n_block
                if n_block != null then
                        for i in [0..mpropdef.msignature.arity[ do
@@ -1896,17 +1911,13 @@ redef class AMethPropdef
                                v.assign(v.variable(variable), arguments[i+1])
                        end
                        v.stmt(n_block)
-               else if mpropdef.is_intern then
-                       compile_intern_to_c(v, mpropdef, arguments)
-               else if mpropdef.is_extern then
-                       if mpropdef.mproperty.is_init then
-                               compile_externinit_to_c(v, mpropdef, arguments)
-                       else
-                               compile_externmeth_to_c(v, mpropdef, arguments)
-                       end
-               else
-                       abort
+                       return
                end
+
+               # We have a problem
+               var cn = v.class_name_string(arguments.first)
+               v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
+               v.add_raw_abort
        end
 
        redef fun can_inline
@@ -1919,7 +1930,7 @@ redef class AMethPropdef
                return false
        end
 
-       fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+       fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool
        do
                var pname = mpropdef.mproperty.name
                var cname = mpropdef.mclassdef.mclass.name
@@ -1936,239 +1947,242 @@ redef class AMethPropdef
                if cname == "Int" then
                        if pname == "output" then
                                v.add("printf(\"%ld\\n\", {arguments.first});")
-                               return
+                               return true
                        else if pname == "object_id" then
                                v.ret(arguments.first)
-                               return
+                               return true
                        else if pname == "+" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "-" then
                                v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "*" then
                                v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "/" then
                                v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "%" then
                                v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "lshift" then
                                v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "rshift" then
                                v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
-                               return
+                               return true
                        else if pname == "!=" then
                                var res = v.equal_test(arguments[0], arguments[1])
                                v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "<" then
                                v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == ">" then
                                v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "<=" then
                                v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == ">=" then
                                v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "to_f" then
                                v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "ascii" then
                                v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
-                               return
+                               return true
                        end
                else if cname == "Char" then
                        if pname == "output" then
                                v.add("printf(\"%c\", {arguments.first});")
-                               return
+                               return true
                        else if pname == "object_id" then
                                v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "successor" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "predecessor" then
                                v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
-                               return
+                               return true
                        else if pname == "!=" then
                                var res = v.equal_test(arguments[0], arguments[1])
                                v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "<" then
                                v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == ">" then
                                v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "<=" then
                                v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == ">=" then
                                v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "to_i" then
                                v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "ascii" then
                                v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
-                               return
+                               return true
                        end
                else if cname == "Bool" then
                        if pname == "output" then
                                v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
-                               return
+                               return true
                        else if pname == "object_id" then
                                v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
-                               return
+                               return true
                        else if pname == "!=" then
                                var res = v.equal_test(arguments[0], arguments[1])
                                v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
+                               return true
                        end
                else if cname == "Float" then
                        if pname == "output" then
                                v.add("printf(\"%f\\n\", {arguments.first});")
-                               return
+                               return true
                        else if pname == "object_id" then
                                v.ret(v.new_expr("(double){arguments.first}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "+" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "-" then
                                v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "succ" then
                                v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "prec" then
                                v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "*" then
                                v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "/" then
                                v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "==" then
                                v.ret(v.equal_test(arguments[0], arguments[1]))
-                               return
+                               return true
                        else if pname == "!=" then
                                var res = v.equal_test(arguments[0], arguments[1])
                                v.ret(v.new_expr("!{res}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "<" then
                                v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == ">" then
                                v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "<=" then
                                v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == ">=" then
                                v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "to_i" then
                                v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
-                               return
+                               return true
                        end
                else if cname == "NativeString" then
                        if pname == "[]" then
                                v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "[]=" then
                                v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
-                               return
+                               return true
                        else if pname == "copy_to" then
                                v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
-                               return
+                               return true
                        else if pname == "atoi" then
                                v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
-                               return
+                               return true
                        else if pname == "init" then
                                v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
-                               return
+                               return true
                        end
                else if cname == "NativeArray" then
                        v.native_array_def(pname, ret, arguments)
-                       return
+                       return true
                end
                if pname == "exit" then
                        v.add("exit({arguments[1]});")
-                       return
+                       return true
                else if pname == "sys" then
                        v.ret(v.new_expr("glob_sys", ret.as(not null)))
-                       return
+                       return true
                else if pname == "calloc_string" then
                        v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
-                       return
+                       return true
                else if pname == "calloc_array" then
                        v.calloc_array(ret.as(not null), arguments)
-                       return
+                       return true
                else if pname == "object_id" then
                        v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
-                       return
+                       return true
                else if pname == "is_same_type" then
                        v.ret(v.is_same_type_test(arguments[0], arguments[1]))
-                       return
+                       return true
                else if pname == "is_same_instance" then
                        v.ret(v.equal_test(arguments[0], arguments[1]))
-                       return
+                       return true
                else if pname == "output_class_name" then
                        var nat = v.class_name_string(arguments.first)
                        v.add("printf(\"%s\\n\", {nat});")
-                       return
+                       return true
                else if pname == "native_class_name" then
                        var nat = v.class_name_string(arguments.first)
                        v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
-                       return
+                       return true
                else if pname == "force_garbage_collection" then
                        v.add("nit_gcollect();")
-                       return
+                       return true
                else if pname == "native_argc" then
                        v.ret(v.new_expr("glob_argc", ret.as(not null)))
-                       return
+                       return true
                else if pname == "native_argv" then
                        v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
-                       return
+                       return true
                end
-               v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
-               debug("Not implemented {mpropdef}")
+               return false
        end
 
-       fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+       # Compile an extern method
+       # Return `true` if the compilation was successful, `false` if a fall-back is needed
+       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
-                       v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
-                       v.add("show_backtrace(1);")
-                       return
+               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
+                       var nextern = self.n_extern
+                       if nextern == null then return false
+                       externname = nextern.text.substring(1, nextern.text.length-2)
                end
-               externname = nextern.text.substring(1, nextern.text.length-2)
                if location.file != null then
                        var file = location.file.filename
                        v.add_extern(file)
@@ -2189,18 +2203,23 @@ redef class AMethPropdef
                        res = v.box_extern(res, ret.as(not null))
                        v.ret(res)
                end
+               return true
        end
 
-       fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+       # Compile an extern factory
+       # Return `true` if the compilation was successful, `false` if a fall-back is needed
+       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
-                       v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
-                       v.add("show_backtrace(1);")
-                       return
+               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
+                       var nextern = self.n_extern
+                       if nextern == null then return false
+                       externname = nextern.text.substring(1, nextern.text.length-2)
                end
-               externname = nextern.text.substring(1, nextern.text.length-2)
                if location.file != null then
                        var file = location.file.filename
                        v.add_extern(file)
@@ -2215,6 +2234,7 @@ redef class AMethPropdef
                v.add("{res} = {externname}({arguments.join(", ")});")
                res = v.box_extern(res, ret)
                v.ret(res)
+               return true
        end
 end
 
@@ -3053,4 +3073,3 @@ for mmodule in mmodules do
        end
        toolcontext.run_global_phases(ms)
 end
-