X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index f1cce80..aabe809 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -80,7 +80,6 @@ redef class ModelBuilder end # Build the properties of `nclassdef`. - # REQUIRE: all superclasses are built. private fun build_properties(nclassdef: AClassdef) do # Force building recursively @@ -110,7 +109,8 @@ redef class ModelBuilder if mpropdef.bound == null then continue if not check_virtual_types_circularity(npropdef, mpropdef.mproperty, mclassdef.bound_mtype, mclassdef.mmodule) then # Invalidate the bound - mpropdef.bound = mclassdef.mmodule.model.null_type + mpropdef.is_broken = true + mpropdef.bound = new MErrorType(mclassdef.mmodule.model) end end for npropdef in nclassdef2.n_propdefs do @@ -144,7 +144,7 @@ redef class ModelBuilder # 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) + var mprop = new MMethod(mclassdef, "init", nclassdef.location, mclassdef.mclass.visibility) mprop.is_root_init = true var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) var mparameters = new Array[MParameter] @@ -152,15 +152,11 @@ redef class ModelBuilder 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 @@ -182,29 +178,21 @@ redef class ModelBuilder var initializers = new Array[MProperty] for npropdef in nclassdef.n_propdefs do if npropdef isa AMethPropdef then - if npropdef.mpropdef == null then return # Skip broken method - var at = npropdef.get_single_annotation("autoinit", self) - if at == null then continue # Skip non tagged init - - var sig = npropdef.mpropdef.msignature + if not npropdef.is_autoinit then continue # Skip non tagged autoinit + var mpropdef = npropdef.mpropdef + if mpropdef == null then return # Skip broken method + var sig = mpropdef.msignature if sig == null then continue # Skip broken method - if not npropdef.mpropdef.is_intro then - self.error(at, "Error: `autoinit` cannot be set on redefinitions.") - continue - end - - for param in sig.mparameters do - var ret_type = param.mtype - var mparameter = new MParameter(param.name, ret_type, false, ret_type isa MNullableType) - mparameters.add(mparameter) - end - initializers.add(npropdef.mpropdef.mproperty) - npropdef.mpropdef.mproperty.is_autoinit = true + mparameters.add_all sig.mparameters + initializers.add(mpropdef.mproperty) + mpropdef.mproperty.is_autoinit = true end if npropdef isa AAttrPropdef then var mreadpropdef = npropdef.mreadpropdef - if mreadpropdef == null or mreadpropdef.msignature == null then return # Skip broken attribute + if mreadpropdef == null then return # Skip broken attribute + var msignature = mreadpropdef.msignature + if msignature == null then return # Skip broken attribute if npropdef.noinit then continue # Skip noinit attribute var atlateinit = npropdef.get_single_annotation("lateinit", self) if atlateinit != null then @@ -214,25 +202,28 @@ redef class ModelBuilder mreadpropdef.mproperty.is_autoinit = true continue end - if npropdef.has_value then continue - var paramname = mreadpropdef.mproperty.name - var ret_type = mreadpropdef.msignature.return_mtype - if ret_type == null then return - var mparameter = new MParameter(paramname, ret_type, false, ret_type isa MNullableType) - mparameters.add(mparameter) + if npropdef.has_value and not npropdef.is_optional then continue var msetter = npropdef.mwritepropdef if msetter == null then # No setter, it is a readonly attribute, so just add it + var paramname = mreadpropdef.mproperty.name + var ret_type = msignature.return_mtype + if ret_type == null then return + var mparameter = new MParameter(paramname, ret_type, false) + mparameters.add(mparameter) + initializers.add(npropdef.mpropdef.mproperty) npropdef.mpropdef.mproperty.is_autoinit = true else # Add the setter to the list + mparameters.add_all msetter.msignature.mparameters initializers.add(msetter.mproperty) msetter.mproperty.is_autoinit = true end end end + var the_root_init_mmethod = self.the_root_init_mmethod if the_root_init_mmethod == null then return # Look for most-specific new-stype init definitions @@ -287,13 +278,7 @@ redef class ModelBuilder 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) - # Because the last parameter of setters is never default, try to default them for the autoinit. - for param in sig.mparameters do - if not param.is_default and param.mtype isa MNullableType then - param = new MParameter(param.name, param.mtype, param.is_vararg, true) - end - mparameters.add(param) - end + mparameters.add_all(sig.mparameters) else # TODO attributes? abort @@ -315,7 +300,12 @@ redef class ModelBuilder 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(", ")})") + var proposal = new ArraySet[MProperty] + for spd2 in spropdefs do + proposal.add_all spd2.initializers + end + proposal.add_all initializers + self.error(nclassdef, "Error: cannot generate automatic init for class {mclassdef.mclass}. Conflict in the order in inherited initializers {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")}). Use `autoinit` to order initializers. eg `autoinit {proposal.join(", ")}`") # TODO: invalidate the initializer to avoid more errors return end @@ -359,14 +349,13 @@ redef class ModelBuilder end # Else create the local implicit basic init definition - var mprop = the_root_init_mmethod.as(not null) + var mprop = the_root_init_mmethod var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) mpropdef.has_supercall = true mpropdef.initializers.add_all(initializers) var msignature = new MSignature(mparameters, null) 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) mclassdef.mclass.root_init = mpropdef end @@ -384,7 +373,7 @@ redef class ModelBuilder mtype = mtype.undecorate if mtype isa MClassType then vis_type = mtype.mclass.visibility - mmodule_type = mtype.mclass.intro.mmodule + mmodule_type = mtype.mclass.intro_mmodule else if mtype isa MVirtualType then vis_type = mtype.mproperty.visibility mmodule_type = mtype.mproperty.intro_mclassdef.mmodule @@ -392,6 +381,10 @@ redef class ModelBuilder # nothing, always visible else if mtype isa MNullType then # nothing to do. + else if mtype isa MBottomType then + # nothing to do. + else if mtype isa MErrorType then + # nothing to do. else node.debug "Unexpected type {mtype}" abort @@ -451,8 +444,7 @@ redef class ModelBuilder var vt = t.mproperty # Because `vt` is possibly unchecked, we have to do the bound-lookup manually var defs = vt.lookup_definitions(mmodule, recv) - # TODO something to manage correctly bound conflicts - assert not defs.is_empty + if defs.is_empty then return false nexts = new Array[MType] for d in defs do var next = defs.first.bound @@ -497,9 +489,6 @@ end redef class AClassdef # 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 end redef class MClass @@ -601,7 +590,7 @@ redef class APropdef var mdoc = ndoc.to_mdoc mpropdef.mdoc = mdoc mdoc.original_mentity = mpropdef - else if mpropdef.is_intro and mpropdef.mproperty.visibility >= protected_visibility then + else if mpropdef.is_intro and mpropdef.mproperty.visibility >= protected_visibility and mpropdef.name != "new" then modelbuilder.advice(self, "missing-doc", "Documentation warning: Undocumented property `{mpropdef.mproperty}`") end @@ -640,12 +629,12 @@ redef class APropdef return false end - # Check for full-name conflicts in the project. - # A public property should have a unique qualified name `project::class::prop`. + # Check for full-name conflicts in the package. + # A public property should have a unique qualified name `package::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 + if other != mprop and other.intro_mclassdef.mmodule.mgroup != null and other.intro_mclassdef.mmodule.mgroup.mpackage == mprop.intro_mclassdef.mmodule.mgroup.mpackage 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 @@ -682,14 +671,13 @@ redef class ASignature # Visit and fill information about a signature private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool do - var mmodule = mclassdef.mmodule var param_names = self.param_names var param_types = self.param_types for np in self.n_params do param_names.add(np.n_id.text) var ntype = np.n_type if ntype != null then - var mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true) + var mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true) if mtype == null then return false # Skip error for i in [0..param_names.length-param_types.length[ do param_types.add(mtype) @@ -706,7 +694,7 @@ redef class ASignature end var ntype = self.n_type if ntype != null then - self.ret_type = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true) + self.ret_type = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true) if self.ret_type == null then return false # Skip error end @@ -720,17 +708,18 @@ redef class ASignature for np in self.n_params do var ntype = np.n_type if ntype != null then - if modelbuilder.resolve_mtype(mclassdef.mmodule, mclassdef, ntype) == null then + if modelbuilder.resolve_mtype(mclassdef, ntype) == null then res = false end end end var ntype = self.n_type if ntype != null then - if modelbuilder.resolve_mtype(mclassdef.mmodule, mclassdef, ntype) == null then + if modelbuilder.resolve_mtype(mclassdef, ntype) == null then res = false end end + if not res then is_broken = true return res end end @@ -743,6 +732,8 @@ end redef class AMethPropdef redef type MPROPDEF: MMethodDef + # Is the method annotated `autoinit`? + var is_autoinit = false # Can self be used as a root init? private fun look_like_a_root_init(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool @@ -778,22 +769,21 @@ redef class AMethPropdef do var n_kwinit = n_kwinit var n_kwnew = n_kwnew - var is_init = n_kwinit != null or n_kwnew != null + var is_new = n_kwnew != null + var is_init = n_kwinit != null or is_new var name: String var amethodid = self.n_methid var name_node: ANode if amethodid == null then - if not is_init then - name = "main" - name_node = self - else if n_kwinit != null then + if n_kwinit != null then name = "init" name_node = n_kwinit else if n_kwnew != null then name = "new" name_node = n_kwnew else - abort + name = "main" + name_node = self end else if amethodid isa AIdMethid then name = amethodid.n_id.text @@ -831,17 +821,21 @@ redef class AMethPropdef end if mprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) - mprop = new MMethod(mclassdef, name, mvisibility) + mprop = new MMethod(mclassdef, name, self.location, mvisibility) if look_like_a_root_init and modelbuilder.the_root_init_mmethod == null then modelbuilder.the_root_init_mmethod = mprop mprop.is_root_init = true end mprop.is_init = is_init - mprop.is_new = n_kwnew != null - if mprop.is_new then mclassdef.mclass.has_new_factory = true + mprop.is_new = is_new + if is_new then mclassdef.mclass.has_new_factory = true if name == "sys" then mprop.is_toplevel = true # special case for sys allowed in `new` factories - self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then + mprop.is_broken = true + return + end else + if mprop.is_broken 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 @@ -850,7 +844,10 @@ redef class AMethPropdef 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) + if not check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, p) then + mprop.is_broken = true + return + end break end end @@ -875,11 +872,12 @@ redef class AMethPropdef do var mpropdef = self.mpropdef if mpropdef == null then return # Error thus skiped + var mproperty = mpropdef.mproperty var mclassdef = mpropdef.mclassdef var mmodule = mclassdef.mmodule var nsig = self.n_signature - if mpropdef.mproperty.is_root_init and not mclassdef.is_intro then + if mproperty.is_root_init and not mclassdef.is_intro then var root_init = mclassdef.mclass.root_init if root_init != null then # Inherit the initializers by refinement @@ -909,7 +907,7 @@ redef class AMethPropdef # FIXME: do not inherit from the intro, but from the most specific var msignature: nullable MSignature = null if not mpropdef.is_intro then - msignature = mpropdef.mproperty.intro.msignature + msignature = mproperty.intro.msignature if msignature == null then return # Skip error # The local signature is adapted to use the local formal types, if any. @@ -919,14 +917,14 @@ redef class AMethPropdef if param_names.length != msignature.arity then var node: ANode if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mpropdef.mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mpropdef.mproperty.full_name}`.") + modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mproperty.full_name}`.") return end - else if mpropdef.mproperty.is_init and not mpropdef.mproperty.is_new then + else if mproperty.is_init and not mproperty.is_new then # FIXME UGLY: inherit signature from a super-constructor for msupertype in mclassdef.supertypes do msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype) - var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name) + var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mproperty.name) if candidate != null then if msignature == null then msignature = candidate.intro.as(MMethodDef).msignature @@ -957,26 +955,20 @@ redef class AMethPropdef var mparameters = new Array[MParameter] for i in [0..param_names.length[ do - var is_default = false - if vararg_rank == -1 and param_types[i] isa MNullableType then - if i < param_names.length-1 or accept_special_last_parameter then - is_default = true - end - end - var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank, is_default) + var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank) if nsig != null then nsig.n_params[i].mparameter = mparameter mparameters.add(mparameter) end # In `new`-factories, the return type is by default the classtype. - if ret_type == null and mpropdef.mproperty.is_new then ret_type = mclassdef.mclass.mclass_type + if ret_type == null and mproperty.is_new then ret_type = mclassdef.mclass.mclass_type # Special checks for operator methods if not accept_special_last_parameter and mparameters.not_empty and mparameters.last.is_vararg then - modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mpropdef.mproperty.name}`.") + modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mproperty.name}`.") end if ret_type == null and return_is_mandatory then - modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mpropdef.mproperty.name}`.") + modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mproperty.name}`.") end msignature = new MSignature(mparameters, ret_type) @@ -988,6 +980,17 @@ redef class AMethPropdef # 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.") + + var atautoinit = self.get_single_annotation("autoinit", modelbuilder) + if atautoinit != null then + if not mpropdef.is_intro then + modelbuilder.error(atautoinit, "Error: `autoinit` cannot be set on redefinitions.") + else if not mclassdef.is_intro then + modelbuilder.error(atautoinit, "Error: `autoinit` cannot be used in class refinements.") + else + self.is_autoinit = true + end + end end redef fun check_signature(modelbuilder) @@ -997,13 +1000,14 @@ redef class AMethPropdef var mclassdef = mpropdef.mclassdef var mmodule = mclassdef.mmodule var nsig = self.n_signature - var mysignature = self.mpropdef.msignature + var mysignature = mpropdef.msignature if mysignature == null then return # Error thus skiped # Check if nsig != null then if not nsig.check_signature(modelbuilder, mclassdef) then - self.mpropdef.msignature = null # invalidate + mpropdef.msignature = null # invalidate + mpropdef.is_broken = true return # Forward error end end @@ -1017,8 +1021,9 @@ redef class AMethPropdef var precursor_ret_type = msignature.return_mtype var ret_type = mysignature.return_mtype if ret_type != null and precursor_ret_type == null then - modelbuilder.error(nsig.n_type.as(not null), "Redef Error: `{mpropdef.mproperty}` is a procedure, not a function.") - self.mpropdef.msignature = null + modelbuilder.error(nsig.n_type, "Redef Error: `{mpropdef.mproperty}` is a procedure, not a function.") + mpropdef.msignature = null + mpropdef.is_broken = true return end @@ -1030,7 +1035,8 @@ redef class AMethPropdef var node = nsig.n_params[i] if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then modelbuilder.error(node, "Redef Error: expected `{prt}` for parameter `{mysignature.mparameters[i].name}'; got `{myt}`.") - self.mpropdef.msignature = null + mpropdef.msignature = null + mpropdef.is_broken = true end end end @@ -1043,12 +1049,13 @@ redef class AMethPropdef ret_type = precursor_ret_type else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then modelbuilder.error(node, "Redef Error: expected `{precursor_ret_type}` for return type; got `{ret_type}`.") - self.mpropdef.msignature = null + mpropdef.msignature = null + mpropdef.is_broken = true end end end - if mysignature.arity > 0 then + if nsig != null then # Check parameters visibility for i in [0..mysignature.arity[ do var nt = nsig.n_params[i].n_type @@ -1063,6 +1070,8 @@ redef class AMethPropdef # For parameters, type is always useless in a redef. # For return type, type is useless if not covariant with introduction. redef fun check_repeated_types(modelbuilder) do + var mpropdef = self.mpropdef + if mpropdef == null then return if mpropdef.is_intro or n_signature == null then return # check params for param in n_signature.n_params do @@ -1135,14 +1144,21 @@ end redef class AAttrPropdef redef type MPROPDEF: MAttributeDef + # The static type of the property (declared, inferred or inherited) + # This attribute is also used to check if the property was analyzed and is valid. + var mtype: nullable MType + # Is the node tagged `noinit`? var noinit = false # Is the node tagged lazy? var is_lazy = false - # Has the node a default value? - # Could be through `n_expr` or `n_block` + # Is the node tagged optional? + var is_optional = false + + # Does the node have a default value? + # Could be through `n_expr`, `n_block` or `is_lazy` var has_value = false # The guard associated to a lazy attribute. @@ -1168,7 +1184,7 @@ redef class AAttrPropdef modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.") end - var mprop = new MAttribute(mclassdef, "_" + name, private_visibility) + var mprop = new MAttribute(mclassdef, "_" + name, self.location, private_visibility) var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) self.mpropdef = mpropdef modelbuilder.mpropdef2npropdef[mpropdef] = self @@ -1178,14 +1194,24 @@ redef class AAttrPropdef 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 = new MMethod(mclassdef, readname, self.location, mvisibility) + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then + mreadprop.is_broken = true + return + end else + if mreadprop.is_broken then return if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop) end mclassdef.mprop2npropdef[mreadprop] = self + var attr_mpropdef = mpropdef + if attr_mpropdef != null then + mreadprop.getter_for = attr_mpropdef.mproperty + attr_mpropdef.mproperty.getter = mreadprop + end + var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) self.mreadpropdef = mreadpropdef modelbuilder.mpropdef2npropdef[mreadpropdef] = self @@ -1209,8 +1235,7 @@ redef class AAttrPropdef return end if atabstract != null then - modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot be abstract.") - return + modelbuilder.warning(atnoinit, "useless-noautoinit", "Warning: superfluous `noautoinit` on abstract attribute.") end end @@ -1231,11 +1256,21 @@ redef class AAttrPropdef return end is_lazy = true - var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility) + var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, self.location, none_visibility) + mlazyprop.is_fictive = true var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location) + mlazypropdef.is_fictive = true self.mlazypropdef = mlazypropdef end + var atoptional = self.get_single_annotation("optional", modelbuilder) + if atoptional != null then + if not has_value then + modelbuilder.error(atoptional, "Error: `optional` attributes need a default value.") + end + is_optional = true + end + var atreadonly = self.get_single_annotation("readonly", modelbuilder) if atreadonly != null then if not has_value then @@ -1245,6 +1280,10 @@ redef class AAttrPropdef return end + if not mclassdef.is_intro and not has_value and not noinit then + modelbuilder.advice(self, "attr-in-refinement", "Warning: attributes in refinement need a value or `noautoinit`.") + end + var writename = name + "=" var atwritable = self.get_single_annotation("writable", modelbuilder) if atwritable != null then @@ -1260,12 +1299,18 @@ redef class AAttrPropdef if atwritable != null then mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility) else - mvisibility = private_visibility + mvisibility = mreadprop.visibility + # By default, use protected visibility at most + if mvisibility > protected_visibility then mvisibility = protected_visibility + end + mwriteprop = new MMethod(mclassdef, writename, self.location, mvisibility) + if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then + mwriteprop.is_broken = true + return end - mwriteprop = new MMethod(mclassdef, writename, mvisibility) - if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return mwriteprop.deprecation = mreadprop.deprecation else + if mwriteprop.is_broken then return if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return if atwritable != null then check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop) @@ -1273,11 +1318,32 @@ redef class AAttrPropdef end mclassdef.mprop2npropdef[mwriteprop] = self + if attr_mpropdef != null then + mwriteprop.setter_for = attr_mpropdef.mproperty + attr_mpropdef.mproperty.setter = mwriteprop + end + var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) self.mwritepropdef = mwritepropdef modelbuilder.mpropdef2npropdef[mwritepropdef] = self mwritepropdef.mdoc = mreadpropdef.mdoc if atabstract != null then mwritepropdef.is_abstract = true + + var atautoinit = self.get_single_annotation("autoinit", modelbuilder) + if atautoinit != null then + if has_value then + modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot have an initial value.") + else if not mwritepropdef.is_intro then + modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot be set on redefinitions.") + else if not mclassdef.is_intro then + modelbuilder.error(atautoinit, "Error: `autoinit` attributes cannot be used in class refinements.") + else if atabstract == null then + modelbuilder.warning(atautoinit, "useless-autoinit", "Warning: superfluous `autoinit` on attribute.") + end + else if atabstract != null then + # By default, abstract attribute are not autoinit + noinit = true + end end redef fun build_signature(modelbuilder) @@ -1292,7 +1358,7 @@ redef class AAttrPropdef var ntype = self.n_type if ntype != null then - mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true) + mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true) if mtype == null then return end @@ -1312,35 +1378,12 @@ redef class AAttrPropdef var nexpr = self.n_expr if mtype == null then if nexpr != null then - if nexpr isa ANewExpr then - mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true) - else if nexpr isa AIntExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int") - if cla != null then mtype = cla.mclass_type - else if nexpr isa AFloatExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float") - if cla != null then mtype = cla.mclass_type - else if nexpr isa ACharExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char") - if cla != null then mtype = cla.mclass_type - else if nexpr isa ABoolExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool") - if cla != null then mtype = cla.mclass_type - else if nexpr isa ASuperstringExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") - if cla != null then mtype = cla.mclass_type - else if nexpr isa AStringFormExpr then - 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 `{mreadpropdef}`. Implicit typing allowed only for literals and new.") - end - + mtype = infer_static_type(modelbuilder, nexpr, mclassdef, mmodule, mreadpropdef) if mtype == null then return end else if ntype != null and inherited_type == mtype then if nexpr isa ANewExpr then - var xmtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true) + var xmtype = modelbuilder.resolve_mtype_unchecked(mclassdef, nexpr.n_type, true) if xmtype == mtype then modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition") end @@ -1352,6 +1395,8 @@ redef class AAttrPropdef return end + self.mtype = mtype + if mpropdef != null then mpropdef.static_mtype = mtype end @@ -1363,26 +1408,99 @@ redef class AAttrPropdef var mwritepropdef = self.mwritepropdef if mwritepropdef != null then + var mwritetype = mtype + if is_optional then + mwritetype = mwritetype.as_nullable + end var name: String name = n_id2.text - var mparameter = new MParameter(name, mtype, false, false) + var mparameter = new MParameter(name, mwritetype, false) var msignature = new MSignature([mparameter], null) mwritepropdef.msignature = msignature end var mlazypropdef = self.mlazypropdef if mlazypropdef != null then - mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type + mlazypropdef.static_mtype = mmodule.bool_type end check_repeated_types(modelbuilder) end + # Detect the static type from the value assigned to the attribute `self` + # + # Return the static type if it can be safely inferred. + private fun infer_static_type(modelbuilder: ModelBuilder, nexpr: AExpr, + mclassdef: MClassDef, mmodule: MModule, mreadpropdef: MPropDef): nullable MType + do + var mtype = null + if nexpr isa ANewExpr then + mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, nexpr.n_type, true) + else if nexpr isa AAsCastExpr then + mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, nexpr.n_type, true) + else if nexpr isa AIntegerExpr then + var cla: nullable MClass = null + if nexpr.value isa Int then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int") + else if nexpr.value isa Byte then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Byte") + else if nexpr.value isa Int8 then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int8") + else if nexpr.value isa Int16 then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int16") + else if nexpr.value isa UInt16 then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "UInt16") + else if nexpr.value isa Int32 then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int32") + else if nexpr.value isa UInt32 then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "UInt32") + else + # Should not happen, and should be updated as new types are added + abort + end + if cla != null then mtype = cla.mclass_type + else if nexpr isa AFloatExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float") + if cla != null then mtype = cla.mclass_type + else if nexpr isa ACharExpr then + var cla: nullable MClass + if nexpr.is_ascii then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Byte") + else if nexpr.is_code_point then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int") + else + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char") + end + if cla != null then mtype = cla.mclass_type + else if nexpr isa ABoolExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool") + if cla != null then mtype = cla.mclass_type + else if nexpr isa ASuperstringExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") + if cla != null then mtype = cla.mclass_type + else if nexpr isa AStringFormExpr then + var cla: nullable MClass + if nexpr.is_bytestring then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bytes") + else if nexpr.is_re then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Regex") + else if nexpr.is_string then + cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") + else + abort + end + if cla != null then mtype = cla.mclass_type + else + modelbuilder.error(self, "Error: untyped attribute `{mreadpropdef}`. Implicit typing allowed only for literals and new.") + end + return mtype + end + redef fun check_signature(modelbuilder) do var mpropdef = self.mpropdef if mpropdef == null then return # Error thus skipped var ntype = self.n_type - var mtype = self.mpropdef.static_mtype + var mtype = self.mtype if mtype == null then return # Error thus skipped var mclassdef = mpropdef.mclassdef @@ -1390,11 +1508,11 @@ redef class AAttrPropdef # Check types if ntype != null then - if modelbuilder.resolve_mtype(mmodule, mclassdef, ntype) == null then return + if modelbuilder.resolve_mtype(mclassdef, ntype) == null then return end var nexpr = n_expr if nexpr isa ANewExpr then - if modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type) == null then return + if modelbuilder.resolve_mtype(mclassdef, nexpr.n_type) == null then return end # Lookup for signature in the precursor @@ -1482,6 +1600,8 @@ redef class AAttrPropdef # Type is useless if the attribute type is the same thant the intro. redef fun check_repeated_types(modelbuilder) do + var mreadpropdef = self.mreadpropdef + if mreadpropdef == null then return if mreadpropdef.is_intro or n_type == null then return # get intro var intro = mreadpropdef.mproperty.intro @@ -1495,7 +1615,7 @@ redef class AAttrPropdef ntype = n_intro.n_type.mtype end # check - if ntype ==null or ntype != n_type.mtype then return + if ntype == null or ntype != n_type.mtype or mpropdef == null then return modelbuilder.advice(n_type, "useless-signature", "Warning: useless type repetition on redefined attribute `{mpropdef.name}`") end end @@ -1505,31 +1625,33 @@ redef class ATypePropdef redef fun build_property(modelbuilder, mclassdef) do - var name = self.n_id.text - var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name) + var name = self.n_qid.n_id.text + var mprop = modelbuilder.try_get_mproperty_by_name(self.n_qid, mclassdef, name) if mprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) - mprop = new MVirtualTypeProp(mclassdef, name, mvisibility) + mprop = new MVirtualTypeProp(mclassdef, name, self.location, mvisibility) for c in name.chars do if c >= 'a' and c<= 'z' then - modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.") + modelbuilder.warning(n_qid, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.") break end - if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return else - if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return + if mprop.is_broken then return assert mprop isa MVirtualTypeProp check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) end - mclassdef.mprop2npropdef[mprop] = self 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 + if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, not mpropdef.is_intro, mprop) then + mpropdef.is_broken =true + end + mclassdef.mprop2npropdef[mprop] = self + modelbuilder.mpropdef2npropdef[mpropdef] = self set_doc(mpropdef, modelbuilder) var atfixed = get_single_annotation("fixed", modelbuilder) @@ -1543,11 +1665,10 @@ redef class ATypePropdef var mpropdef = self.mpropdef if mpropdef == null then return # Error thus skipped var mclassdef = mpropdef.mclassdef - var mmodule = mclassdef.mmodule var mtype: nullable MType = null var ntype = self.n_type - mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true) + mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true) if mtype == null then return mpropdef.bound = mtype @@ -1569,7 +1690,7 @@ redef class ATypePropdef var anchor = mclassdef.bound_mtype var ntype = self.n_type - if modelbuilder.resolve_mtype(mmodule, mclassdef, ntype) == null then + if modelbuilder.resolve_mtype(mclassdef, ntype) == null then mpropdef.bound = null return end @@ -1577,14 +1698,13 @@ redef class ATypePropdef # Check redefinitions for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do var supbound = p.bound - if supbound == null then break # broken super bound, skip error + if supbound == null or supbound isa MBottomType or p.is_broken 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 end if p.mclassdef.mclass == mclassdef.mclass then - # Still a warning to pass existing bad code - modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.") + modelbuilder.error(n_type, "Redef Error: a virtual type cannot be refined.") break end if not modelbuilder.check_subtype(n_type, mmodule, anchor, bound, supbound) then