From ad4bec1a8598a7449da2b00458c7af40ced18635 Mon Sep 17 00:00:00 2001 From: Jean Privat Date: Tue, 19 Aug 2014 19:53:37 -0400 Subject: [PATCH] src: introduce new constructors Signed-off-by: Jean Privat --- src/abstract_compiler.nit | 32 ++++++++++- src/auto_super_init.nit | 43 ++++++++++++++- src/model/model.nit | 16 ++++++ src/modelize_property.nit | 126 ++++++++++++++++++++++++++++++++++++++----- src/naive_interpreter.nit | 27 ++++++++++ src/rapid_type_analysis.nit | 10 ++++ src/separate_compiler.nit | 5 +- src/typing.nit | 14 +++-- 8 files changed, 252 insertions(+), 21 deletions(-) diff --git a/src/abstract_compiler.nit b/src/abstract_compiler.nit index 979832a..9d441cc 100644 --- a/src/abstract_compiler.nit +++ b/src/abstract_compiler.nit @@ -1044,9 +1044,27 @@ abstract class AbstractCompilerVisitor return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule) end - fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable + fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do - return self.send(callsite.mproperty, args) + var initializers = callsite.mpropdef.initializers + if not initializers.is_empty then + var recv = arguments.first + + assert initializers.length == arguments.length - 1 else debug("expected {initializers.length}, got {arguments.length - 1}") + var i = 1 + for p in initializers do + if p isa MMethod then + self.send(p, [recv, arguments[i]]) + else if p isa MAttribute then + self.write_attribute(p, recv, arguments[i]) + else abort + i += 1 + end + + return self.send(callsite.mproperty, [recv]) + end + + return self.send(callsite.mproperty, arguments) end fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract @@ -2251,6 +2269,15 @@ redef class AClassdef private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do if mpropdef == self.mfree_init then + if mpropdef.mproperty.is_root_init then + assert self.super_inits == null + assert arguments.length == 1 + if not mpropdef.is_intro then + v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) + end + return + end + var super_inits = self.super_inits if super_inits != null then var args_of_super = arguments @@ -2259,6 +2286,7 @@ redef class AClassdef v.send(su, args_of_super) end end + var recv = arguments.first var i = 1 # Collect undefined attributes diff --git a/src/auto_super_init.nit b/src/auto_super_init.nit index 513d071..8891df3 100644 --- a/src/auto_super_init.nit +++ b/src/auto_super_init.nit @@ -105,7 +105,9 @@ redef class AMethPropdef end # Still here? So it means that we must determine what super inits need to be automatically invoked + # The code that follow is required to deal with complex cases with old-style and new-style inits + # Look for old-style super constructors var auto_super_inits = new Array[CallSite] for msupertype in mclassdef.supertypes do # FIXME: the order is quite arbitrary @@ -121,20 +123,59 @@ redef class AMethPropdef end assert candidate isa MMethod + # Skip new-style init + if candidate.is_root_init then continue + var candidatedefs = candidate.lookup_definitions(mmodule, anchor) var candidatedef = candidatedefs.first # TODO, we drop the others propdefs in the callsite, that is not great :( - var msignature = candidatedef.msignature + var msignature = candidatedef.new_msignature or else candidatedef.msignature msignature = msignature.resolve_for(recvtype, anchor, mmodule, true) var callsite = new CallSite(self, recvtype, mmodule, anchor, true, candidate, candidatedef, msignature, false) auto_super_inits.add(callsite) end + + # No old style? The look for new-style super constructors (called from a old style constructor) + var the_root_init_mmethod = modelbuilder.the_root_init_mmethod + if the_root_init_mmethod != null and auto_super_inits.is_empty then + var candidatedefs = the_root_init_mmethod.lookup_definitions(mmodule, anchor) + + # Search the longest-one and checks for conflict + var candidatedef = candidatedefs.first + if candidatedefs.length > 1 then + # Check for conflict in the order of initializers + # Each initializer list must me a prefix of the longest list + # part 1. find the longest list + for spd in candidatedefs do + if spd.initializers.length > candidatedef.initializers.length then candidatedef = spd + end + # compare + for spd in candidatedefs do + var i = 0 + for p in spd.initializers do + if p != candidatedef.initializers[i] then + modelbuilder.error(self, "Error: Cannot do an implicit constructor call to comflicting for inherited inits {spd}({spd.initializers.join(", ")}) and {candidatedef}({candidatedef.initializers.join(", ")}). NOTE: Do not mix old-style and new-style init!") + return + end + i += 1 + end + end + end + + var msignature = candidatedef.new_msignature or else candidatedef.msignature + msignature = msignature.resolve_for(recvtype, anchor, mmodule, true) + + var callsite = new CallSite(self, recvtype, mmodule, anchor, true, the_root_init_mmethod, candidatedef, msignature, false) + auto_super_inits.add(callsite) + end if auto_super_inits.is_empty then modelbuilder.error(self, "Error: No constructors to call implicitely in {mpropdef}. Call one explicitely.") return end + + # Can the super-constructors be called? for auto_super_init in auto_super_inits do var auto_super_init_def = auto_super_init.mpropdef var msig = mpropdef.msignature.as(not null) diff --git a/src/model/model.nit b/src/model/model.nit index cc6ca95..f3c3164 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -1827,6 +1827,9 @@ class MMethod # therefore, you should use `is_init_for` the verify if the property is a legal constructor for a given class var is_init: Bool writable = false + # The constructor is a (the) root init with empty signature but a set of initializers + var is_root_init: Bool writable = false + # The the property a 'new' contructor? var is_new: Bool writable = false @@ -1945,6 +1948,19 @@ class MMethodDef # The signature attached to the property definition var msignature: nullable MSignature writable = null + # The signature attached to the `new` call on a root-init + # This is a concatenation of the signatures of the initializers + # + # REQUIRE `mproperty.is_root_init == (new_msignature != null)` + var new_msignature: nullable MSignature writable = null + + # List of initialisers to call in root-inits + # + # They could be setters or attributes + # + # REQUIRE `mproperty.is_root_init == (new_msignature != null)` + var initializers = new Array[MProperty] + # Is the method definition abstract? var is_abstract: Bool writable = false diff --git a/src/modelize_property.nit b/src/modelize_property.nit index 8273fb8..db51cbd 100644 --- a/src/modelize_property.nit +++ b/src/modelize_property.nit @@ -68,6 +68,11 @@ redef class ModelBuilder process_default_constructors(nclassdef) end + # the root init of the Object class + # Is usually implicitly defined + # Then explicit or implicit definitions of root-init are attached to it + var the_root_init_mmethod: nullable MMethod + # Introduce or inherit default constructor # This is the last part of `build_properties`. private fun process_default_constructors(nclassdef: AClassdef) @@ -77,27 +82,54 @@ redef class ModelBuilder # Are we a refinement if not mclassdef.is_intro then return + var mmodule = nclassdef.mclassdef.mmodule + + # Look for the init in Object, or create it + if mclassdef.mclass.name == "Object" and the_root_init_mmethod == null then + # Create the implicit root-init method + var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) + mprop.is_root_init = true + var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) + var mparameters = new Array[MParameter] + var msignature = new MSignature(mparameters, null) + mpropdef.msignature = msignature + mpropdef.new_msignature = msignature + mprop.is_init = true + nclassdef.mfree_init = mpropdef + self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3) + the_root_init_mmethod = mprop + return + end + # Is the class forbid constructors? if not mclassdef.mclass.kind.need_init then return # Is there already a constructor defined? + var defined_init: nullable MMethodDef = null for mpropdef in mclassdef.mpropdefs do if not mpropdef isa MMethodDef then continue - if mpropdef.mproperty.is_init then return + if not mpropdef.mproperty.is_init then continue + if mpropdef.mproperty.is_root_init then + assert defined_init == null + defined_init = mpropdef + else + # An explicit old-style init, so return + return + end end if not nclassdef isa AStdClassdef then return - var mmodule = mclassdef.mmodule - # Do we inherit for a constructor? - var combine = new Array[MMethod] - var inhc: nullable MClass = null - for st in mclassdef.supertypes do + # Do we inherit a old-style constructor? + var combine = new Array[MMethod] # old-style constructors without arguments + var inhc: nullable MClass = null # single super-class with a constructor with arguments + if defined_init == null then for st in mclassdef.supertypes do var c = st.mclass if not c.kind.need_init then continue st = st.anchor_to(mmodule, mclassdef.bound_mtype) var candidate = self.try_get_mproperty_by_name2(nclassdef, mmodule, st, "init").as(nullable MMethod) if candidate != null then + if candidate.is_root_init then continue if candidate.intro.msignature != null then if candidate.intro.msignature.arity == 0 then combine.add(candidate) @@ -117,6 +149,7 @@ redef class ModelBuilder # Collect undefined attributes var mparameters = new Array[MParameter] + var initializers = new Array[MProperty] var anode: nullable ANode = null for npropdef in nclassdef.n_propdefs do if npropdef isa AAttrPropdef then @@ -135,6 +168,14 @@ redef class ModelBuilder if ret_type == null then return var mparameter = new MParameter(paramname, ret_type, false) mparameters.add(mparameter) + var msetter = npropdef.mwritepropdef + if msetter == null then + # No setter, it is a old-style attribute, so just add it + initializers.add(npropdef.mpropdef.mproperty) + else + # Add the setter to the list + initializers.add(msetter.mproperty) + end if anode == null then anode = npropdef end end @@ -148,15 +189,14 @@ redef class ModelBuilder # TODO: actively inherit the consturctor self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3) - mclassdef.mclass.inherit_init_from = inhc - return + #mclassdef.mclass.inherit_init_from = inhc + #return end if not combine.is_empty and inhc != null then self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}") return end - if not combine.is_empty then if mparameters.is_empty and combine.length == 1 then # No need to create a local init, the inherited one is enough @@ -166,13 +206,75 @@ redef class ModelBuilder return end nclassdef.super_inits = combine + var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) + var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) + var msignature = new MSignature(mparameters, null) + mpropdef.msignature = msignature + mprop.is_init = true + nclassdef.mfree_init = mpropdef + self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3) + return + end + + if the_root_init_mmethod == null then return + + # Look for nost-specific new-stype init definitions + var spropdefs = the_root_init_mmethod.lookup_super_definitions(mclassdef.mmodule, mclassdef.bound_mtype) + if spropdefs.is_empty then + toolcontext.fatal_error(nclassdef.location, "Fatal error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?") end - var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) + # Search the longest-one and checks for conflict + var longest = spropdefs.first + if spropdefs.length > 1 then + # Check for conflict in the order of initializers + # Each initializer list must me a prefix of the longest list + # part 1. find the longest list + for spd in spropdefs do + if spd.initializers.length > longest.initializers.length then longest = spd + end + # part 2. compare + for spd in spropdefs do + var i = 0 + for p in spd.initializers do + if p != longest.initializers[i] then + self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})") + return + end + i += 1 + end + end + end + + # Can we just inherit? + if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then + self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3) + return + end + + # Combine the inherited list to what is collected + if longest.initializers.length > 0 then + mparameters.prepend longest.new_msignature.mparameters + initializers.prepend longest.initializers + end + + # If we already have a basic init definition, then setup its initializers + if defined_init != null then + defined_init.initializers.add_all(initializers) + var msignature = new MSignature(mparameters, null) + defined_init.new_msignature = msignature + self.toolcontext.info("{mclassdef} extends its basic constructor signature to {defined_init}{msignature}", 3) + return + end + + # Else create the local implicit basic init definition + var mprop = the_root_init_mmethod.as(not null) var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) + mpropdef.has_supercall = true + mpropdef.initializers.add_all(initializers) var msignature = new MSignature(mparameters, null) - mpropdef.msignature = msignature - mprop.is_init = true + mpropdef.new_msignature = msignature + mpropdef.msignature = new MSignature(new Array[MParameter], null) # always an empty real signature nclassdef.mfree_init = mpropdef self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3) end diff --git a/src/naive_interpreter.nit b/src/naive_interpreter.nit index d974e08..c741f04 100644 --- a/src/naive_interpreter.nit +++ b/src/naive_interpreter.nit @@ -408,6 +408,22 @@ private class NaiveInterpreter # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance do + var initializers = callsite.mpropdef.initializers + if not initializers.is_empty then + assert initializers.length == arguments.length - 1 else debug("expected {initializers.length} got {arguments.length - 1}") + var recv = arguments.first + var i = 1 + for p in initializers do + if p isa MMethod then + self.send(p, [recv, arguments[i]]) + else if p isa MAttribute then + assert recv isa MutableInstance + recv.attributes[p] = arguments[i] + else abort + i += 1 + end + return send(callsite.mproperty, [recv]) + end return send(callsite.mproperty, arguments) end @@ -1046,6 +1062,17 @@ redef class AClassdef # Execute an implicit `mpropdef` associated with the current node. private fun call(v: NaiveInterpreter, mpropdef: MMethodDef, args: Array[Instance]): nullable Instance do + if mpropdef.mproperty.is_root_init then + assert self.super_inits == null + assert args.length == 1 + if not mpropdef.is_intro then + # standard call-next-method + var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype) + v.call_without_varargs(superpd, args) + end + return null + end + var super_inits = self.super_inits if super_inits != null then var args_of_super = args diff --git a/src/rapid_type_analysis.nit b/src/rapid_type_analysis.nit index 1585fb1..6518860 100644 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@ -226,6 +226,7 @@ class RapidTypeAnalysis v.add_monomorphic_send(vararg, self.modelbuilder.force_get_primitive_method(node, "with_native", vararg.mclass, self.mainmodule)) end + # TODO? new_msignature var sig = mmethoddef.msignature.as(not null) var osig = mmeth.intro.msignature.as(not null) for i in [0..sig.arity[ do @@ -247,6 +248,10 @@ class RapidTypeAnalysis v.add_monomorphic_send(v.receiver, su) end end + + if mmethoddef.mproperty.is_root_init and not mmethoddef.is_intro then + self.add_super_send(v.receiver, mmethoddef) + end else abort end @@ -499,6 +504,11 @@ class RapidTypeVisitor fun add_cast_type(mtype: MType) do analysis.add_cast(mtype) fun add_callsite(callsite: nullable CallSite) do if callsite != null then + for m in callsite.mpropdef.initializers do + if m isa MMethod then + analysis.add_send(callsite.recv, m) + end + end analysis.add_send(callsite.recv, callsite.mproperty) analysis.live_callsites.add(callsite) end diff --git a/src/separate_compiler.nit b/src/separate_compiler.nit index 1e05f7e..4dfbeb0 100644 --- a/src/separate_compiler.nit +++ b/src/separate_compiler.nit @@ -958,11 +958,12 @@ class SeparateCompilerVisitor do var rta = compiler.runtime_type_analysis var recv = args.first.mtype - if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null then + var mmethod = callsite.mproperty + # TODO: Inlining of new-style constructors + if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then var tgs = rta.live_targets(callsite) if tgs.length == 1 then # DIRECT CALL - var mmethod = callsite.mproperty self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args) var res0 = before_send(mmethod, args) var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args) diff --git a/src/typing.nit b/src/typing.nit index d4e33eb..a415667 100644 --- a/src/typing.nit +++ b/src/typing.nit @@ -277,7 +277,7 @@ private class TypeVisitor end - var msignature = mpropdef.msignature.as(not null) + var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature) var erasure_cast = false @@ -1267,6 +1267,9 @@ redef class ASendExpr if not (vmpropdef isa MMethodDef and vmpropdef.mproperty.is_init) then v.error(self, "Can call a init only in another init") end + if vmpropdef isa MMethodDef and vmpropdef.mproperty.is_root_init and not callsite.mproperty.is_root_init then + v.error(self, "Error: {vmpropdef} cannot call a factory {callsite.mproperty}") + end end var ret = msignature.return_mtype @@ -1522,12 +1525,15 @@ redef class ASuperExpr if v.modelbuilder.toolcontext.error_count > errcount then return # Forard error continue # Try next super-class end - if superprop != null and superprop.mproperty != candidate then + if superprop != null and candidate.is_root_init then + continue + end + if superprop != null and superprop.mproperty != candidate and not superprop.mproperty.is_root_init then v.error(self, "Error: conflicting super constructor to call for {mproperty}: {candidate.full_name}, {superprop.mproperty.full_name}") return end var candidatedefs = candidate.lookup_definitions(v.mmodule, recvtype) - if superprop != null then + if superprop != null and superprop.mproperty == candidate then if superprop == candidatedefs.first then continue candidatedefs.add(superprop) end @@ -1542,7 +1548,7 @@ redef class ASuperExpr return end - var msignature = superprop.msignature.as(not null) + var msignature = superprop.new_msignature or else superprop.msignature.as(not null) msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) var callsite = new CallSite(self, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) -- 1.7.9.5