Callref expressions support for Global Compiler
[nit.git] / src / compiler / global_compiler.nit
index 9a82243..8f3b437 100644 (file)
@@ -59,35 +59,7 @@ redef class ModelBuilder
                self.toolcontext.info("*** GENERATING C ***", 1)
 
                var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis)
-               compiler.compile_header
-
-               for t in runtime_type_analysis.live_types do
-                       compiler.declare_runtimeclass(t)
-               end
-
-               compiler.compile_class_names
-
-               # Init instance code (allocate and init-arguments)
-               for t in runtime_type_analysis.live_types do
-                       if t.ctype == "val*" then
-                               compiler.generate_init_instance(t)
-                       else
-                               compiler.generate_box_instance(t)
-                       end
-               end
-
-               # The main function of the C
-               compiler.compile_nitni_global_ref_functions
-               compiler.compile_main_function
-
-               # Compile until all runtime_functions are visited
-               while not compiler.todos.is_empty do
-                       var m = compiler.todos.shift
-                       self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
-                       m.compile_to_c(compiler)
-               end
-               self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
-
+               compiler.do_compilation
                compiler.display_stats
 
                var time1 = get_time
@@ -108,20 +80,59 @@ class GlobalCompiler
        # The result of the RTA (used to know live types and methods)
        var runtime_type_analysis: RapidTypeAnalysis
 
-       init(mainmodule: MModule, modelbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis)
+       init
        do
-               super(mainmodule, modelbuilder)
-               var file = new_file("{mainmodule.name}.nitgg")
+               var file = new_file("{mainmodule.c_name}.nitgg")
                self.header = new CodeWriter(file)
-               self.runtime_type_analysis = runtime_type_analysis
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
-                       if t.ctype != "val*" then
+                       if t.is_c_primitive or t.mclass.name == "Pointer" then
                                self.live_primitive_types.add(t)
                        end
                end
        end
 
+       redef fun do_compilation
+       do
+               var compiler = self
+
+               compiler.compile_header
+
+               if mainmodule.model.get_mclasses_by_name("Pointer") != null then
+                       runtime_type_analysis.live_types.add(mainmodule.pointer_type)
+               end
+               for t in runtime_type_analysis.live_types do
+                       compiler.declare_runtimeclass(t)
+               end
+
+               compiler.compile_class_names
+
+               # Init instance code (allocate and init-arguments)
+               for t in runtime_type_analysis.live_types do
+                       if not t.is_c_primitive then
+                               compiler.generate_init_instance(t)
+                               if t.mclass.kind == extern_kind then
+                                       compiler.generate_box_instance(t)
+                               end
+                       else
+                               compiler.generate_box_instance(t)
+                       end
+               end
+
+               # The main function of the C
+               compiler.compile_nitni_global_ref_functions
+               compiler.compile_main_function
+
+               # Compile until all runtime_functions are visited
+               while not compiler.todos.is_empty do
+                       var m = compiler.todos.shift
+                       modelbuilder.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
+                       m.compile_to_c(compiler)
+               end
+               modelbuilder.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
+
+       end
+
        # Compile class names (for the class_name and output_class_name methods)
        protected fun compile_class_names do
                var v = new_visitor
@@ -140,7 +151,7 @@ class GlobalCompiler
                if self.classids.has_key(mtype) then
                        return self.classids[mtype]
                end
-               print "No classid for {mtype}"
+               print_error "No classid for {mtype}"
                abort
        end
 
@@ -157,7 +168,7 @@ class GlobalCompiler
 
        # Subset of runtime_type_analysis.live_types that contains only primitive types
        # Used to implement the equal test
-       var live_primitive_types: Array[MClassType]
+       var live_primitive_types: Array[MClassType] is noinit
 
        # Add a new todo task
        fun todo(m: AbstractRuntimeFunction)
@@ -193,11 +204,28 @@ class GlobalCompiler
                        v.add_decl("{mtype.arguments.first.ctype} values[1];")
                end
 
-               if mtype.ctype != "val*" then
+                if all_routine_types_name.has(mtype.mclass.name) then
+                        v.add_decl("val* recv;")
+                        var c_args = ["val* self"]
+                        var c_ret = "void"
+                        var k = mtype.arguments.length
+                        if mtype.mclass.name.has("Fun") then
+                                c_ret = mtype.arguments.last.ctype
+                                k -= 1
+                        end
+                        for i in [0..k[ do
+                                var t = mtype.arguments[i]
+                                c_args.push("{t.ctype} p{i}")
+                        end
+                        var c_sig = c_args.join(", ")
+                        v.add_decl("{c_ret} (*method)({c_sig});")
+                end
+
+               if mtype.ctype_extern != "val*" then
                        # Is the Nit type is native then the struct is a box with two fields:
                        # * the `classid` to be polymorph
                        # * the `value` that contains the native value.
-                       v.add_decl("{mtype.ctype} value;")
+                       v.add_decl("{mtype.ctype_extern} value;")
                end
 
                # Collect all attributes and associate them a field in the structure.
@@ -217,17 +245,33 @@ class GlobalCompiler
        fun generate_init_instance(mtype: MClassType)
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
-               assert mtype.ctype == "val*"
+               assert not mtype.is_c_primitive
                var v = self.new_visitor
 
                var is_native_array = mtype.mclass.name == "NativeArray"
-
+                var is_routine_ref = all_routine_types_name.has(mtype.mclass.name)
                var sig
                if is_native_array then
                        sig = "int length"
                else
                        sig = "void"
                end
+                if is_routine_ref then
+                        var c_args = ["val* self"]
+                        var c_ret = "void"
+                        var k = mtype.arguments.length
+                        if mtype.mclass.name.has("Fun") then
+                                c_ret = mtype.arguments.last.ctype
+                                k -= 1
+                        end
+                        for i in [0..k[ do
+                                var t = mtype.arguments[i]
+                                c_args.push("{t.ctype} p{i}")
+                        end
+                        # The underlying method signature
+                        var method_sig = "{c_ret} (*method)({c_args.join(", ")})"
+                        sig = "val* recv, {method_sig}"
+                end
 
                self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig});")
                v.add_decl("/* allocate {mtype} */")
@@ -235,12 +279,15 @@ class GlobalCompiler
                var res = v.new_var(mtype)
                res.is_exact = true
                if is_native_array then
-                       var mtype_elt = mtype.arguments.first
-                       v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof(val*));")
                        v.add("((struct {mtype.c_name}*){res})->length = length;")
                else
                        v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}));")
                end
+                if is_routine_ref then
+                       v.add("((struct {mtype.c_name}*){res})->recv = recv;")
+                        v.add("((struct {mtype.c_name}*){res})->method = method;")
+                end
                v.add("{res}->classid = {self.classid(mtype)};")
 
                self.generate_init_attr(v, res, mtype)
@@ -252,7 +299,6 @@ class GlobalCompiler
        fun generate_box_instance(mtype: MClassType)
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
-               assert mtype.ctype != "val*"
                var v = self.new_visitor
 
                self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
@@ -293,16 +339,16 @@ class GlobalCompilerVisitor
        do
                if value.mtype == mtype then
                        return value
-               else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
                        return value
-               else if value.mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive then
                        return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
-               else if mtype.ctype == "val*" then
+               else if not mtype.is_c_primitive then
                        var valtype = value.mtype.as(MClassType)
                        var res = self.new_var(mtype)
                        if 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\"); show_backtrace(1);")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
                                return res
                        end
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
@@ -313,11 +359,39 @@ class GlobalCompilerVisitor
                        # Bad things will appen!
                        var res = self.new_var(mtype)
                        self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
-                       self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
+                       self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); fatal_exit(1);")
+                       return res
+               end
+       end
+
+       redef fun unbox_extern(value, mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind and
+                  mtype.mclass.name != "CString" then
+                       var res = self.new_var_extern(mtype)
+                       self.add "{res} = ((struct {mtype.c_name}*){value})->value; /* unboxing {value.mtype} */"
                        return res
+               else
+                       return value
                end
        end
 
+       redef fun box_extern(value, mtype)
+       do
+               if not mtype isa MClassType or mtype.mclass.kind != extern_kind or
+                       mtype.mclass.name == "CString" then return value
+
+               var valtype = value.mtype.as(MClassType)
+               var res = self.new_var(mtype)
+               if 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
+               self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
+               return res
+       end
+
        # The runtime types that are acceptable for a given receiver.
        fun collect_types(recv: RuntimeVariable): Array[MClassType]
        do
@@ -348,41 +422,106 @@ class GlobalCompilerVisitor
                var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values"
                if pname == "[]" then
                        self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
-                       return
+                       return true
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
-                       return
+                       return true
                else if pname == "length" then
                        self.ret(self.new_expr("((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->length", ret_type.as(not null)))
-                       return
+                       return true
                else if pname == "copy_to" then
                        var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values"
                        self.add("memmove({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));")
-                       return
+                       return true
+               else if pname == "memmove" then
+                       # fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) is intern do
+                       var recv1 = "((struct {arguments[3].mcasttype.c_name}*){arguments[3]})->values"
+                       self.add("memmove({recv1}+{arguments[4]}, {recv}+{arguments[1]}, {arguments[2]}*sizeof({elttype.ctype}));")
+                       return true
                end
+               return false
        end
 
-       redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
+       redef fun native_array_instance(elttype, length)
        do
-               var ret_type = self.get_class("NativeArray").get_mtype([elttype])
-               return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
+               var ret_type = mmodule.native_array_type(elttype)
+               ret_type = anchor(ret_type).as(MClassType)
+               length = autobox(length, compiler.mainmodule.int_type)
+               return self.new_expr("NEW_{ret_type.c_name}((int){length})", ret_type)
        end
 
-       redef fun calloc_array(ret_type, arguments)
+       redef fun native_array_get(nat, i)
        do
-               self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
+               var recv = "((struct {nat.mcasttype.c_name}*){nat})->values"
+               var ret_type = nat.mcasttype.as(MClassType).arguments.first
+               return self.new_expr("{recv}[{i}]", ret_type)
        end
 
+       redef fun native_array_set(nat, i, val)
+       do
+               var recv = "((struct {nat.mcasttype.c_name}*){nat})->values"
+               self.add("{recv}[{i}]={val};")
+       end
+
+        redef fun routine_ref_instance(routine_mclass_type, recv, mmethoddef)
+        do
+                var method = new CustomizedRuntimeFunction(mmethoddef, recv.mcasttype.as(MClassType))
+                var my_recv = recv
+                if recv.mtype.is_c_primitive then
+                        var object_type = mmodule.object_type
+                        my_recv = autobox(recv, object_type)
+                end
+                var thunk = new CustomizedThunkFunction(mmethoddef, my_recv.mtype.as(MClassType))
+                thunk.polymorph_call_flag = not my_recv.is_exact #true
+                #thunk.force_polymorphism = not my_recv.is_exact
+                compiler.todo(method)
+                compiler.todo(thunk)
+
+                var res = self.new_expr("NEW_{routine_mclass_type.c_name}({my_recv}, &{thunk.c_name})", routine_mclass_type)
+                return res
+        end
+
+        redef fun routine_ref_call(mmethoddef, arguments)
+        do
+                var routine = arguments.first
+                var routine_type = routine.mtype.as(MClassType)
+                var routine_class = routine_type.mclass
+                var underlying_recv = "((struct {routine.mcasttype.c_name}*){routine})->recv"
+                var underlying_method = "((struct {routine.mcasttype.c_name}*){routine})->method"
+                adapt_signature(mmethoddef, arguments)
+                arguments.shift
+                var ss = "{underlying_recv}"
+                if arguments.length > 0 then
+                        ss = "{ss}, {arguments.join(", ")}"
+                end
+                arguments.unshift routine
+
+                var ret_mtype = mmethoddef.msignature.return_mtype
+
+                if ret_mtype != null then
+                        # TODO check for separate compiler
+                        ret_mtype = resolve_for(ret_mtype, routine)
+                        # var temp = ret_mtype
+                        # If mmethoddef has a return type, use the type defined
+                        # in the routine instance instead.
+                        #ret_mtype = routine_type.arguments.last
+                end
+                var callsite = "{underlying_method}({ss})"
+                if ret_mtype != null then
+                        var subres = new_expr("{callsite}", ret_mtype)
+                        ret(subres)
+                else
+                        add("{callsite};")
+               end
+        end
+
        redef fun send(m, args)
        do
                var types = self.collect_types(args.first)
 
                var res: nullable RuntimeVariable
                var ret = m.intro.msignature.return_mtype
-               if m.is_new then
-                       ret = args.first.mtype
-                       res = self.new_var(ret)
-               else if ret == null then
+               if ret == null then
                        res = null
                else
                        ret = self.resolve_for(ret, args.first)
@@ -390,9 +529,13 @@ class GlobalCompilerVisitor
                end
 
                self.add("/* send {m} on {args.first.inspect} */")
-               if args.first.mtype.ctype != "val*" then
+               if args.first.mtype.is_c_primitive then
                        var mclasstype = args.first.mtype.as(MClassType)
                        if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
+                               self.add("/* skip, dead class {mclasstype} */")
+                               return res
+                       end
+                       if not mclasstype.has_mproperty(self.compiler.mainmodule, m) then
                                self.add("/* skip, no method {m} */")
                                return res
                        end
@@ -405,7 +548,7 @@ class GlobalCompilerVisitor
                if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then
                        # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException
                        self.add("if ({args.first} == NULL) \{ /* Special null case */")
-                       if m.name == "==" then
+                       if m.name == "==" or m.name == "is_same_instance" then
                                assert res != null
                                if args[1].mcasttype isa MNullableType then
                                        self.add("{res} = ({args[1]} == NULL);")
@@ -441,7 +584,7 @@ class GlobalCompilerVisitor
                var defaultpropdef: nullable MMethodDef = null
                for t in types do
                        var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
-                       if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
+                       if propdef.mclassdef.mclass.name == "Object" and not t.is_c_primitive then
                                defaultpropdef = propdef
                                continue
                        end
@@ -469,7 +612,7 @@ class GlobalCompilerVisitor
        fun check_valid_reciever(recvtype: MClassType)
        do
                if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return
-               print "{recvtype} is not a live type"
+               print_error "{recvtype} is not a live type"
                abort
        end
 
@@ -479,51 +622,24 @@ class GlobalCompilerVisitor
        private fun get_recvtype(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): MClassType
        do
                check_valid_reciever(recvtype)
-               #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}")
-               if m.mproperty.is_toplevel then
-                       # Do not customize top-level methods
-                       recvtype = m.mclassdef.bound_mtype
-               end
                return recvtype
        end
 
-       # Subpart of old call function
-       # Gets the receiver boxed and casted if necessary
-       private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable
+       redef fun call(m, recvtype, args)
        do
-               return self.autoadapt(self.autobox(args.first, recvtype), recvtype)
-       end
+               var recv_type = get_recvtype(m, recvtype, args)
+               var recv = self.autoadapt(self.autobox(args.first, recvtype), recvtype)
+               if m.is_extern then recv = unbox_extern(recv, recv_type)
+
+               args = args.to_a
+               args.first = recv
 
-       # Finalizes a call to a method Â´m´ on type Â´recvtype´ with arguments Â´args´
-       private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
-       do
                assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.")
 
                var rm = new CustomizedRuntimeFunction(m, recvtype)
                return rm.call(self, args)
        end
 
-       redef fun call(m, recvtype, args)
-       do
-               var recv_type = get_recvtype(m, recvtype, args)
-               var recv = get_recv(recv_type, args)
-               var new_args = args.to_a
-               self.varargize(m, m.msignature.as(not null), new_args)
-               new_args.first = recv
-               return finalize_call(m, recv_type, new_args)
-       end
-
-       # Does a call without encapsulating varargs into an array
-       # Avoids multiple encapsulation when calling a super in a variadic function
-       fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
-       do
-               var recv_type = get_recvtype(m, recvtype, args)
-               var recv = get_recv(recv_type, args)
-               var new_args = args.to_a
-               new_args.first = recv
-               return finalize_call(m, recv_type, new_args)
-       end
-
        redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var types = self.collect_types(args.first)
@@ -538,14 +654,14 @@ class GlobalCompilerVisitor
                end
 
                self.add("/* super {m} on {args.first.inspect} */")
-               if args.first.mtype.ctype != "val*" then
+               if args.first.mtype.is_c_primitive then
                        var mclasstype = args.first.mtype.as(MClassType)
                        if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
                                self.add("/* skip, no method {m} */")
                                return res
                        end
                        var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype)
-                       var res2 = self.call_without_varargize(propdef, mclasstype, args)
+                       var res2 = self.call(propdef, mclasstype, args)
                        if res != null then self.assign(res, res2.as(not null))
                        return res
                end
@@ -567,7 +683,7 @@ class GlobalCompilerVisitor
                        else
                                self.add("case {self.compiler.classid(t)}: /* test {t} */")
                        end
-                       var res2 = self.call_without_varargize(propdef, t, args)
+                       var res2 = self.call(propdef, t, args)
                        if res != null then self.assign(res, res2.as(not null))
                        self.add "break;"
                end
@@ -583,8 +699,9 @@ class GlobalCompilerVisitor
        do
                var recv = args.first
                for i in [0..m.msignature.arity[ do
-                       var t = m.msignature.mparameters[i].mtype
-                       if i == m.msignature.vararg_rank then
+                       var mp = m.msignature.mparameters[i]
+                       var t = mp.mtype
+                       if mp.is_vararg then
                                t = args[i+1].mtype
                        end
                        t = self.resolve_for(t, recv)
@@ -592,6 +709,20 @@ class GlobalCompilerVisitor
                end
        end
 
+       redef fun unbox_signature_extern(m, args)
+       do
+               var recv = args.first
+               for i in [0..m.msignature.arity[ do
+                       var mp = m.msignature.mparameters[i]
+                       var t = mp.mtype
+                       if mp.is_vararg then
+                               t = args[i+1].mtype
+                       end
+                       t = self.resolve_for(t, recv)
+                       if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
+               end
+       end
+
        # FIXME: this is currently buggy since recv is not exact
        redef fun vararg_instance(mpropdef, recv, varargs, elttype)
        do
@@ -601,9 +732,9 @@ class GlobalCompilerVisitor
 
        fun bugtype(recv: RuntimeVariable)
        do
-               if recv.mtype.ctype != "val*" then return
+               if recv.mtype.is_c_primitive then return
                self.add("PRINT_ERROR(\"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
-               self.add("show_backtrace(1);")
+               self.add("fatal_exit(1);")
        end
 
        redef fun isset_attribute(a, recv)
@@ -632,7 +763,7 @@ class GlobalCompilerVisitor
                        ta = self.resolve_for(ta, recv2)
                        var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
                        if not ta isa MNullableType then
-                               if ta.ctype == "val*" then
+                               if not ta.is_c_primitive then
                                        self.add("{res} = ({attr} != NULL);")
                                else
                                        self.add("{res} = 1; /*NOTYET isset on primitive attributes*/")
@@ -678,7 +809,7 @@ class GlobalCompilerVisitor
                        ta = self.resolve_for(ta, recv2)
                        var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
                        if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
-                               if ta.ctype == "val*" then
+                               if not ta.is_c_primitive then
                                        self.add("if ({res2} == NULL) \{")
                                        self.add_abort("Uninitialized attribute {a.name}")
                                        self.add("\}")
@@ -754,7 +885,7 @@ class GlobalCompilerVisitor
                var res = self.new_var(bool_type)
 
                self.add("/* isa {mtype} on {value.inspect} */")
-               if value.mtype.ctype != "val*" then
+               if value.mtype.is_c_primitive then
                        if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                                self.add("{res} = 1;")
                        else
@@ -789,14 +920,14 @@ class GlobalCompilerVisitor
        redef fun is_same_type_test(value1, value2)
        do
                var res = self.new_var(bool_type)
-               if value2.mtype.ctype == "val*" then
-                       if value1.mtype.ctype == "val*" then
+               if not value2.mtype.is_c_primitive then
+                       if not value1.mtype.is_c_primitive then
                                self.add "{res} = {value1}->classid == {value2}->classid;"
                        else
                                self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;"
                        end
                else
-                       if value1.mtype.ctype == "val*" then
+                       if not value1.mtype.is_c_primitive then
                                self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};"
                        else if value1.mcasttype == value2.mcasttype then
                                self.add "{res} = 1;"
@@ -811,7 +942,7 @@ class GlobalCompilerVisitor
        do
                var res = self.get_name("var_class_name")
                self.add_decl("const char* {res};")
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        self.add "{res} = class_names[{value}->classid];"
                else
                        self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];"
@@ -822,15 +953,15 @@ class GlobalCompilerVisitor
        redef fun equal_test(value1, value2)
        do
                var res = self.new_var(bool_type)
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+               if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
                        var tmp = value1
                        value1 = value2
                        value2 = tmp
                end
-               if value1.mtype.ctype != "val*" then
+               if value1.mtype.is_c_primitive then
                        if value2.mtype == value1.mtype then
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.ctype != "val*" then
+                       else if value2.mtype.is_c_primitive then
                                self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
                        else
                                var mtype1 = value1.mtype.as(MClassType)
@@ -846,6 +977,15 @@ class GlobalCompilerVisitor
                                if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue
                                s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)"
                        end
+
+                       if self.compiler.mainmodule.model.get_mclasses_by_name("Pointer") != null then
+                               var pointer_type = self.compiler.mainmodule.pointer_type
+                               if value1.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) or
+                                       value2.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) then
+                                       s.add "(((struct {pointer_type.c_name}*){value1})->value == ((struct {pointer_type.c_name}*){value2})->value)"
+                               end
+                       end
+
                        if s.is_empty then
                                self.add("{res} = {value1} == {value2};")
                        else
@@ -858,10 +998,10 @@ class GlobalCompilerVisitor
        redef fun array_instance(array, elttype)
        do
                elttype = self.anchor(elttype)
-               var arraytype = self.get_class("Array").get_mtype([elttype])
+               var arraytype = mmodule.array_type(elttype)
                var res = self.init_instance(arraytype)
                self.add("\{ /* {res} = array_instance Array[{elttype}] */")
-               var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
+               var nat = self.new_var(mmodule.native_array_type(elttype))
                nat.is_exact = true
                self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});")
                for i in [0..array.length[ do
@@ -875,7 +1015,7 @@ class GlobalCompilerVisitor
        end
 end
 
-# A runtime function customized on a specific monomrph receiver type
+# A runtime function customized on a specific monomorph receiver type
 private class CustomizedRuntimeFunction
        super AbstractRuntimeFunction
 
@@ -886,12 +1026,6 @@ private class CustomizedRuntimeFunction
        # (usually is a live type but no strong guarantee)
        var recv: MClassType
 
-       init(mmethoddef: MMethodDef, recv: MClassType)
-       do
-               super(mmethoddef)
-               self.recv = recv
-       end
-
        redef fun build_c_name
        do
                var res = self.c_name_cache
@@ -926,88 +1060,85 @@ private class CustomizedRuntimeFunction
                end
        end
 
-       # compile the code customized for the reciever
-       redef fun compile_to_c(compiler)
-       do
-               var recv = self.recv
-               var mmethoddef = self.mmethoddef
-               if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then
-                       print("problem: why do we compile {self} for {recv}?")
-                       abort
-               end
-
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               if compiler.runtime_type_analysis.live_types.has(recv) then
-                       selfvar.is_exact = true
-               end
-               var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
-               v.frame = frame
-
-               var sig = new FlatBuffer
-               var comment = new FlatBuffer
-               var ret = mmethoddef.msignature.return_mtype
-               if ret != null then
-                       ret = v.resolve_for(ret, selfvar)
-                       sig.append("{ret.ctype} ")
-               else if mmethoddef.mproperty.is_new then
-                       ret = recv
-                       sig.append("{ret.ctype} ")
-               else
-                       sig.append("void ")
-               end
-               sig.append(self.c_name)
-               sig.append("({recv.ctype} {selfvar}")
-               comment.append("(self: {recv}")
-               arguments.add(selfvar)
-               for i in [0..mmethoddef.msignature.arity[ do
-                       var mtype = mmethoddef.msignature.mparameters[i].mtype
-                       if i == mmethoddef.msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
-                       end
-                       mtype = v.resolve_for(mtype, selfvar)
-                       comment.append(", {mtype}")
-                       sig.append(", {mtype.ctype} p{i}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
-               end
-               sig.append(")")
-               comment.append(")")
-               if ret != null then
-                       comment.append(": {ret}")
-               end
-               compiler.header.add_decl("{sig};")
-
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               #v.add("printf(\"method {self} for {comment}\\n\");")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
+        redef fun recv_mtype
+        do
+                return recv
+        end
 
-               mmethoddef.compile_inside_to_c(v, arguments)
+        redef var return_mtype
 
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
+        redef fun resolve_receiver(v)
+        do
+                var selfvar = new RuntimeVariable("self", recv, recv)
+               if v.compiler.runtime_type_analysis.live_types.has(recv) then
+                       selfvar.is_exact = true
                end
-               v.add("\}")
-               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
-       end
+                return selfvar
+        end
+
+        redef fun resolve_return_mtype(v)
+        do
+                var selfvar = v.frame.selfvar
+                if has_return then
+                        var ret = msignature.return_mtype.as(not null)
+                        return_mtype = v.resolve_for(ret, selfvar)
+                end
+        end
+        redef fun resolve_ith_parameter(v, i)
+        do
+                var selfvar = v.frame.selfvar
+                var mp = msignature.mparameters[i]
+                var mtype = mp.mtype
+                if mp.is_vararg then
+                        mtype = v.mmodule.array_type(mtype)
+                end
+                mtype = v.resolve_for(mtype, selfvar)
+                return new RuntimeVariable("p{i}", mtype, mtype)
+        end
+
+        redef fun declare_signature(v, sig)
+        do
+                v.compiler.header.add_decl("{sig};")
+        end
+
+        redef fun end_compile_to_c(v)
+        do
+               if not self.c_name.has_substring("VIRTUAL", 0) then v.compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
+        end
 
        redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var ret = self.mmethoddef.msignature.return_mtype
-               if self.mmethoddef.mproperty.is_new then
-                       ret = recv
-               end
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
                end
-               if self.mmethoddef.can_inline(v) then
-                       var frame = new Frame(v, self.mmethoddef, self.recv, arguments)
+
+                # TODO: remove this guard when gcc warning issue (#2781) is resolved
+                # WARNING: the next two lines of code is used to prevent inlining.
+                # Inlining of a callref seems to work all the time. However,
+                # it will produce some deadcode in certain scenarios (when using nullable type).
+                #
+                # ~~~~nitish
+                # class A[E]
+                #       fun toto(x: E)
+                #       do
+                #               ...do something with x...
+                #       end
+                # end
+                # end
+                # var a = new A[nullable Int]
+                # var f = &a.toto
+                # f.call(null) <-- Will produce a proper C callsite, but it will
+                #               -- produce unreachable (dead code) for type checking
+                #               -- and covariance. Thus, creating warnings when
+                #               -- compiling in global. However, if you ignore
+                #               -- those warnings, the binary works perfectly fine.
+                # ~~~~
+                var intromclassdef = self.mmethoddef.mproperty.intro_mclassdef
+                var is_callref = v.compiler.all_routine_types_name.has(intromclassdef.name)
+
+                if self.mmethoddef.can_inline(v) and not is_callref then
+                       var frame = new StaticFrame(v, self.mmethoddef, self.recv, arguments)
                        frame.returnlabel = v.get_name("RET_LABEL")
                        if ret != null then
                                frame.returnvar = v.new_var(ret)
@@ -1033,3 +1164,67 @@ private class CustomizedRuntimeFunction
                end
        end
 end
+
+class CustomizedThunkFunction
+        super ThunkFunction
+        super CustomizedRuntimeFunction
+
+        #var force_polymorphism = false
+
+        redef fun c_name
+        do
+                return "THUNK_" + super
+        end
+
+        redef fun hash
+        do
+                return super + c_name.hash
+        end
+
+        redef fun resolve_receiver(v)
+        do
+                var res = super(v)
+                if res.is_exact then res.is_exact = not polymorph_call_flag
+                return res
+        end
+
+        redef fun target_recv
+        do
+                # If the targeted method was introduced by a primitive type,
+                # then target_recv must be set to it. Otherwise, there will
+                # be a missing cast. Here's an example:
+                #
+                # ~~~~nitish
+                # class Int
+                #       fun mult_by(x:Int):Int do return x * self
+                # end
+                #
+                # var f = &10.mult_by
+                # ~~~~
+                # Here the thunk `f` must box the receiver `10` into an object.
+                # This is due to the memory representation of a call ref which
+                # has a pointer to an opaque type `val*`:
+                #
+                # ```C
+                # struct Mult_by_callref_struct {
+                #       classid;
+                #       // The receiver `10` would be here
+                #       val* recv;
+                #       // the targeted receiver is a `long`
+                #       long (*pointer_to_mult_by)(long, long);
+                # }
+                # ```
+                #
+                # Thus, every primitive type must be boxed into an `Object` when
+                # instantiating a callref.
+                #
+                # However, if the underlying method was introduced by a primitive
+                # type then a cast must be invoked to convert our boxed receiver
+                # to its original primitive type.
+                var intro_recv = mmethoddef.mproperty.intro_mclassdef.bound_mtype
+                if intro_recv.is_c_primitive then
+                        return intro_recv
+                end
+                return recv_mtype
+        end
+end