# 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
# 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.
# 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
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
# 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
# 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
# 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
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)
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]});")
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
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
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} */")
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)
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)
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
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
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")
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
# 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(", ")
else
return self.box_kinds[mclass]
end
-
end
fun compile_color_consts(colors: Map[Object, Int]) do
attr_tables[mclass] = attr_colorer.build_layout(mclass)
end
-
end
# colorize live types of the program
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]]
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
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
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
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
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
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
# 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.
#
end
class SeparateThunkFunction
- super ThunkFunction
- super SeparateRuntimeFunction
- redef var target_recv
+ super ThunkFunction
+ super SeparateRuntimeFunction
+ redef var target_recv
end
redef class MType
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
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
# 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
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
# 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.
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
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]
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
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
# 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
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)
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
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
# Other cases are managed later
end
-
#debug("CALL {unsafe_type}.{msignature}")
# Associate each parameter to a position in the arguments
end
end
-
redef class AContinueExpr
redef fun accept_typing(v)
do
end
end
-
redef class ANotExpr
redef fun accept_typing(v)
do
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
# 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
end
end
-
redef class AAttrAssignExpr
redef fun accept_typing(v)
do
-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>
-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.
--- /dev/null
+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
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
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
--- /dev/null
+# 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"
# 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