compiler: skip broken entities
[nit.git] / src / compiler / separate_compiler.nit
index 31a4f20..d3390af 100644 (file)
@@ -252,7 +252,8 @@ class SeparateCompiler
        do
                # Collect all bas box class
                # FIXME: this is not completely fine with a separate compilation scheme
-               for classname in ["Int", "Bool", "Byte", "Char", "Float", "NativeString", "Pointer"] do
+               for classname in ["Int", "Bool", "Byte", "Char", "Float", "NativeString",
+                                "Pointer", "Int8", "Int16", "UInt16", "Int32", "UInt32"] do
                        var classes = self.mainmodule.model.get_mclasses_by_name(classname)
                        if classes == null then continue
                        assert classes.length == 1 else print classes.join(", ")
@@ -625,6 +626,7 @@ class SeparateCompiler
                for cd in mmodule.mclassdefs do
                        for pd in cd.mpropdefs do
                                if not pd isa MMethodDef then continue
+                               if pd.mproperty.is_broken or pd.is_broken or pd.msignature == null then continue # Skip broken method
                                var rta = runtime_type_analysis
                                if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
                                #print "compile {pd} @ {cd} @ {mmodule}"
@@ -812,18 +814,23 @@ class SeparateCompiler
        # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
        fun compile_class_to_c(mclass: MClass)
        do
+               if mclass.is_broken then return
+
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
 
                var v = new_visitor
 
                var rta = runtime_type_analysis
-               var is_dead = rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" and mclass.name != "Pointer"
+               var is_dead = rta != null and not rta.live_classes.has(mclass)
+               # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
+               # in order to compile and link correctly the C code, these symbols should be declared and defined.
+               var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
 
-               v.add_decl("/* runtime class {c_name} */")
+               v.add_decl("/* runtime class {c_name}: {mclass.full_name} (dead={is_dead}; need_corpse={need_corpse})*/")
 
                # Build class vft
-               if not is_dead then
+               if not is_dead or need_corpse then
                        self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
                        v.add_decl("const struct class class_{c_name} = \{")
                        v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
@@ -838,6 +845,9 @@ class SeparateCompiler
                                        if rta != null and not rta.live_methoddefs.has(mpropdef) then
                                                v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
                                                continue
+                                       else if mpropdef.is_broken or mpropdef.msignature == null or mpropdef.mproperty.is_broken then
+                                               v.add_decl("NULL, /* DEAD (BROKEN) {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
+                                               continue
                                        end
                                        var rf = mpropdef.virtual_runtime_function
                                        v.require_declaration(rf.c_name)
@@ -860,7 +870,8 @@ class SeparateCompiler
                        self.header.add_decl("{mtype.ctype_extern} value;")
                        self.header.add_decl("\};")
 
-                       if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
+                       # Pointer is needed by extern types, live or not
+                       if is_dead and mtype.mclass.name != "Pointer" then return
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
@@ -876,6 +887,7 @@ class SeparateCompiler
                        v.add("return (val*)res;")
                        v.add("\}")
 
+                       # A Pointer class also need its constructor
                        if mtype.mclass.name != "Pointer" then return
 
                        v = new_visitor
@@ -930,7 +942,7 @@ class SeparateCompiler
                        var pointer_type = mainmodule.pointer_type
 
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
-                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("/* allocate extern {mtype} */")
                        v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
                        if is_dead then
                                v.add_abort("{mclass} is DEAD")
@@ -991,10 +1003,13 @@ class SeparateCompiler
                        # use some Huffman coding.
                        if t.name == "Int" then
                                class_info[1] = t
+                               t.mclass_type.tag_value = 1
                        else if t.name == "Char" then
                                class_info[2] = t
+                               t.mclass_type.tag_value = 2
                        else if t.name == "Bool" then
                                class_info[3] = t
+                               t.mclass_type.tag_value = 3
                        else
                                continue
                        end
@@ -1193,7 +1208,7 @@ class SeparateCompilerVisitor
                                if mtype.name == "Int" then
                                        return self.new_expr("(long)({value})>>2", mtype)
                                else if mtype.name == "Char" then
-                                       return self.new_expr("(char)((long)({value})>>2)", mtype)
+                                       return self.new_expr("(uint32_t)((long)({value})>>2)", mtype)
                                else if mtype.name == "Bool" then
                                        return self.new_expr("(short int)((long)({value})>>2)", mtype)
                                else
@@ -1202,27 +1217,29 @@ class SeparateCompilerVisitor
                        end
                        return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if not mtype.is_c_primitive then
+                       assert value.mtype == value.mcasttype
                        if value.mtype.is_tagged then
+                               var res
                                if value.mtype.name == "Int" then
-                                       return self.new_expr("(val*)({value}<<2|1)", mtype)
+                                       res = self.new_expr("(val*)({value}<<2|1)", mtype)
                                else if value.mtype.name == "Char" then
-                                       return self.new_expr("(val*)((long)({value})<<2|2)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|2)", mtype)
                                else if value.mtype.name == "Bool" then
-                                       return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|3)", mtype)
                                else
                                        abort
                                end
+                               # Do not loose type info
+                               res.mcasttype = value.mcasttype
+                               return res
                        end
                        var valtype = value.mtype.as(MClassType)
                        if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
                                valtype = compiler.mainmodule.pointer_type
                        end
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
-                               self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       # Do not loose type info
+                       res.mcasttype = value.mcasttype
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
@@ -1258,11 +1275,7 @@ class SeparateCompilerVisitor
                   mtype.mclass.name != "NativeString" then
                        var valtype = compiler.mainmodule.pointer_type
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
-                               self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       compiler.undead_types.add(mtype)
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
                        self.require_declaration("type_{mtype.c_name}")
@@ -1853,7 +1866,7 @@ class SeparateCompilerVisitor
                        else
                                var mtype1 = value1.mtype.as(MClassType)
                                self.require_declaration("class_{mtype1.c_name}")
-                               self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
+                               self.add("{res} = ({value2} != NULL) && ({class_info(value2)} == &class_{mtype1.c_name}); /* is_same_type_test */")
                        end
                else
                        self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
@@ -1886,20 +1899,58 @@ class SeparateCompilerVisitor
                        value2 = tmp
                end
                if value1.mtype.is_c_primitive then
-                       if value2.mtype == value1.mtype then
+                       var t1 = value1.mtype
+                       assert t1 == value1.mcasttype
+
+                       # Fast case: same C type.
+                       if value2.mtype == t1 then
+                               # Same exact C primitive representation.
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.is_c_primitive then
-                               self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
-                       else if value1.mtype.is_tagged then
-                               self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
+                               return res
+                       end
+
+                       # Complex case: value2 has a different representation
+                       # Thus, it should be checked if `value2` is type-compatible with `value1`
+                       # This compatibility is done statically if possible and dynamically else
+
+                       # Conjunction (ands) of dynamic tests according to the static knowledge
+                       var tests = new Array[String]
+
+                       var t2 = value2.mcasttype
+                       if t2 isa MNullableType then
+                               # The destination type cannot be null
+                               tests.add("({value2} != NULL)")
+                               t2 = t2.mtype
+                       else if t2 isa MNullType then
+                               # `value2` is known to be null, thus incompatible with a primitive
+                               self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
+                               return res
+                       end
+
+                       if t2 == t1 then
+                               # Same type but different representation.
+                       else if t2.is_c_primitive then
+                               # Type of `value2` is a different primitive type, thus incompatible
+                               self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
+                               return res
+                       else if t1.is_tagged then
+                               # To be equal, `value2` should also be correctly tagged
+                               tests.add("({extract_tag(value2)} == {t1.tag_value})")
                        else
-                               var mtype1 = value1.mtype.as(MClassType)
-                               self.require_declaration("class_{mtype1.c_name}")
-                               self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
-                               self.add("if ({res}) \{")
-                               self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
-                               self.add("\}")
+                               # To be equal, `value2` should also be boxed with the same class
+                               self.require_declaration("class_{t1.c_name}")
+                               tests.add "({class_info(value2)} == &class_{t1.c_name})"
+                       end
+
+                       # Compare the unboxed `value2` with `value1`
+                       if tests.not_empty then
+                               self.add "if ({tests.join(" && ")}) \{"
                        end
+                       self.add "{res} = {self.autobox(value2, t1)} == {value1};"
+                       if tests.not_empty then
+                               self.add "\} else {res} = 0;"
+                       end
+
                        return res
                end
                var maybe_null = true
@@ -2208,13 +2259,15 @@ class SeparateRuntimeFunction
        # The C type for the function pointer.
        var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
-       # The arguments, as generated by `compile_to_c`
-       private var arguments: Array[RuntimeVariable] is noinit
-
        redef fun compile_to_c(compiler)
        do
                var mmethoddef = self.mmethoddef
 
+               var sig = "{c_ret} {c_name}{c_sig}"
+               compiler.provide_declaration(self.c_name, "{sig};")
+
+               var rta = compiler.as(SeparateCompiler).runtime_type_analysis
+
                var recv = self.mmethoddef.mclassdef.bound_mtype
                var v = compiler.new_visitor
                var selfvar = new RuntimeVariable("self", called_recv, recv)
@@ -2225,12 +2278,7 @@ class SeparateRuntimeFunction
                var msignature = called_signature
                var ret = called_signature.return_mtype
 
-               var sig = new FlatBuffer
                var comment = new FlatBuffer
-               sig.append(c_ret)
-               sig.append(" ")
-               sig.append(self.c_name)
-               sig.append(c_sig)
                comment.append("({selfvar}: {selfvar.mtype}")
                arguments.add(selfvar)
                for i in [0..msignature.arity[ do
@@ -2246,8 +2294,6 @@ class SeparateRuntimeFunction
                if ret != null then
                        comment.append(": {ret}")
                end
-               compiler.provide_declaration(self.c_name, "{sig};")
-               self.arguments = arguments.to_a
 
                v.add_decl("/* method {self} for {comment} */")
                v.add_decl("{sig} \{")
@@ -2262,6 +2308,8 @@ class SeparateRuntimeFunction
                                assert subret != null
                                v.assign(frame.returnvar.as(not null), subret)
                        end
+               else if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
+                       v.add_abort("FATAL: Dead method executed.")
                else
                        mmethoddef.compile_inside_to_c(v, arguments)
                end
@@ -2280,8 +2328,10 @@ class SeparateRuntimeFunction
        fun compile_trampolines(compiler: SeparateCompiler)
        do
                var recv = self.mmethoddef.mclassdef.bound_mtype
-               var selfvar = arguments.first
+               var selfvar = new RuntimeVariable("self", called_recv, recv)
                var ret = called_signature.return_mtype
+               var arguments = ["self"]
+               for i in [0..called_signature.arity[ do arguments.add "p{i}"
 
                if mmethoddef.is_intro and not recv.is_c_primitive then
                        var m = mmethoddef.mproperty
@@ -2323,6 +2373,12 @@ redef class MType
        # Are values of `self` tagged?
        # If false, it means that the type is not primitive, or is boxed.
        var is_tagged = false
+
+       # The tag value of the type
+       #
+       # ENSURE `is_tagged == (tag_value > 0)`
+       # ENSURE `not is_tagged == (tag_value == 0)`
+       var tag_value = 0
 end
 
 redef class MEntity