From 5e750ab907cedf75778102b8bceefb6fea022d16 Mon Sep 17 00:00:00 2001 From: Louis-Vincent Boudreault Date: Thu, 26 Sep 2019 10:52:15 -0400 Subject: [PATCH] Updated `tests/syntax_callref.nit` Signed-off-by: Louis-Vincent Boudreault --- src/compiler/abstract_compiler.nit | 461 +++++++++++++++++------------------ src/compiler/global_compiler.nit | 24 +- src/compiler/separate_compiler.nit | 466 ++++++++++++++++++------------------ src/rapid_type_analysis.nit | 2 - tests/sav/syntax_callref.res | 25 +- tests/sav/syntax_callref_alt1.res | 2 +- tests/sav/test_callref.res | 13 + tests/syntax_callref.nit | 9 +- tests/test_callref.nit | 104 ++++++++ 9 files changed, 606 insertions(+), 500 deletions(-) create mode 100644 tests/sav/test_callref.res create mode 100644 tests/test_callref.nit diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 669159f..e3132da 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -625,19 +625,19 @@ 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 + # 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 @@ -1391,11 +1391,11 @@ 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, mmethoddef: MMethodDef): RuntimeVariable is abstract + # Instantiate a new routine pointer + fun routine_ref_instance(routine_mclass_type: MClassType, recv: RuntimeVariable, mmethoddef: MMethodDef): RuntimeVariable is abstract - # Call the underlying referenced function - fun routine_ref_call(mmethoddef: MMethodDef, args: Array[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 # @@ -2104,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 @@ -2117,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 @@ -2128,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 @@ -2323,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 @@ -2443,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 @@ -2544,19 +2544,6 @@ redef class MClassType end end -redef class MSignature - fun change_all_mtype_for(mtype: MType): MSignature - do - var ps = new Array[MParameter] - for p in mparameters do - ps.push(new MParameter(p.name, mtype, p.is_vararg)) - end - var ret: nullable MType = null - if return_mtype != null then ret = mtype - return new MSignature(ps, ret) - end -end - redef class MPropDef type VISITOR: AbstractCompilerVisitor end @@ -2722,15 +2709,17 @@ redef class AMethPropdef var pname = mpropdef.mproperty.name var cname = mpropdef.mclassdef.mclass.name var ret = mpropdef.msignature.return_mtype - 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. This weird behavior doesn't affect - # the inner mecanics of callref since the return type is already solved by - # `routine_ref_call` - if ret != null and not compiler.all_routine_types_name.has(cname) 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 != "!=" and pname != "call" and not compiler.all_routine_types_name.has(cname) then + + if pname != "==" and pname != "!=" then v.adapt_signature(mpropdef, arguments) v.unbox_signature_extern(mpropdef, arguments) end @@ -3493,9 +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 + 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]});") @@ -4414,12 +4403,12 @@ redef class ASendExpr end redef class ACallrefExpr - 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).mpropdef) - return res - 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).mpropdef) + return res + end end redef class ASendReassignFormExpr diff --git a/src/compiler/global_compiler.nit b/src/compiler/global_compiler.nit index 8f3b437..2d2e6f2 100644 --- a/src/compiler/global_compiler.nit +++ b/src/compiler/global_compiler.nit @@ -472,8 +472,7 @@ class GlobalCompilerVisitor 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 + thunk.polymorph_call_flag = not my_recv.is_exact compiler.todo(method) compiler.todo(thunk) @@ -499,12 +498,7 @@ class GlobalCompilerVisitor 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 @@ -1122,17 +1116,17 @@ private class CustomizedRuntimeFunction # class A[E] # fun toto(x: E) # do - # ...do something with x... + # # ...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. + # 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) @@ -1165,12 +1159,12 @@ private class CustomizedRuntimeFunction end end +# Thunk implementation for global compiler. +# For more detail see `abstract_compiler::ThunkFunction` documentation. class CustomizedThunkFunction super ThunkFunction super CustomizedRuntimeFunction - #var force_polymorphism = false - redef fun c_name do return "THUNK_" + super diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index f06e61f..9c98d07 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -149,7 +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] + private var thunks_to_compile: Set[SeparateRuntimeFunction] = new HashSet[SeparateRuntimeFunction] init do var file = new_file("nit.common") @@ -196,14 +196,14 @@ 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 + 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 @@ -263,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(", ") @@ -283,7 +283,7 @@ class SeparateCompiler return self.box_kinds[self.mainmodule.pointer_type.mclass] else return self.box_kinds[mclass] - end + end end fun compile_color_consts(colors: Map[Object, Int]) do @@ -648,15 +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) + 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 @@ -954,29 +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 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 @@ -2188,119 +2188,119 @@ class SeparateCompilerVisitor self.add("{recv}[{i}]={val};") end - redef fun routine_ref_instance(routine_type, recv, mmethoddef) - do - #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("type_{routine_type.c_name}") - - compiler.undead_types.add(routine_type) - 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 - - # 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}, &type_{routine_type.c_name})", routine_type) - #debug "LEAVING ref_instance" - 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 + redef fun routine_ref_instance(routine_type, recv, mmethoddef) + do + #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("type_{routine_type.c_name}") + + compiler.undead_types.add(routine_type) + 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 + + # 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}, &type_{routine_type.c_name})", routine_type) + #debug "LEAVING ref_instance" + 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 @@ -2326,44 +2326,42 @@ redef class MMethodDef 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 runtime_function = virtual_runtime_function - var object_type = mclassdef.mmodule.object_type - var nullable_object = object_type.as_nullable - var msignature2 = msignature.change_all_mtype_for(nullable_object) - var intromclassdef = mproperty.intro.mclassdef - - #var introrecv = intromclassdef.bound_mtype - ## If the thunk signature is equivalent to its - ## virtual counterpart, then nothing to do. - #print "recv vs intro : {recv_mtype} vs {introrecv}" - #if msignature2.c_equiv(runtime_function.called_signature) and recv_mtype == introrecv then - # callref_thunk_cache = res - # return runtime_function - #end - # receiver cannot be null - 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 + # 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 @@ -2389,7 +2387,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 @@ -2428,20 +2426,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 @@ -2472,32 +2470,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 - - 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 + 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 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. # @@ -2547,9 +2545,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 diff --git a/src/rapid_type_analysis.nit b/src/rapid_type_analysis.nit index 2bb3a86..5b8d41b 100644 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@ -704,8 +704,6 @@ redef class ACallrefExpr redef fun accept_rapid_type_visitor(v) do super - var b = v.analysis.live_types.has(callsite.recv) - v.add_type(callsite.recv.as(MClassType)) v.add_type(mtype.as(MClassType)) end end diff --git a/tests/sav/syntax_callref.res b/tests/sav/syntax_callref.res index c3ae17c..cc5dc21 100644 --- a/tests/sav/syntax_callref.res +++ b/tests/sav/syntax_callref.res @@ -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 +foo +baz +foo +baz +foo +baz +foo +foo +baz +foo +baz +foo +baz diff --git a/tests/sav/syntax_callref_alt1.res b/tests/sav/syntax_callref_alt1.res index 253e163..4331840 100644 --- a/tests/sav/syntax_callref_alt1.res +++ b/tests/sav/syntax_callref_alt1.res @@ -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 index 0000000..a97c247 --- /dev/null +++ b/tests/sav/test_callref.res @@ -0,0 +1,13 @@ +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 diff --git a/tests/syntax_callref.nit b/tests/syntax_callref.nit index 37c971f..cd18f7b 100644 --- a/tests/syntax_callref.nit +++ b/tests/syntax_callref.nit @@ -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 index 0000000..66125d6 --- /dev/null +++ b/tests/test_callref.nit @@ -0,0 +1,104 @@ +# 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 +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" -- 1.7.9.5