Merge: Callref compilers
authorJean Privat <jean@pryen.org>
Fri, 25 Oct 2019 21:09:46 +0000 (17:09 -0400)
committerJean Privat <jean@pryen.org>
Fri, 25 Oct 2019 21:09:46 +0000 (17:09 -0400)
Pull-Request: #2793
Reviewed-by: Jean Privat <jean@pryen.org>

13 files changed:
src/compiler/abstract_compiler.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/interpreter/naive_interpreter.nit
src/rapid_type_analysis.nit
src/semantize/typing.nit
tests/sav/syntax_callref.res
tests/sav/syntax_callref_alt1.res
tests/sav/test_callref.res [new file with mode: 0644]
tests/syntax_callref.nit
tests/test_callref.nit [new file with mode: 0644]
tests/testall.sh

index b512cdd..77a20f4 100644 (file)
@@ -625,6 +625,20 @@ abstract class AbstractCompiler
        # The targeted specific platform
        var target_platform: Platform is noinit
 
+       # All methods who already has a callref_thunk generated for
+       var compiled_callref_thunk = new HashSet[MMethodDef]
+
+       var all_routine_types_name: Set[String] do
+               var res = new HashSet[String]
+               for name in ["Fun", "Proc", "FunRef", "ProcRef"] do
+                       # Currently there's 20 arity per func type
+                       for i in [0..20[ do
+                               res.add("{name}{i}")
+                       end
+               end
+               return res
+       end
+
        init
        do
                self.realmainmodule = mainmodule
@@ -1377,6 +1391,12 @@ abstract class AbstractCompilerVisitor
        # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
        fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
 
+       # Instantiate a new routine pointer
+       fun routine_ref_instance(routine_mclass_type: MClassType, recv: RuntimeVariable, callsite: CallSite): RuntimeVariable is abstract
+
+       # Call the underlying referenced function
+       fun routine_ref_call(mmethoddef: MMethodDef, args: Array[RuntimeVariable]) is abstract
+
        # Allocate `size` bytes with the low_level `nit_alloc` C function
        #
        # This method can be redefined to inject statistic or tracing code.
@@ -2084,7 +2104,7 @@ abstract class AbstractRuntimeFunction
        # The associated Nit method
        var mmethoddef: MMethodDef
 
-        protected var return_mtype: nullable MType = null
+       protected var return_mtype: nullable MType = null
 
        # The mangled c name of the runtime_function
        # Subclasses should redefine `build_c_name` instead
@@ -2097,7 +2117,7 @@ abstract class AbstractRuntimeFunction
                return res
        end
 
-        fun c_ref: String do return "&{c_name}"
+       fun c_ref: String do return "&{c_name}"
 
        # Non cached version of `c_name`
        protected fun build_c_name: String is abstract
@@ -2108,162 +2128,162 @@ abstract class AbstractRuntimeFunction
        # May inline the body or generate a C function call
        fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
 
-        # Returns `true` if the associated `mmethoddef`'s return type isn't null,
-        # otherwise `false`.
-        fun has_return: Bool
-        do
-                return mmethoddef.msignature.return_mtype != null
-        end
-
-        # The current msignature to use when compiling : `signature_to_c` and `body_to_c`.
-        # This method is useful since most concrete implementation doesn't use the
-        # mmethoddef's signature. By providing a definition in the abstract class,
-        # subclasses can use any msignature.
-        fun msignature: MSignature
-        do
-                return mmethoddef.msignature.as(not null)
-        end
-
-        # The current receiver type to compile : `signature_to_c` and `body_to_c`.
-        # See `msignature` method for more information.
-        protected fun recv_mtype: MType
-        do
-                return mmethoddef.mclassdef.bound_mtype
-        end
-
-        # Prepare the `self` runtime variable to be used by the rest of
-        # compilation steps.
-        # Step 1
-        protected fun resolve_receiver(v: VISITOR): RuntimeVariable
-        do
-                var casttype = mmethoddef.mclassdef.bound_mtype
-                return new RuntimeVariable("self", recv_mtype, casttype)
-        end
-
-        # Builds the static frame for current runtime method
-        # Step 2
-        protected fun build_frame(v: VISITOR, arguments: Array[RuntimeVariable]): StaticFrame
-        do
-                return new StaticFrame(v, mmethoddef, recv_mtype.as(MClassType), arguments)
-        end
-
-        # Step 3 : Returns the return type used by the runtime function.
-        protected fun resolve_return_mtype(v: VISITOR)
-        do
-                return_mtype = msignature.return_mtype
-        end
-
-        # Fills the argument array inside v.frame.arguments, calling `resolve_ith_parameter`
-        # for each parameter.
-        private fun fill_parameters(v: VISITOR)
-        do
-                assert v.frame != null
-                for i in [0..msignature.arity[ do
-                        var arg = resolve_ith_parameter(v, i)
+       # Returns `true` if the associated `mmethoddef`'s return type isn't null,
+       # otherwise `false`.
+       fun has_return: Bool
+       do
+               return mmethoddef.msignature.return_mtype != null
+       end
+
+       # The current msignature to use when compiling : `signature_to_c` and `body_to_c`.
+       # This method is useful since most concrete implementation doesn't use the
+       # mmethoddef's signature. By providing a definition in the abstract class,
+       # subclasses can use any msignature.
+       fun msignature: MSignature
+       do
+               return mmethoddef.msignature.as(not null)
+       end
+
+       # The current receiver type to compile : `signature_to_c` and `body_to_c`.
+       # See `msignature` method for more information.
+       protected fun recv_mtype: MType
+       do
+               return mmethoddef.mclassdef.bound_mtype
+       end
+
+       # Prepare the `self` runtime variable to be used by the rest of
+       # compilation steps.
+       # Step 1
+       protected fun resolve_receiver(v: VISITOR): RuntimeVariable
+       do
+               var casttype = mmethoddef.mclassdef.bound_mtype
+               return new RuntimeVariable("self", recv_mtype, casttype)
+       end
+
+       # Builds the static frame for current runtime method
+       # Step 2
+       protected fun build_frame(v: VISITOR, arguments: Array[RuntimeVariable]): StaticFrame
+       do
+               return new StaticFrame(v, mmethoddef, recv_mtype.as(MClassType), arguments)
+       end
+
+       # Step 3 : Returns the return type used by the runtime function.
+       protected fun resolve_return_mtype(v: VISITOR)
+       do
+               return_mtype = msignature.return_mtype
+       end
+
+       # Fills the argument array inside v.frame.arguments, calling `resolve_ith_parameter`
+       # for each parameter.
+       private fun fill_parameters(v: VISITOR)
+       do
+               assert v.frame != null
+               for i in [0..msignature.arity[ do
+                       var arg = resolve_ith_parameter(v, i)
                        v.frame.arguments.add(arg)
                end
-        end
+       end
 
-        # Step 4 : Creates `RuntimeVariable` for each method argument.
-        protected fun resolve_ith_parameter(v: VISITOR, i: Int): RuntimeVariable
-        do
-                var mp = msignature.mparameters[i]
-                var mtype = mp.mtype
-                if mp.is_vararg then
+       # Step 4 : Creates `RuntimeVariable` for each method argument.
+       protected fun resolve_ith_parameter(v: VISITOR, i: Int): RuntimeVariable
+       do
+               var mp = msignature.mparameters[i]
+               var mtype = mp.mtype
+               if mp.is_vararg then
                        mtype = v.mmodule.array_type(mtype)
                end
-                return new RuntimeVariable("p{i}", mtype, mtype)
-        end
-
-        # Generate the code for the signature with an open curly brace
-        #
-        # Returns the generated signature without a semicolon and curly brace,
-        # e.g `RES f(T0 p0, T1 p1, ..., TN pN)`
-        # Step 5
-        protected fun signature_to_c(v: VISITOR): String
-        do
-                assert v.frame != null
-                var arguments = v.frame.arguments
-                var comment = new FlatBuffer
-                var selfvar = v.frame.selfvar
-                var c_ret = "void"
-                if has_return then
-                        c_ret = "{return_mtype.ctype}"
-                end
-                var sig = new FlatBuffer
-                sig.append("{c_ret} {c_name}({recv_mtype.ctype} self")
+               return new RuntimeVariable("p{i}", mtype, mtype)
+       end
+
+       # Generate the code for the signature with an open curly brace
+       #
+       # Returns the generated signature without a semicolon and curly brace,
+       # e.g `RES f(T0 p0, T1 p1, ..., TN pN)`
+       # Step 5
+       protected fun signature_to_c(v: VISITOR): String
+       do
+               assert v.frame != null
+               var arguments = v.frame.arguments
+               var comment = new FlatBuffer
+               var selfvar = v.frame.selfvar
+               var c_ret = "void"
+               if has_return then
+                       c_ret = "{return_mtype.ctype}"
+               end
+               var sig = new FlatBuffer
+               sig.append("{c_ret} {c_name}({recv_mtype.ctype} self")
                comment.append("({selfvar}: {selfvar.mtype}")
 
-                for i in [0..arguments.length-1[ do
-                        # Skip the receiver
-                        var arg = arguments[i+1]
+               for i in [0..arguments.length-1[ do
+                       # Skip the receiver
+                       var arg = arguments[i+1]
                        comment.append(", {arg.mtype}")
-                        sig.append(", {arg.mtype.ctype} p{i}")
+                       sig.append(", {arg.mtype.ctype} p{i}")
                end
-                sig.append(")")
+               sig.append(")")
                comment.append(")")
-                if has_return then
-                        comment.append(": {return_mtype.as(not null)}")
-                end
-                v.add_decl("/* method {self} for {comment} */")
-                v.add_decl("{sig} \{")
-                return sig.to_s
-        end
-
-        # How the concrete compiler will declare the method, e.g inside a global header file,
-        # extern signature, etc.
-        # Step 6
-        protected fun declare_signature(v: VISITOR, signature: String) is abstract
-
-        # Generate the code for the body without return statement at the end and
-        # no curly brace.
-        # Step 7
-        protected fun body_to_c(v: VISITOR)
-        do
-                mmethoddef.compile_inside_to_c(v, v.frame.arguments)
-        end
-
-        # Hook called at the end of `compile_to_c` function. This function
-        # is useful if you need to register any function compiled to c.
-        # Step 8 (optional).
-        protected fun end_compile_to_c(v: VISITOR)
-        do
-                # Nothing to do by default
-        end
+               if has_return then
+                       comment.append(": {return_mtype.as(not null)}")
+               end
+               v.add_decl("/* method {self} for {comment} */")
+               v.add_decl("{sig} \{")
+               return sig.to_s
+       end
+
+       # How the concrete compiler will declare the method, e.g inside a global header file,
+       # extern signature, etc.
+       # Step 6
+       protected fun declare_signature(v: VISITOR, signature: String) is abstract
+
+       # Generate the code for the body without return statement at the end and
+       # no curly brace.
+       # Step 7
+       protected fun body_to_c(v: VISITOR)
+       do
+               mmethoddef.compile_inside_to_c(v, v.frame.arguments)
+       end
+
+       # Hook called at the end of `compile_to_c` function. This function
+       # is useful if you need to register any function compiled to c.
+       # Step 8 (optional).
+       protected fun end_compile_to_c(v: VISITOR)
+       do
+               # Nothing to do by default
+       end
 
        # Generate the code
-        fun compile_to_c(compiler: COMPILER)
-        do
-                var v = compiler.new_visitor
-                var selfvar = resolve_receiver(v)
-                var arguments = [selfvar]
-                var frame = build_frame(v, arguments)
-                v.frame = frame
-
-                resolve_return_mtype(v)
-                fill_parameters(v)
-
-                # WARNING: the signature must be declared before creating
-                # any returnlabel and returnvar (`StaticFrame`). Otherwise,
-                # you could end up with variable outside the function.
-                var sig = signature_to_c(v)
-                declare_signature(v, sig)
-
-                frame.returnlabel = v.get_name("RET_LABEL")
-                if has_return then
-                        var ret_mtype = return_mtype
-                        assert ret_mtype != null
-                        frame.returnvar = v.new_var(ret_mtype)
-                end
-
-                body_to_c(v)
-                v.add("{frame.returnlabel.as(not null)}:;")
+       fun compile_to_c(compiler: COMPILER)
+       do
+               var v = compiler.new_visitor
+               var selfvar = resolve_receiver(v)
+               var arguments = [selfvar]
+               var frame = build_frame(v, arguments)
+               v.frame = frame
+
+               resolve_return_mtype(v)
+               fill_parameters(v)
+
+               # WARNING: the signature must be declared before creating
+               # any returnlabel and returnvar (`StaticFrame`). Otherwise,
+               # you could end up with variable outside the function.
+               var sig = signature_to_c(v)
+               declare_signature(v, sig)
+
+               frame.returnlabel = v.get_name("RET_LABEL")
+               if has_return then
+                       var ret_mtype = return_mtype
+                       assert ret_mtype != null
+                       frame.returnvar = v.new_var(ret_mtype)
+               end
+
+               body_to_c(v)
+               v.add("{frame.returnlabel.as(not null)}:;")
                if has_return then
                        v.add("return {frame.returnvar.as(not null)};")
                end
-                v.add "\}"
-                end_compile_to_c(v)
-        end
+               v.add "\}"
+               end_compile_to_c(v)
+       end
 end
 
 # Base class for all thunk-like function. A thunk is a function whose purpose
@@ -2303,48 +2323,48 @@ end
 # its receiver is of type `Object`.
 # In the same vibe, a call reference has all of its argument boxed as `Object`.
 abstract class ThunkFunction
-        super AbstractRuntimeFunction
-
-        # Determines if the callsite should be polymorphic or static.
-        var polymorph_call_flag = false is writable
-
-        # The type expected by the callee. Used to resolve `mmethoddef`'s formal
-        # parameters and virtual type. This type must NOT need anchor.
-        fun target_recv: MType is abstract
-
-        redef fun body_to_c(v)
-        do
-                assert not target_recv.need_anchor
-                var frame = v.frame
-                assert frame != null
-                var selfvar = frame.selfvar
-                var arguments = frame.arguments
-                var arguments2 = new Array[RuntimeVariable]
-                arguments2.push(v.autobox(selfvar, target_recv))
-                var resolved_sig = msignature.resolve_for(target_recv, target_recv.as(MClassType), v.mmodule, true)
-                for i in [0..resolved_sig.arity[ do
-                var param = resolved_sig.mparameters[i]
-                        var mtype = param.mtype
-                        if param.is_vararg then
+       super AbstractRuntimeFunction
+
+       # Determines if the callsite should be polymorphic or static.
+       var polymorph_call_flag = false is writable
+
+       # The type expected by the callee. Used to resolve `mmethoddef`'s formal
+       # parameters and virtual type. This type must NOT need anchor.
+       fun target_recv: MType is abstract
+
+       redef fun body_to_c(v)
+       do
+               assert not target_recv.need_anchor
+               var frame = v.frame
+               assert frame != null
+               var selfvar = frame.selfvar
+               var arguments = frame.arguments
+               var arguments2 = new Array[RuntimeVariable]
+               arguments2.push(v.autobox(selfvar, target_recv))
+               var resolved_sig = msignature.resolve_for(target_recv, target_recv.as(MClassType), v.mmodule, true)
+               for i in [0..resolved_sig.arity[ do
+               var param = resolved_sig.mparameters[i]
+                       var mtype = param.mtype
+                       if param.is_vararg then
                                mtype = v.mmodule.array_type(mtype)
                        end
-                        var temp = v.autobox(arguments[i+1], mtype)
-                        arguments2.push(temp)
-                end
-                v.add("/* {mmethoddef}, {recv_mtype.ctype} */")
-                var subret: nullable RuntimeVariable = null
-                if polymorph_call_flag then
-                        subret = v.send(mmethoddef.mproperty, arguments2)
-                else
-                        subret = v.call(mmethoddef, arguments2[0].mcasttype.as(MClassType), arguments2)
-                end
-                if has_return then
-                        assert subret != null
-                        var subret2 = v.autobox(subret, return_mtype.as(not null))
-                        v.assign(frame.returnvar.as(not null), subret2)
-                end
-
-        end
+                       var temp = v.autobox(arguments[i+1], mtype)
+                       arguments2.push(temp)
+               end
+               v.add("/* {mmethoddef}, {recv_mtype.ctype} */")
+               var subret: nullable RuntimeVariable = null
+               if polymorph_call_flag then
+                       subret = v.send(mmethoddef.mproperty, arguments2)
+               else
+                       subret = v.call(mmethoddef, arguments2[0].mcasttype.as(MClassType), arguments2)
+               end
+               if has_return then
+                       assert subret != null
+                       var subret2 = v.autobox(subret, return_mtype.as(not null))
+                       v.assign(frame.returnvar.as(not null), subret2)
+               end
+
+       end
 
 end
 
@@ -2423,13 +2443,13 @@ class StaticFrame
        # The array comprehension currently filled, if any
        private var comprehension: nullable RuntimeVariable = null
 
-        # Returns the first argument (the receiver) of a frame.
-        # REQUIRE: arguments.length >= 1
-        fun selfvar: RuntimeVariable
-        do
-                assert arguments.length >= 1
-                return arguments.first
-        end
+       # Returns the first argument (the receiver) of a frame.
+       # REQUIRE: arguments.length >= 1
+       fun selfvar: RuntimeVariable
+       do
+               assert arguments.length >= 1
+               return arguments.first
+       end
 end
 
 redef class MType
@@ -2689,9 +2709,16 @@ redef class AMethPropdef
                var pname = mpropdef.mproperty.name
                var cname = mpropdef.mclassdef.mclass.name
                var ret = mpropdef.msignature.return_mtype
-               if ret != null then
+               var compiler = v.compiler
+
+               # WARNING: we must not resolve the return type when it's a functional type.
+               # Otherwise, we get a compile error exactly here. Moreover, `routine_ref_call`
+               # already handles the return type. NOTE: this warning only apply when compiling
+               # in `semi-global`.
+               if ret != null and not compiler.all_routine_types_name.has(cname) then
                        ret = v.resolve_for(ret, arguments.first)
                end
+
                if pname != "==" and pname != "!=" then
                        v.adapt_signature(mpropdef, arguments)
                        v.unbox_signature_extern(mpropdef, arguments)
@@ -3455,6 +3482,9 @@ redef class AMethPropdef
                                v.ret(v.new_expr("~{arguments[0]}", ret.as(not null)))
                                return true
                        end
+               else if compiler.all_routine_types_name.has(cname) then
+                       v.routine_ref_call(mpropdef, arguments)
+                       return true
                end
                if pname == "exit" then
                        v.add("exit((int){arguments[1]});")
@@ -4373,11 +4403,12 @@ redef class ASendExpr
 end
 
 redef class ACallrefExpr
-        redef fun expr(v)
-        do
-                v.add_abort("NOT YET IMPLEMENTED callref expressions.")
-                return null
-        end
+       redef fun expr(v)
+       do
+               var recv = v.expr(self.n_expr, null)
+               var res = v.routine_ref_instance(mtype.as(MClassType), recv, callsite.as(not null))
+               return res
+       end
 end
 
 redef class ASendReassignFormExpr
index ce61657..ac5a2a1 100644 (file)
@@ -204,6 +204,23 @@ class GlobalCompiler
                        v.add_decl("{mtype.arguments.first.ctype} values[1];")
                end
 
+                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
@@ -232,13 +249,29 @@ class GlobalCompiler
                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} */")
@@ -251,6 +284,10 @@ class GlobalCompiler
                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)
@@ -426,6 +463,53 @@ class GlobalCompilerVisitor
                self.add("{recv}[{i}]={val};")
        end
 
+        redef fun routine_ref_instance(routine_mclass_type, recv, callsite)
+        do
+               var mmethoddef = callsite.mpropdef
+                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
+                compiler.todo(method)
+                compiler.todo(thunk)
+               var ret_type = self.anchor(routine_mclass_type).as(MClassType)
+                var res = self.new_expr("NEW_{ret_type.c_name}({my_recv}, &{thunk.c_name})", ret_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
+                        ret_mtype = resolve_for(ret_mtype, routine)
+                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)
@@ -1023,7 +1107,32 @@ private class CustomizedRuntimeFunction
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
                end
-               if self.mmethoddef.can_inline(v) then
+
+                # 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
@@ -1050,3 +1159,67 @@ private class CustomizedRuntimeFunction
                end
        end
 end
+
+# Thunk implementation for global compiler.
+# For more detail see `abstract_compiler::ThunkFunction` documentation.
+class CustomizedThunkFunction
+        super ThunkFunction
+        super CustomizedRuntimeFunction
+
+        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
index f9d1444..4716dae 100644 (file)
@@ -149,6 +149,7 @@ class SeparateCompiler
        private var type_ids: Map[MType, Int] is noinit
        private var type_colors: Map[MType, Int] is noinit
        private var opentype_colors: Map[MType, Int] is noinit
+       private var thunks_to_compile: Set[SeparateRuntimeFunction] = new HashSet[SeparateRuntimeFunction]
 
        init do
                var file = new_file("nit.common")
@@ -195,6 +196,15 @@ class SeparateCompiler
                compiler.compile_types
        end
 
+       fun thunk_todo(thunk: SeparateRuntimeFunction)
+       do
+               # Concrete instance of `SeparateRuntimeFunction` are already
+               # handled by the compiler. Avoid duplicate compilation.
+               if thunk isa SeparateThunkFunction then
+                       thunks_to_compile.add(thunk)
+               end
+       end
+
        # Color and compile type structures and cast information
        fun compile_types
        do
@@ -253,7 +263,7 @@ class SeparateCompiler
                # Collect all bas box class
                # FIXME: this is not completely fine with a separate compilation scheme
                for classname in ["Int", "Bool", "Byte", "Char", "Float", "CString",
-                                "Pointer", "Int8", "Int16", "UInt16", "Int32", "UInt32"] do
+                                "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_error classes.join(", ")
@@ -274,7 +284,6 @@ class SeparateCompiler
                else
                        return self.box_kinds[mclass]
                end
-
        end
 
        fun compile_color_consts(colors: Map[Object, Int]) do
@@ -414,7 +423,6 @@ class SeparateCompiler
                        attr_tables[mclass] = attr_colorer.build_layout(mclass)
                end
 
-
        end
 
        # colorize live types of the program
@@ -506,7 +514,6 @@ class SeparateCompiler
                return tables
        end
 
-
        private fun compute_type_test_layouts(mtypes: Set[MClassType], cast_types: Set[MType]) do
                # Group cast_type by their classes
                var bucklets = new HashMap[MClass, Set[MType]]
@@ -641,6 +648,15 @@ class SeparateCompiler
                                end
                        end
                end
+               var compiled_thunks = new Array[SeparateRuntimeFunction]
+               # Compile thunks here to write them in the same module they are declared.
+               for thunk in thunks_to_compile do
+                       if thunk.mmethoddef.mclassdef.mmodule == mmodule then
+                               thunk.compile_to_c(self)
+                               compiled_thunks.add(thunk)
+                       end
+               end
+               thunks_to_compile.remove_all(compiled_thunks)
                self.mainmodule = old_module
        end
 
@@ -938,6 +954,29 @@ class SeparateCompiler
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
+               else if mclass.name == "RoutineRef" then
+                       self.header.add_decl("struct instance_{c_name} \{")
+                       self.header.add_decl("const struct type *type;")
+                       self.header.add_decl("const struct class *class;")
+                       self.header.add_decl("val* recv;")
+                       self.header.add_decl("nitmethod_t method;")
+                       self.header.add_decl("\};")
+
+                       self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class, const struct type* type);")
+                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class, const struct type* type)\{")
+                       var res = v.get_name("self")
+                       v.add_decl("struct instance_{c_name} *{res};")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
+                       v.add("{res} = {alloc};")
+                       v.add("{res}->type = type;")
+                       hardening_live_type(v, "type")
+                       v.add("{res}->class = class;")
+                       v.add("{res}->recv = recv;")
+                       v.add("{res}->method = method;")
+                       v.add("return (val*){res};")
+                       v.add("\}")
+                       return
                else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
                        # Is an extern class (other than Pointer and CString)
                        # Pointer is caught in a previous `if`, and CString is internal
@@ -2149,6 +2188,127 @@ class SeparateCompilerVisitor
                self.add("{recv}[{i}]={val};")
        end
 
+       redef fun routine_ref_instance(routine_type, recv, callsite)
+       do
+               #debug "ENTER ref_instance"
+               var mmethoddef = callsite.mpropdef
+               var mmethod = mmethoddef.mproperty
+               # routine_mclass is the specialized one, e.g: FunRef1, ProcRef2, etc..
+               var routine_mclass = routine_type.mclass
+
+               var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
+               var base_routine_mclass = nclasses.first
+
+               # All routine classes use the same `NEW` constructor.
+               # However, they have different declared `class` and `type` value.
+               self.require_declaration("NEW_{base_routine_mclass.c_name}")
+
+               var recv_class_cname = recv.mcasttype.as(MClassType).mclass.c_name
+               var my_recv = recv
+
+               if recv.mtype.is_c_primitive then
+                       my_recv = autobox(recv, mmodule.object_type)
+               end
+               var my_recv_mclass_type = my_recv.mtype.as(MClassType)
+
+               # The class of the concrete Routine must exist (e.g ProcRef0, FunRef0, etc.)
+               self.require_declaration("class_{routine_mclass.c_name}")
+               self.require_declaration(mmethoddef.c_name)
+
+               var thunk_function = mmethoddef.callref_thunk(my_recv_mclass_type)
+               # If the receiver is exact, then there's no need to make a
+               # polymorph call to the underlying method.
+               thunk_function.polymorph_call_flag = not my_recv.is_exact
+               var runtime_function = mmethoddef.virtual_runtime_function
+
+               var is_c_equiv = runtime_function.msignature.c_equiv(thunk_function.msignature)
+
+               var c_ref = thunk_function.c_ref
+               if is_c_equiv then
+                       var const_color = mmethoddef.mproperty.const_color
+                       c_ref = "{class_info(my_recv)}->vft[{const_color}]"
+                       self.require_declaration(const_color)
+               else
+                       self.require_declaration(thunk_function.c_name)
+                       compiler.thunk_todo(thunk_function)
+               end
+               var res: RuntimeVariable
+               if routine_type.need_anchor then
+                       hardening_live_open_type(routine_type)
+                       link_unresolved_type(self.frame.mpropdef.mclassdef, routine_type)
+                       var recv2 = self.frame.arguments.first
+                       var recv2_type_info = self.type_info(recv2)
+                       self.require_declaration(routine_type.const_color)
+                       res = self.new_expr("NEW_{base_routine_mclass.c_name}({my_recv}, (nitmethod_t){c_ref}, &class_{routine_mclass.c_name}, {recv2_type_info}->resolution_table->types[{routine_type.const_color}])", routine_type)
+               else
+                       self.require_declaration("type_{routine_type.c_name}")
+                       compiler.undead_types.add(routine_type)
+                       res = self.new_expr("NEW_{base_routine_mclass.c_name}({my_recv}, (nitmethod_t){c_ref}, &class_{routine_mclass.c_name}, &type_{routine_type.c_name})", routine_type)
+               end
+               return res
+       end
+
+       redef fun routine_ref_call(mmethoddef, arguments)
+       do
+               #debug "ENTER ref_call"
+               compiler.modelbuilder.nb_invok_by_tables += 1
+               if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
+               var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
+               var nclass = nclasses.first
+               var runtime_function = mmethoddef.virtual_runtime_function
+
+               # Save the current receiver since adapt_signature will autobox
+               # the routine receiver which is not the underlying receiver.
+               # The underlying receiver has already been adapted in the
+               # `routine_ref_instance` method. Here we just want to adapt the
+               # rest of the signature, but it's easier to pass the wrong
+               # receiver in adapt_signature then discards it with `shift`.
+               #
+               # ~~~~nitish
+               # class A; def toto do print "toto"; end
+               # var a = new A
+               # var f = &a.toto # `a` is the underlying receiver
+               # f.call # here `f` is the routine receiver
+               # ~~~~
+               var routine = arguments.first
+
+               # Retrieve the concrete routine type
+               var original_recv_c = "(((struct instance_{nclass.c_name}*){arguments[0]})->recv)"
+               var nitmethod = "(({runtime_function.c_funptrtype})(((struct instance_{nclass.c_name}*){arguments[0]})->method))"
+               if arguments.length > 1 then
+                       adapt_signature(mmethoddef, arguments)
+               end
+
+               var ret_mtype = runtime_function.called_signature.return_mtype
+
+               if ret_mtype != null then
+                       # `ret` is actually always nullable Object. When invoking
+                       # a callref, we don't have the original callsite information.
+                       # Thus, we need to recompute the return type of the callsite.
+                       ret_mtype = resolve_for(ret_mtype, routine)
+               end
+
+               # remove the routine's receiver
+               arguments.shift
+               var ss = arguments.join(", ")
+               # replace the receiver with the original one
+               if arguments.length > 0 then
+                       ss = "{original_recv_c}, {ss}"
+               else
+                       ss = original_recv_c
+               end
+
+               arguments.unshift routine # put back the routine ref receiver
+               add "/* {mmethoddef.mproperty} on {arguments.first.inspect}*/"
+               var callsite = "{nitmethod}({ss})"
+               if ret_mtype != null then
+                       var subres = new_expr("{callsite}", ret_mtype)
+                       ret(subres)
+               else
+                       add("{callsite};")
+               end
+       end
+
        fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
                assert mtype.need_anchor
                var compiler = self.compiler
@@ -2172,6 +2332,43 @@ redef class MMethodDef
                end
                return res
        end
+
+       # Returns true if the current method definition differ from
+       # its original introduction in terms of receiver type.
+       fun recv_differ_from_intro: Bool
+       do
+               var intromclassdef = mproperty.intro.mclassdef
+               var introrecv = intromclassdef.bound_mtype
+               return self.mclassdef.bound_mtype != introrecv
+       end
+
+       # The C thunk function associated to a mmethoddef. Receives only nullable
+       # Object and cast them to the original mmethoddef signature.
+       fun callref_thunk(recv_mtype: MClassType): SeparateThunkFunction
+       do
+               var res = callref_thunk_cache
+               if res == null then
+                       var object_type = mclassdef.mmodule.object_type
+                       var nullable_object = object_type.as_nullable
+                       var ps = new Array[MParameter]
+
+                       # Replace every argument type by nullable object
+                       for p in msignature.mparameters do
+                               ps.push(new MParameter(p.name, nullable_object, p.is_vararg))
+                       end
+                       var ret: nullable MType = null
+                       if msignature.return_mtype != null then ret = nullable_object
+                       var msignature2 = new MSignature(ps, ret)
+                       var intromclassdef = mproperty.intro.mclassdef
+
+                       res = new SeparateThunkFunction(self, recv_mtype, msignature2, "THUNK_{c_name}", mclassdef.bound_mtype)
+                       res.polymorph_call_flag = true
+                       callref_thunk_cache = res
+               end
+               return res
+       end
+
+       private var callref_thunk_cache: nullable SeparateThunkFunction
        private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
 
        # The C function associated to a mmethoddef, that can be stored into a VFT of a class
@@ -2197,7 +2394,7 @@ redef class MMethodDef
                                self.virtual_runtime_function_cache = res
                                return res
                        end
-                        res = new SeparateThunkFunction(self, recv, msignature, "VIRTUAL_{c_name}", mclassdef.bound_mtype)
+                       res = new SeparateThunkFunction(self, recv, msignature, "VIRTUAL_{c_name}", mclassdef.bound_mtype)
                end
                return res
        end
@@ -2236,20 +2433,20 @@ class SeparateRuntimeFunction
 
        redef fun to_s do return self.mmethoddef.to_s
 
-        redef fun msignature
-        do
-                return called_signature
-        end
+       redef fun msignature
+       do
+               return called_signature
+       end
 
-        redef fun recv_mtype
-        do
-                return called_recv
-        end
+       redef fun recv_mtype
+       do
+               return called_recv
+       end
 
-        redef fun return_mtype
-        do
-                return called_signature.return_mtype
-        end
+       redef fun return_mtype
+       do
+               return called_signature.return_mtype
+       end
 
        # The C return type (something or `void`)
        var c_ret: String is lazy do
@@ -2280,33 +2477,32 @@ class SeparateRuntimeFunction
        # The C type for the function pointer.
        var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
-        redef fun declare_signature(v, sig)
-        do
-                v.compiler.provide_declaration(c_name, "{sig};")
-        end
+       redef fun declare_signature(v, sig)
+       do
+               v.compiler.provide_declaration(c_name, "{sig};")
+       end
 
-        redef fun body_to_c(v)
-        do
-                var rta = v.compiler.as(SeparateCompiler).runtime_type_analysis
-                if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
+       redef fun body_to_c(v)
+       do
+               var rta = v.compiler.as(SeparateCompiler).runtime_type_analysis
+               if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
                        v.add_abort("FATAL: Dead method executed.")
-                else
-                        super
-                end
-        end
-
+               else
+                       super
+               end
+       end
 
-        redef fun end_compile_to_c(v)
-        do
-                var compiler = v.compiler
-                compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
-        end
+       redef fun end_compile_to_c(v)
+       do
+               var compiler = v.compiler
+               compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
+       end
 
-        redef fun build_frame(v, arguments)
-        do
-                var recv = mmethoddef.mclassdef.bound_mtype
-                return new StaticFrame(v, mmethoddef, recv, arguments)
-        end
+       redef fun build_frame(v, arguments)
+       do
+               var recv = mmethoddef.mclassdef.bound_mtype
+               return new StaticFrame(v, mmethoddef, recv, arguments)
+       end
 
        # Compile the trampolines used to implement late-binding.
        #
@@ -2356,9 +2552,9 @@ class SeparateRuntimeFunction
 end
 
 class SeparateThunkFunction
-        super ThunkFunction
-        super SeparateRuntimeFunction
-        redef var target_recv
+       super ThunkFunction
+       super SeparateRuntimeFunction
+       redef var target_recv
 end
 
 redef class MType
index 511cb75..8e0a0b2 100644 (file)
@@ -322,6 +322,26 @@ class SeparateErasureCompiler
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
+                else if mclass.name == "RoutineRef" then
+                        self.header.add_decl("struct instance_{c_name} \{")
+                        self.header.add_decl("const struct class *class;")
+                        self.header.add_decl("val* recv;")
+                        self.header.add_decl("nitmethod_t method;")
+                        self.header.add_decl("\};")
+
+                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class);")
+                        v.add_decl("/* allocate {mtype} */")
+                        v.add_decl("{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class)\{")
+                        var res = v.get_name("self")
+                        v.add_decl("struct instance_{c_name} *{res};")
+                        var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
+                        v.add("{res} = {alloc};")
+                        v.add("{res}->class = class;")
+                        v.add("{res}->recv = recv;")
+                        v.add("{res}->method = method;")
+                        v.add("return (val*){res};")
+                        v.add("\}")
+                        return
                else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
                        var pointer_type = mainmodule.pointer_type
 
@@ -658,4 +678,55 @@ class SeparateErasureCompilerVisitor
                self.add("{res} = NEW_{nclass.c_name}({length});")
                return res
        end
+
+        redef fun routine_ref_instance(routine_type, recv, callsite)
+        do
+               var mmethoddef = callsite.mpropdef
+                #debug "ENTER ref_instance"
+                var mmethod = mmethoddef.mproperty
+                # routine_mclass is the specialized one, e.g: FunRef1, ProcRef2, etc..
+                var routine_mclass = routine_type.mclass
+
+                var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
+                var base_routine_mclass = nclasses.first
+
+                # All routine classes use the same `NEW` constructor.
+                # However, they have different declared `class` and `type` value.
+                self.require_declaration("NEW_{base_routine_mclass.c_name}")
+
+                var recv_class_cname = recv.mcasttype.as(MClassType).mclass.c_name
+                var my_recv = recv
+
+                if recv.mtype.is_c_primitive then
+                        my_recv = autobox(recv, mmodule.object_type)
+                end
+                var my_recv_mclass_type = my_recv.mtype.as(MClassType)
+
+                # The class of the concrete Routine must exist (e.g ProcRef0, FunRef0, etc.)
+                self.require_declaration("class_{routine_mclass.c_name}")
+
+                self.require_declaration(mmethoddef.c_name)
+
+                var thunk_function = mmethoddef.callref_thunk(my_recv_mclass_type)
+                var runtime_function = mmethoddef.virtual_runtime_function
+
+                var is_c_equiv = runtime_function.msignature.c_equiv(thunk_function.msignature)
+
+                var c_ref = thunk_function.c_ref
+                if is_c_equiv then
+                        var const_color = mmethoddef.mproperty.const_color
+                        c_ref = "{class_info(my_recv)}->vft[{const_color}]"
+                        self.require_declaration(const_color)
+                else
+                        self.require_declaration(thunk_function.c_name)
+                        compiler.thunk_todo(thunk_function)
+                end
+                compiler.thunk_todo(thunk_function)
+
+                # Each RoutineRef points to a receiver AND a callref_thunk
+                var res = self.new_expr("NEW_{base_routine_mclass.c_name}({my_recv}, (nitmethod_t){c_ref}, &class_{routine_mclass.c_name})", routine_type)
+                #debug "LEAVING ref_instance"
+                return res
+
+        end
 end
index 9b4fb1c..7557da7 100644 (file)
@@ -71,6 +71,9 @@ class NaiveInterpreter
        # The main Sys instance
        var mainobj: nullable Instance is noinit
 
+       # Name of all supported functional names
+       var routine_types: Set[String] = new HashSet[String]
+
        init
        do
                if mainmodule.model.get_mclasses_by_name("Bool") != null then
@@ -80,6 +83,15 @@ class NaiveInterpreter
                        init_instance_primitive(self.false_instance)
                end
                self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)
+
+               routine_types.add("RoutineRef")
+               for name in ["Proc", "Fun", "ProcRef", "FunRef"] do
+                       # 20 is a magic number = upper limit of the arity of each functional class.
+                       # i.e. Proc0, Proc1, ... Proc19
+                       for i  in [0..20[ do
+                               routine_types.add("{name}{i}")
+                       end
+               end
        end
 
        # Starts the interpreter on the main module of a program
@@ -456,6 +468,22 @@ class NaiveInterpreter
        # Store known methods, used to trace methods as they are reached
        var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
 
+       # Consumes an iterator of expressions and tries to map each element to
+       # its corresponding Instance.
+       #
+       # If any AExprs doesn't resolve to an Instance, then it returns null.
+       # Otherwise return an array of instances
+       fun aexprs_to_instances(aexprs: Iterator[AExpr]): nullable Array[Instance]
+       do
+               var accumulator = new Array[Instance]
+               for aexpr in aexprs do
+                       var instance = expr(aexpr)
+                       if instance == null then return null
+                       accumulator.push(instance)
+               end
+               return accumulator
+       end
+
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of instances to use in the call.
@@ -470,21 +498,15 @@ class NaiveInterpreter
 
                if map == null then
                        assert args.length == msignature.arity else debug("Expected {msignature.arity} args, got {args.length}")
-                       for ne in args do
-                               var e = self.expr(ne)
-                               if e == null then return null
-                               res.add e
-                       end
+                       var rest_args = aexprs_to_instances(args.iterator)
+                       if rest_args == null then return null
+                       res.append(rest_args)
                        return res
                end
 
                # Eval in order of arguments, not parameters
-               var exprs = new Array[Instance].with_capacity(args.length)
-               for ne in args do
-                       var e = self.expr(ne)
-                       if e == null then return null
-                       exprs.add e
-               end
+               var exprs = aexprs_to_instances(args.iterator)
+               if exprs == null then return null
 
                # Fill `res` with the result of the evaluation according to the mapping
                for i in [0..msignature.arity[ do
@@ -775,6 +797,27 @@ class MutableInstance
        var attributes: Map[MAttribute, Instance] = new HashMap[MAttribute, Instance]
 end
 
+# An instance with the original receiver and callsite (for function reference)
+class CallrefInstance
+       super Instance
+
+       # The original receiver
+       #
+       # ~~~nitish
+       # var a = new A
+       # var f = &a.toto # `a` is the original receiver
+       # ~~~
+       var recv: Instance
+
+       # The original callsite
+       #
+       # ~~~nitish
+       # var a = new A
+       # var f = &a.toto # `toto` is the original callsite
+       # ~~~
+       var callsite: CallSite
+end
+
 # Special instance to handle primitives values (int, bool, etc.)
 # The trick is just to encapsulate the “real” value.
 class PrimitiveInstance[E]
@@ -960,6 +1003,19 @@ redef class AMethPropdef
        do
                var pname = mpropdef.mproperty.name
                var cname = mpropdef.mclassdef.mclass.name
+
+               if pname == "call" and v.routine_types.has(cname) then
+                       var routine = args.shift
+                       assert routine isa CallrefInstance
+                       # Swap the receiver position with the original recv of the call form.
+                       args.unshift routine.recv
+                       var res = v.callsite(routine.callsite, args)
+                       # recover the old args state
+                       args.shift
+                       args.unshift routine
+                       return res
+               end
+
                if pname == "output" then
                        var recv = args.first
                        recv.val.output
@@ -2229,18 +2285,24 @@ redef class ASendExpr
 
                var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
                if args == null then return null
-
                var res = v.callsite(callsite, args)
                return res
        end
 end
 
 redef class ACallrefExpr
-        redef fun expr(v)
-        do
-                fatal(v, "NOT YET IMPLEMENTED callref expressions.")
-                return null
-        end
+       redef fun expr(v)
+       do
+               var recv = v.expr(self.n_expr)
+               if recv == null then return null
+               var mtype = self.mtype
+               assert mtype != null
+               # In case we are in generic class where formal parameter can not
+               # be resolved.
+               var mtype2 = v.unanchor_type(mtype)
+               var inst = new CallrefInstance(mtype2, recv, callsite.as(not null))
+               return inst
+       end
 end
 
 redef class ASendReassignFormExpr
index 01042e4..5b8d41b 100644 (file)
@@ -14,7 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
 # Rapid type analysis on the AST
 #
 # Rapid type analysis is an analyse that aproximates the set of live classes
@@ -701,6 +700,13 @@ redef class ASendExpr
        end
 end
 
+redef class ACallrefExpr
+        redef fun accept_rapid_type_visitor(v)
+        do
+                super
+                v.add_type(mtype.as(MClassType))
+        end
+end
 
 redef class ASendReassignFormExpr
        redef fun accept_rapid_type_visitor(v)
index 0658445..b9acd5e 100644 (file)
@@ -181,7 +181,6 @@ private class TypeVisitor
                return self.visit_expr_subtype(nexpr, self.type_bool(nexpr))
        end
 
-
        fun check_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
        do
                var sub = nexpr.mtype
@@ -424,7 +423,6 @@ private class TypeVisitor
                return build_callsite_by_name(node, recvtype, name, recv_is_self)
        end
 
-
        # Visit the expressions of args and check their conformity with the corresponding type in signature
        # The point of this method is to handle varargs correctly
        # Note: The signature must be correctly adapted
@@ -445,7 +443,6 @@ private class TypeVisitor
                        # Other cases are managed later
                end
 
-
                #debug("CALL {unsafe_type}.{msignature}")
 
                # Associate each parameter to a position in the arguments
@@ -1177,7 +1174,6 @@ redef class AVarReassignExpr
        end
 end
 
-
 redef class AContinueExpr
        redef fun accept_typing(v)
        do
@@ -1503,7 +1499,6 @@ redef class AAndExpr
        end
 end
 
-
 redef class ANotExpr
        redef fun accept_typing(v)
        do
@@ -2074,7 +2069,6 @@ redef class AUnaryopExpr
        redef fun compute_raw_arguments do return new Array[AExpr]
 end
 
-
 redef class ACallExpr
        redef fun property_name do return n_qid.n_id.text
        redef fun property_node do return n_qid
@@ -2215,11 +2209,24 @@ redef class ACallrefExpr
                 # end
                 #
                 # var a = new A[Int]
-                # var f = &a.toto <- without anchor : ProcRef1[E]
-                #               ^--- with anchor : ProcRef[Int]
+                # var f = &a.toto # without anchor : ProcRef1[E]
+                #                # with anchor : ProcRef[Int]
                 # ~~~~
-                var routine_type = routine_mclass.get_mtype(types_list).anchor_to(v.mmodule, recv.as(MClassType))
-
+               # However, we can only anchor if we can resolve every formal
+               # parameter, here's an example where we can't.
+               # ~~~~nitish
+               # class A[E]
+               #       fun bar: A[E] do return self
+               #       fun foo: Fun0[A[E]] do return &bar # here we can't anchor
+               # end
+               # var f1 = a1.foo # when this expression will be evaluated,
+               #                 # `a1` will anchor `&bar` returned by `foo`.
+               # print f1.call
+               # ~~~~
+               var routine_type = routine_mclass.get_mtype(types_list)
+               if not recv.need_anchor then
+                       routine_type = routine_type.anchor_to(v.mmodule, recv.as(MClassType))
+               end
                 is_typed = true
                self.mtype = routine_type
        end
@@ -2509,7 +2516,6 @@ redef class AAttrExpr
        end
 end
 
-
 redef class AAttrAssignExpr
        redef fun accept_typing(v)
        do
index c3ae17c..cc5dc21 100644 (file)
@@ -1,11 +1,14 @@
-syntax_callref.nit:40,5--8: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:41,5--12: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:42,5--16: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:43,6--13: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:44,5--18: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:48,5--10: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:49,5--14: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:50,5--18: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:51,6--15: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:52,5--20: Error: NOT YET IMPLEMENTED callref expressions.
-syntax_callref.nit:54,5--11: Error: NOT YET IMPLEMENTED callref expressions.
+foo<Sys>
+foo<Sys>
+baz<Sys>
+foo<Sys>
+baz<FunRef0>
+foo<Sys>
+baz<Sys>
+foo<Y>
+foo<Y>
+baz<Y>
+foo<Y>
+baz<FunRef0>
+foo<Y>
+baz<Y>
index 253e163..4331840 100644 (file)
@@ -1 +1 @@
-alt/syntax_callref_alt1.nit:47,5--6: Error: `y` is a variable, not a method.
+alt/syntax_callref_alt1.nit:55,5--6: Error: `y` is a variable, not a method.
diff --git a/tests/sav/test_callref.res b/tests/sav/test_callref.res
new file mode 100644 (file)
index 0000000..6fbe92f
--- /dev/null
@@ -0,0 +1,14 @@
+in A::fun1
+in B::fun1
+100
+in B::fun1
+in B::fun1
+in B::fun1
+110
+3
+5
+x is null
+x is null
+x is test
+x is 100
+x is 100
index 37c971f..cd18f7b 100644 (file)
@@ -29,6 +29,13 @@ redef class Object
                print "baz{self}"
                return self
        end
+
+        redef fun to_s
+        do
+               # cname without generics (for erasure compiler)
+               var cname = class_name.split('[')[0]
+               return "<{cname}>"
+        end
 end
 
 class Y
@@ -45,7 +52,7 @@ x = (&foo.bar).baz
 x = &(foo.bar).baz
 
 var y = new Y
-#_lt1#x = &y # error since y is a variable TODO: put `alt1` back
+#alt1#x = &y # error since y is a variable
 x = &y.foo
 x = &y.foo.bar
 x = &y.foo.bar.baz
diff --git a/tests/test_callref.nit b/tests/test_callref.nit
new file mode 100644 (file)
index 0000000..08c3219
--- /dev/null
@@ -0,0 +1,110 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import functional
+
+redef class Object
+       fun toto(x: Int): Int
+       do
+               return x + 1
+       end
+end
+
+redef class Int
+       redef fun toto(x) do return x + self
+
+       fun mult_by(x: Int): Int do return x * self
+end
+
+class A
+       fun fun1: String
+       do
+               return "in A::fun1"
+       end
+end
+
+class B
+       super A
+
+       redef fun fun1
+       do
+               return "in B::fun1"
+       end
+end
+
+class Counter
+       var x = 0
+       fun incr do x += 1
+end
+
+class C[E]
+       var x: E
+       redef fun to_s
+       do
+               if x != null then
+                       return "x is {x.as(not null)}"
+               end
+               return "x is null"
+       end
+
+       fun bar: C[E] do return self
+       fun foo: Fun0[C[E]] do return &bar
+end
+
+var a = new A
+var b: A = new B
+
+var f1 = &a.fun1
+print f1.call  # "in A::fun1"
+
+var f2 = &b.fun1
+print f2.call  # "in B::fun1"
+
+var f3 = &10.mult_by
+print f3.call(10)      # 100
+
+var f4 = &f2.call
+print f4.call  # "in B::fun1"
+
+var f5: Fun0[Object] = &f4.call
+print f5.call
+print f5.call
+
+print((&10.toto).call(100))    # 110
+print((&"allo".toto).call(2))  # 3
+
+var cnt = new Counter
+var p1 = &cnt.incr
+var ps = [p1,p1,p1,p1,p1]
+
+for p in ps do p.call
+print cnt.x    # 5
+
+var c1 = new C[nullable Object](null)
+var c2 = new C[nullable Int](null)
+
+var f6 = &c1.to_s
+var f7 = &c2.to_s
+
+print f6.call  # "x is null"
+print f7.call  # "x is null"
+
+c1.x = "test"
+c2.x = 100
+
+print f6.call  # "x is test"
+print f7.call  # "x is 100"
+
+var f8 = c2.foo
+print f8.call  # "x is 100"
index 7740b5f..ff71517 100755 (executable)
@@ -15,7 +15,7 @@
 
 # Run some tests on each engine
 
-engine=(nitcg nitcg nitcs nitcsg nitce niti nitvm)
+engine=(nitcg nitcg nitcs nitcsg nitce niti)
 if uname | grep MINGW64 1>/dev/null 2>&1; then
        engine=(nitcg nitcg nitcs nitcsg nitce)
 fi