X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index cd50d4a..8529ed6 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -42,6 +42,17 @@ redef class ModelBuilder # Public clients need to use `mpropdef2node` to access stuff. private var mpropdef2npropdef = new HashMap[MPropDef, APropdef] + # Associate a `npropdef` with its `mpropdef` + # + # Be careful, this method is unsafe, no checking is done when it's used. + # The safe way to add method it's to use the `build_property` + # + # See `mpropdef2npropdef` + fun unsafe_add_mpropdef2npropdef(mpropdef: MPropDef,npropdef: APropdef) + do + mpropdef2npropdef[mpropdef] = npropdef + end + # Retrieve the associated AST node of a mpropertydef. # This method is used to associate model entity with syntactic entities. # @@ -55,10 +66,9 @@ redef class ModelBuilder 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 + # Fall back to the class node if any. + res = mclassdef2nclassdef.get_or_null(mpropdef.mclassdef) + if res != null then return res return null end @@ -80,7 +90,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 @@ -111,7 +120,7 @@ redef class ModelBuilder if not check_virtual_types_circularity(npropdef, mpropdef.mproperty, mclassdef.bound_mtype, mclassdef.mmodule) then # Invalidate the bound mpropdef.is_broken = true - mpropdef.bound = new MBottomType(mclassdef.mmodule.model) + mpropdef.bound = new MErrorType(mclassdef.mmodule.model) end end for npropdef in nclassdef2.n_propdefs do @@ -145,18 +154,15 @@ 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] 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 there already a constructor defined? @@ -167,12 +173,14 @@ redef class ModelBuilder if mpropdef.mproperty.is_root_init then assert defined_init == null defined_init = mpropdef - else if mpropdef.mproperty.name == "init" then - # An explicit old-style init named "init", so return + end + if mpropdef.name == "defaultinit" then return end end + if mclassdef.default_init != null then return + if not nclassdef isa AStdClassdef then return # Collect undefined attributes @@ -185,7 +193,6 @@ redef class ModelBuilder if mpropdef == null then return # Skip broken method var sig = mpropdef.msignature if sig == null then continue # Skip broken method - mparameters.add_all sig.mparameters initializers.add(mpropdef.mproperty) mpropdef.mproperty.is_autoinit = true @@ -204,19 +211,21 @@ redef class ModelBuilder mreadpropdef.mproperty.is_autoinit = true continue end - if npropdef.has_value then continue - 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) + 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 @@ -227,10 +236,12 @@ redef class ModelBuilder if the_root_init_mmethod == null then return # 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.error(nclassdef.location, "Error: `{mclassdef}` does not specialize `{the_root_init_mmethod.intro_mclassdef}`. Possible duplication of the root class `Object`?") - return + var spropdefs = new ArraySet[MMethodDef] + for x in mclassdef.in_hierarchy.direct_greaters do + var y = x.mclass.intro.default_init + if y == null then continue + if y.is_broken or y.msignature == null then return + spropdefs.add y end # Look at the autoinit class-annotation @@ -284,7 +295,7 @@ redef class ModelBuilder abort end end - else + else if spropdefs.not_empty then # Search the longest-one and checks for conflict var longest = spropdefs.first if spropdefs.length > 1 then @@ -323,42 +334,26 @@ redef class ModelBuilder mparameters.clear initializers.clear else - # 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 + mparameters.prepend longest.msignature.mparameters initializers.prepend longest.initializers end end end - # If we already have a basic init definition, then setup its initializers - if defined_init != null then - defined_init.initializers.add_all(initializers) + # Create a specific new autoinit constructor + do + var mprop = new MMethod(mclassdef, "defaultinit", nclassdef.location, public_visibility) + mprop.is_init = true + var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) + mpropdef.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) - mclassdef.mclass.root_init = defined_init - return + mpropdef.msignature = msignature + mclassdef.default_init = mpropdef + self.toolcontext.info("{mclassdef} gets a free auto constructor `{mpropdef}{msignature}`. {spropdefs}", 3) + mclassdef.mclass.the_root_init_mmethod = the_root_init_mmethod end - - # Else create the local implicit basic init definition - 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 # Check the visibility of `mtype` as an element of the signature of `mpropdef`. @@ -384,6 +379,8 @@ redef class ModelBuilder # 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 @@ -488,18 +485,13 @@ 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 # The base init of the class. - # Used to get the common new_msignature and initializers # - # TODO: Where to put this information is not clear because unlike other - # informations, the initialisers are stable in a same class. - var root_init: nullable MMethodDef = null + # TODO: merge with `root_init` and `ModelBuilder::the_root_init_mmethod` if possible + var the_root_init_mmethod: nullable MMethod = null end redef class MClassDef @@ -592,7 +584,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 @@ -673,14 +665,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) @@ -697,7 +688,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 @@ -711,14 +702,14 @@ 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 @@ -772,22 +763,27 @@ 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 + var is_old_style_init = false 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 + var old_style_annot = get_single_annotation("old_style_init", modelbuilder) + if old_style_annot != null or self.n_signature.n_params.not_empty then + name = "defaultinit" + if old_style_annot != null then is_old_style_init = true + end 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 @@ -815,7 +811,7 @@ redef class AMethPropdef var look_like_a_root_init = look_like_a_root_init(modelbuilder, mclassdef) var mprop: nullable MMethod = null - if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod) + if not is_init or n_kwredef != null or look_like_a_root_init then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod) if mprop == null and look_like_a_root_init then mprop = modelbuilder.the_root_init_mmethod var nb = n_block @@ -825,23 +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 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 - end + 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 @@ -862,6 +856,15 @@ redef class AMethPropdef mclassdef.mprop2npropdef[mprop] = self var mpropdef = new MMethodDef(mclassdef, mprop, self.location) + if mpropdef.name == "defaultinit" and mclassdef.is_intro then + assert mclassdef.default_init == null + mpropdef.is_old_style_init = is_old_style_init + mclassdef.default_init = mpropdef + if mpropdef.is_intro then + mpropdef.initializers.add mprop + mpropdef.is_calling_init = true + end + end set_doc(mpropdef, modelbuilder) @@ -878,20 +881,11 @@ 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 - var root_init = mclassdef.mclass.root_init - if root_init != null then - # Inherit the initializers by refinement - mpropdef.new_msignature = root_init.new_msignature - assert mpropdef.initializers.is_empty - mpropdef.initializers.add_all root_init.initializers - end - end - var accept_special_last_parameter = self.n_methid == null or self.n_methid.accept_special_last_parameter var return_is_mandatory = self.n_methid != null and self.n_methid.return_is_mandatory @@ -912,7 +906,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. @@ -922,14 +916,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 @@ -966,14 +960,14 @@ redef class AMethPropdef 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) @@ -1060,7 +1054,7 @@ redef class AMethPropdef 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 @@ -1159,8 +1153,11 @@ redef class AAttrPropdef # 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. @@ -1186,7 +1183,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 @@ -1196,14 +1193,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 @@ -1248,13 +1255,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 @@ -1287,10 +1302,14 @@ redef class AAttrPropdef # By default, use protected visibility at most if mvisibility > protected_visibility then mvisibility = protected_visibility end - mwriteprop = new MMethod(mclassdef, writename, mvisibility) - if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return + 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.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) @@ -1298,6 +1317,11 @@ 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 @@ -1333,7 +1357,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 @@ -1353,53 +1377,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 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 = 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 @@ -1424,20 +1407,120 @@ 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) + 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_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 if nexpr isa AArrayExpr and nexpr.n_type == null and nexpr.n_exprs.not_empty then + # Non-empty arrays without an explicit type + + var item_mtypes = new Set[MType] + var fails = false + for node in nexpr.n_exprs do + var item_mtype = infer_static_type(modelbuilder, node, mclassdef, mmodule, mreadpropdef) + if item_mtype == null then + fails = true + else + item_mtypes.add item_mtype + end + end + + if fails then return null # Failed to infer some types + + if item_mtypes.length > 1 then + modelbuilder.error(self, "Type Error: ambiguous array type {item_mtypes.join(" ")}") + end + + mtype = mmodule.array_type(item_mtypes.first) + else if nexpr isa AUminusExpr and (nexpr.n_expr isa AIntegerExpr or nexpr.n_expr isa AFloatExpr) then + # The Int and Float unary - is defined in `kernel`, so this may + # result in an invalid behavior when using a custom kernel. + # A workaround is to declare the attribute static type. + # This is still very useful, especially to novice programmers. + mtype = infer_static_type(modelbuilder, nexpr.n_expr, mclassdef, mmodule, mreadpropdef) + else if nexpr isa AOnceExpr then + mtype = infer_static_type(modelbuilder, nexpr.n_expr, mclassdef, mmodule, mreadpropdef) + 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 @@ -1451,11 +1534,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 @@ -1568,16 +1651,17 @@ 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 else + if mprop.is_broken then return assert mprop isa MVirtualTypeProp check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) end @@ -1607,11 +1691,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 @@ -1633,7 +1716,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 @@ -1647,8 +1730,7 @@ redef class ATypePropdef 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