X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index 440f245..a4d1141 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -17,10 +17,11 @@ # Analysis and verification of property definitions to instantiate model element module modelize_property -import modelize_class +intrude import modelize_class private import annotation redef class ToolContext + # Run `AClassdef::build_property` on the classdefs of each module var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase]) end @@ -36,9 +37,47 @@ private class ModelizePropertyPhase end redef class ModelBuilder - # Register the npropdef associated to each mpropdef - # FIXME: why not refine the `MPropDef` class with a nullable attribute? - var mpropdef2npropdef = new HashMap[MPropDef, APropdef] + # Registration of the npropdef associated to each mpropdef. + # + # Public clients need to use `mpropdef2node` to access stuff. + private var mpropdef2npropdef = new HashMap[MPropDef, APropdef] + + # Retrieve the associated AST node of a mpropertydef. + # This method is used to associate model entity with syntactic entities. + # + # If the property definition is not associated with a node, returns `null`. + fun mpropdef2node(mpropdef: MPropDef): nullable ANode + do + var res + res = mpropdef2npropdef.get_or_null(mpropdef) + if res != null then + # Run the phases on it + toolcontext.run_phases_on_npropdef(res) + return res + end + if mpropdef isa MMethodDef and mpropdef.mproperty.is_root_init then + res = mclassdef2nclassdef.get_or_null(mpropdef.mclassdef) + if res != null then return res + end + return null + end + + # Retrieve all the attributes nodes localy definied + # FIXME think more about this method and how the separations separate/global and ast/model should be done. + fun collect_attr_propdef(mclassdef: MClassDef): Array[AAttrPropdef] + do + var res = new Array[AAttrPropdef] + var n = mclassdef2nclassdef.get_or_null(mclassdef) + if n == null then return res + for npropdef in n.n_propdefs do + if npropdef isa AAttrPropdef then + # Run the phases on it + toolcontext.run_phases_on_npropdef(npropdef) + res.add(npropdef) + end + end + return res + end # Build the properties of `nclassdef`. # REQUIRE: all superclasses are built. @@ -142,6 +181,7 @@ redef class ModelBuilder mparameters.add(mparameter) end initializers.add(npropdef.mpropdef.mproperty) + npropdef.mpropdef.mproperty.is_autoinit = true end if npropdef isa AAttrPropdef then if npropdef.mpropdef == null then return # Skip broken attribute @@ -151,6 +191,7 @@ redef class ModelBuilder # For autoinit attributes, call the reader to force # the lazy initialization of the attribute. initializers.add(npropdef.mreadpropdef.mproperty) + npropdef.mreadpropdef.mproperty.is_autoinit = true continue end if npropdef.has_value then continue @@ -163,9 +204,11 @@ redef class ModelBuilder if msetter == null then # No setter, it is a old-style attribute, so just add it initializers.add(npropdef.mpropdef.mproperty) + npropdef.mpropdef.mproperty.is_autoinit = true else # Add the setter to the list initializers.add(msetter.mproperty) + msetter.mproperty.is_autoinit = true end end end @@ -175,42 +218,103 @@ redef class ModelBuilder # Look for most-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`?") + toolcontext.error(nclassdef.location, "Error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?") + return end - # 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 + # Look at the autoinit class-annotation + var autoinit = nclassdef.get_single_annotation("autoinit", self) + var noautoinit = nclassdef.get_single_annotation("noautoinit", self) + if autoinit != null then + # Just throws the collected initializers + mparameters.clear + initializers.clear + + if noautoinit != null then + error(autoinit, "Error: `autoinit` and `noautoinit` are incompatible.") + end + + if autoinit.n_args.is_empty then + error(autoinit, "Syntax error: `autoinit` expects method identifiers, use `noautoinit` to clear all autoinits.") 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 + + # Get and check each argument + for narg in autoinit.n_args do + var id = narg.as_id + if id == null then + error(narg, "Syntax error: `autoinit` expects method identifiers.") + return + end + + # Search the property. + # To avoid bad surprises, try to get the setter first. + var p = try_get_mproperty_by_name(narg, mclassdef, id + "=") + if p == null then + p = try_get_mproperty_by_name(narg, mclassdef, id) + end + if p == null then + error(narg, "Error: unknown method `{id}`") + return + end + if not p.is_autoinit then + error(narg, "Error: `{p}` is not an autoinit method") + return + end + + # Register the initializer and the parameters + initializers.add(p) + var pd = p.intro + if pd isa MMethodDef then + # Get the signature resolved for the current receiver + var sig = pd.msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mclassdef.mmodule, false) + mparameters.add_all sig.mparameters + else + # TODO attributes? + abort + end + end + else if noautoinit != null then + if initializers.is_empty then + warning(noautoinit, "useless-noautoinit", "Warning: the list of autoinit is already empty.") + end + # Just clear initializers + mparameters.clear + initializers.clear + else + # 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 - 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) - mclassdef.mclass.root_init = longest - return - 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) + mclassdef.mclass.root_init = longest + 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 + # 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 end # If we already have a basic init definition, then setup its initializers @@ -292,7 +396,8 @@ redef class MPropDef end redef class AClassdef - var build_properties_is_done = false + # Marker used in `ModelBuilder::build_properties` + private var build_properties_is_done = false # The free init (implicitely constructed by the class if required) var mfree_init: nullable MMethodDef = null @@ -364,9 +469,9 @@ redef class APropdef # The associated propdef once build by a `ModelBuilder` var mpropdef: nullable MPROPDEF is writable - private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) is abstract - private fun build_signature(modelbuilder: ModelBuilder) is abstract - private fun check_signature(modelbuilder: ModelBuilder) is abstract + private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) do end + private fun build_signature(modelbuilder: ModelBuilder) do end + private fun check_signature(modelbuilder: ModelBuilder) do end private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility do var mvisibility = public_visibility @@ -438,11 +543,24 @@ redef class APropdef return false end + if mprop isa MMethod and mprop.is_root_init then return true if kwredef == null then if need_redef then modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") return false end + + # Check for full-name conflicts in the project. + # A public property should have a unique qualified name `project::class::prop`. + if mprop.intro_mclassdef.mmodule.mgroup != null and mprop.visibility >= protected_visibility then + var others = modelbuilder.model.get_mproperties_by_name(mprop.name) + if others != null then for other in others do + if other != mprop and other.intro_mclassdef.mmodule.mgroup != null and other.intro_mclassdef.mmodule.mgroup.mproject == mprop.intro_mclassdef.mmodule.mgroup.mproject and other.intro_mclassdef.mclass.name == mprop.intro_mclassdef.mclass.name and other.visibility >= protected_visibility then + modelbuilder.advice(self, "full-name-conflict", "Warning: A property named `{other.full_name}` is already defined in module `{other.intro_mclassdef.mmodule}` for the class `{other.intro_mclassdef.mclass.name}`.") + break + end + end + end else if not need_redef then modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") @@ -618,11 +736,22 @@ redef class AMethPropdef mprop.is_init = is_init mprop.is_new = n_kwnew != null if parent isa ATopClassdef then mprop.is_toplevel = true - if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then return + self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) else - if not mprop.is_root_init and not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) end + + # Check name conflicts in the local class for constructors. + if is_init then + for p, n in mclassdef.mprop2npropdef do + if p != mprop and p isa MMethod and p.name == name then + check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, p) + break + end + end + end + mclassdef.mprop2npropdef[mprop] = self var mpropdef = new MMethodDef(mclassdef, mprop, self.location) @@ -676,6 +805,9 @@ redef class AMethPropdef msignature = mpropdef.mproperty.intro.msignature if msignature == null then return # Skip error + # The local signature is adapted to use the local formal types, if any. + msignature = msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false) + # Check inherited signature arity if param_names.length != msignature.arity then var node: ANode @@ -731,6 +863,10 @@ redef class AMethPropdef mpropdef.is_abstract = self.get_single_annotation("abstract", modelbuilder) != null mpropdef.is_intern = self.get_single_annotation("intern", modelbuilder) != null mpropdef.is_extern = self.n_extern_code_block != null or self.get_single_annotation("extern", modelbuilder) != null + + # Check annotations + var at = self.get_single_annotation("lazy", modelbuilder) + if at != null then modelbuilder.error(at, "Syntax error: `lazy` must be used on attributes.") end redef fun check_signature(modelbuilder) @@ -761,18 +897,21 @@ redef class AMethPropdef for i in [0..mysignature.arity[ do var myt = mysignature.mparameters[i].mtype var prt = msignature.mparameters[i].mtype - if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or - not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then - modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.") + var node = nsig.n_params[i] + if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then + modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.") end end end if precursor_ret_type != null then + var node: nullable ANode = null + if nsig != null then node = nsig.n_type + if node == null then node = self if ret_type == null then # Inherit the return type ret_type = precursor_ret_type - else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then - modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.") + else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then + modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.") end end end @@ -816,32 +955,31 @@ redef class AAttrPropdef redef fun build_property(modelbuilder, mclassdef) do var mclass = mclassdef.mclass + var nid2 = n_id2 + var name = nid2.text + + var atabstract = self.get_single_annotation("abstract", modelbuilder) + if atabstract == null then + if mclass.kind == interface_kind then + modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.") + else if mclass.kind == enum_kind then + modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.") + else if mclass.kind == extern_kind then + modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.") + end - var name: String - name = self.n_id2.text - - if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.") - else if mclass.kind == enum_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.") - else if mclass.kind == extern_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.") + var mprop = new MAttribute(mclassdef, "_" + name, private_visibility) + var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) + self.mpropdef = mpropdef + modelbuilder.mpropdef2npropdef[mpropdef] = self end - # New attribute style - var nid2 = self.n_id2 - var mprop = new MAttribute(mclassdef, "_" + name, private_visibility) - var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) - self.mpropdef = mpropdef - modelbuilder.mpropdef2npropdef[mpropdef] = self - var readname = name var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod) if mreadprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) mreadprop = new MMethod(mclassdef, readname, mvisibility) if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return - mreadprop.deprecation = mprop.deprecation else if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop) @@ -852,15 +990,26 @@ redef class AAttrPropdef self.mreadpropdef = mreadpropdef modelbuilder.mpropdef2npropdef[mreadpropdef] = self set_doc(mreadpropdef, modelbuilder) - mpropdef.mdoc = mreadpropdef.mdoc + if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc + if atabstract != null then mreadpropdef.is_abstract = true has_value = n_expr != null or n_block != null + if atabstract != null and has_value then + modelbuilder.error(atabstract, "Error: `abstract` attributes cannot have an initial value") + return + end + var atnoinit = self.get_single_annotation("noinit", modelbuilder) + if atnoinit == null then atnoinit = self.get_single_annotation("noautoinit", modelbuilder) if atnoinit != null then noinit = true if has_value then - modelbuilder.error(atnoinit, "Error: `noinit` attributes cannot have an initial value") + modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value") + return + end + if atabstract != null then + modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract") return end end @@ -914,7 +1063,7 @@ redef class AAttrPropdef end mwriteprop = new MMethod(mclassdef, writename, mvisibility) if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return - mwriteprop.deprecation = mprop.deprecation + mwriteprop.deprecation = mreadprop.deprecation else if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return if atwritable != null then @@ -926,18 +1075,19 @@ redef class AAttrPropdef var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) self.mwritepropdef = mwritepropdef modelbuilder.mpropdef2npropdef[mwritepropdef] = self - mwritepropdef.mdoc = mpropdef.mdoc + mwritepropdef.mdoc = mreadpropdef.mdoc + if atabstract != null then mwritepropdef.is_abstract = true end redef fun build_signature(modelbuilder) do + var mreadpropdef = self.mreadpropdef var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skipped - var mclassdef = mpropdef.mclassdef + if mreadpropdef == null then return # Error thus skipped + var mclassdef = mreadpropdef.mclassdef var mmodule = mclassdef.mmodule var mtype: nullable MType = null - var mreadpropdef = self.mreadpropdef var ntype = self.n_type if ntype != null then @@ -947,11 +1097,15 @@ redef class AAttrPropdef var inherited_type: nullable MType = null # Inherit the type from the getter (usually an abstract getter) - if mreadpropdef != null and not mreadpropdef.is_intro then + if not mreadpropdef.is_intro then var msignature = mreadpropdef.mproperty.intro.msignature if msignature == null then return # Error, thus skipped inherited_type = msignature.return_mtype - if mtype == null then mtype = inherited_type + if inherited_type != null then + # The inherited type is adapted to use the local formal types, if any. + inherited_type = inherited_type.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false) + if mtype == null then mtype = inherited_type + end end var nexpr = self.n_expr @@ -978,7 +1132,7 @@ redef class AAttrPropdef var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") if cla != null then mtype = cla.mclass_type else - modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.") + modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}. Implicit typing allowed only for literals and new.") end if mtype == null then return @@ -993,13 +1147,15 @@ redef class AAttrPropdef end if mtype == null then - modelbuilder.error(self, "Error: Untyped attribute {mpropdef}") + modelbuilder.error(self, "Error: Untyped attribute {mreadpropdef}") return end - mpropdef.static_mtype = mtype + if mpropdef != null then + mpropdef.static_mtype = mtype + end - if mreadpropdef != null then + do var msignature = new MSignature(new Array[MParameter], mtype) mreadpropdef.msignature = msignature end @@ -1090,21 +1246,20 @@ redef class AAttrPropdef for i in [0..mysignature.arity[ do var myt = mysignature.mparameters[i].mtype var prt = msignature.mparameters[i].mtype - if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or - not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then - var node: ANode - if nsig != null then node = nsig else node = self + var node: ANode + if nsig != null then node = nsig else node = self + if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.") end end end if precursor_ret_type != null then + var node: ANode + if nsig != null then node = nsig else node = self if ret_type == null then # Inherit the return type ret_type = precursor_ret_type - else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then - var node: ANode - if nsig != null then node = nsig else node = self + else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.") end end @@ -1137,6 +1292,11 @@ redef class ATypePropdef var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location) self.mpropdef = mpropdef modelbuilder.mpropdef2npropdef[mpropdef] = self + if mpropdef.is_intro then + modelbuilder.toolcontext.info("{mpropdef} introduces new type {mprop.full_name}", 4) + else + modelbuilder.toolcontext.info("{mpropdef} redefines type {mprop.full_name}", 4) + end set_doc(mpropdef, modelbuilder) var atfixed = get_single_annotation("fixed", modelbuilder) @@ -1195,7 +1355,8 @@ redef class ATypePropdef # Check redefinitions bound = mpropdef.bound.as(not null) for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do - var supbound = p.bound.as(not null) + var supbound = p.bound + if supbound == null then break # broken super bound, skip error if p.is_fixed then modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}") break @@ -1205,7 +1366,7 @@ redef class ATypePropdef modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.") break end - if not bound.is_subtype(mmodule, anchor, supbound) then + if not modelbuilder.check_subtype(n_type, mmodule, anchor, bound, supbound) then modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.") break end