X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index 59d725b..6f7f209 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -42,6 +42,28 @@ 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 + + # Associate a `nclassdef` with its `mclassdef` + # + # Be careful, this method is unsafe, no checking is done when it's used. + # The safe way to add mclass it's to use the `build_property` + # + # See `mclassdef2nclassdef` + fun unsafe_add_mclassdef2nclassdef(mclassdef: MClassDef, nclassdef: AClassdef) + do + mclassdef2nclassdef[mclassdef] = nclassdef + end + # Retrieve the associated AST node of a mpropertydef. # This method is used to associate model entity with syntactic entities. # @@ -79,7 +101,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 +131,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 @@ -139,12 +160,16 @@ redef class ModelBuilder var mclassdef = nclassdef.mclassdef.as(not null) # Are we a refinement - if not mclassdef.is_intro then return + if not mclassdef.is_intro then + # Set the default_init of the mclassdef with the intro default_init + mclassdef.default_init = mclassdef.mclass.intro.default_init + return + end # 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] @@ -163,17 +188,15 @@ redef class ModelBuilder if mpropdef.mproperty.is_root_init then assert defined_init == null defined_init = mpropdef - else if mpropdef.mproperty.name == "autoinit" then - # An explicit old-style init named "init", so return + else if mpropdef.name == "defaultinit" then return end end - if mclassdef.auto_init != null then - return - end + if mclassdef.default_init != null then return - if not nclassdef isa AStdClassdef then return + # If the class is not AStdClassdef or it's an enum just return. No defaultinit is need. + if not nclassdef isa AStdClassdef or nclassdef.n_classkind isa AEnumClasskind then return # Collect undefined attributes var mparameters = new Array[MParameter] @@ -185,7 +208,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 @@ -230,8 +252,9 @@ redef class ModelBuilder # Look for most-specific new-stype init definitions var spropdefs = new ArraySet[MMethodDef] - for x in mclassdef.in_hierarchy.direct_greaters do - var y = x.mclass.intro.auto_init + + for x in mclassdef.get_direct_supermtype 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 @@ -289,30 +312,28 @@ redef class ModelBuilder end end else if spropdefs.not_empty then - # Search for inherited manual autoinit + # Search for inherited manual defaultinit var manual = null for s in spropdefs do if mpropdef2npropdef.has_key(s) then - self.toolcontext.info("{mclassdef} inherits a manual autoinit {s}", 3) - #mclassdef.autoinit = s - #return + self.toolcontext.info("{mclassdef} inherits a manual defaultinit {s}", 3) manual = s end end - # Search the longest-one and checks for conflict var longest = spropdefs.first if spropdefs.length > 1 then # part 1. find the longest list for spd in spropdefs do if spd.initializers.length > longest.initializers.length then longest = spd + if spd != manual and manual != null then - self.toolcontext.info("{mclassdef} conflict between manual autoinit {manual} and automatic autoinit {spd}.", 3) + self.toolcontext.info("{mclassdef} conflict between manual defaultinit {manual} and automatic defaultinit {spd}.", 3) end end # conflict with manual autoinit? if longest != manual and manual != null then - self.error(nclassdef, "Error: conflict between manual autoinit {manual} and automatic autoinit {longest}.") + self.error(nclassdef, "Error: conflict between manual defaultinit {manual} and automatic defaultinit {longest}.") end # part 2. compare # Check for conflict in the order of initializers @@ -355,15 +376,14 @@ redef class ModelBuilder # Create a specific new autoinit constructor do - var mprop = new MMethod(mclassdef, "autoinit", public_visibility) + 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) mpropdef.msignature = msignature - mclassdef.auto_init = mpropdef + mclassdef.default_init = mpropdef self.toolcontext.info("{mclassdef} gets a free auto constructor `{mpropdef}{msignature}`. {spropdefs}", 3) - #mclassdef.mclass.root_init = mpropdef mclassdef.mclass.the_root_init_mmethod = the_root_init_mmethod end end @@ -391,6 +411,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 @@ -500,12 +522,6 @@ end redef class MClass # The base init of the class. # - # 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 - - # The base init of the class. - # # TODO: merge with `root_init` and `ModelBuilder::the_root_init_mmethod` if possible var the_root_init_mmethod: nullable MMethod = null end @@ -681,14 +697,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) @@ -705,7 +720,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 @@ -719,14 +734,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 @@ -780,25 +795,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 - if self.n_signature.n_params.not_empty or get_single_annotation("old_style_init", modelbuilder) != null then - name = "autoinit" + 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 @@ -836,23 +853,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 @@ -873,13 +888,14 @@ redef class AMethPropdef mclassdef.mprop2npropdef[mprop] = self var mpropdef = new MMethodDef(mclassdef, mprop, self.location) - if mprop.name == "autoinit" and mclassdef.is_intro then - assert mclassdef.auto_init == null - mclassdef.auto_init = mpropdef - if mpropdef.is_intro then - mpropdef.initializers.add mprop - mpropdef.is_calling_init = true - end + 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 + # Set the initializers with the mproperty. + # This point is need when a super class define this own default_init and inherited class use the default_init generated automaticlely. + mpropdef.initializers.add mprop + mpropdef.is_calling_init = true end set_doc(mpropdef, modelbuilder) @@ -897,19 +913,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 - 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 @@ -930,7 +938,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. @@ -940,14 +948,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 @@ -984,14 +992,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) @@ -1180,10 +1188,14 @@ redef class AAttrPropdef # Is the node tagged optional? var is_optional = false - # Has the node a default value? - # Could be through `n_expr` or `n_block` + # Does the node have a default value? + # Could be through `n_expr`, `n_block` or `is_lazy` var has_value = false + # The name of the attribute + # Note: The name of the attribute is in reality the name of the mreadpropdef + var name: String = n_id2.text is lazy + # The guard associated to a lazy attribute. # Because some engines does not have a working `isset`, # this additional attribute is used to guard the lazy initialization. @@ -1198,38 +1210,14 @@ 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 not mclass.kind.need_init then - modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.") - end - var mprop = new MAttribute(mclassdef, "_" + name, private_visibility) - var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) - self.mpropdef = mpropdef - modelbuilder.mpropdef2npropdef[mpropdef] = self - end + if atabstract == null then build_attribute_property(modelbuilder, mclassdef) - 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 - else - 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 + # Construction of the read property. If it's not correctly built just return. + if not build_read_property(modelbuilder, mclassdef) then return - var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) - self.mreadpropdef = mreadpropdef - modelbuilder.mpropdef2npropdef[mreadpropdef] = self - set_doc(mreadpropdef, modelbuilder) - 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 @@ -1252,29 +1240,8 @@ redef class AAttrPropdef end end - var atlazy = self.get_single_annotation("lazy", modelbuilder) - var atlateinit = self.get_single_annotation("lateinit", modelbuilder) - if atlazy != null or atlateinit != null then - if atlazy != null and atlateinit != null then - modelbuilder.error(atlazy, "Error: `lazy` incompatible with `lateinit`.") - return - end - if not has_value then - if atlazy != null then - modelbuilder.error(atlazy, "Error: `lazy` attributes need a value.") - else if atlateinit != null then - modelbuilder.error(atlateinit, "Error: `lateinit` attributes need a value.") - end - has_value = true - return - end - is_lazy = true - var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility) - mlazyprop.is_fictive = true - var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location) - mlazypropdef.is_fictive = true - self.mlazypropdef = mlazypropdef - end + # Construction of the read property. If it's not correctly built just return. + if not build_lazy_property(modelbuilder, mclassdef) then return var atoptional = self.get_single_annotation("optional", modelbuilder) if atoptional != null then @@ -1297,6 +1264,88 @@ redef class AAttrPropdef modelbuilder.advice(self, "attr-in-refinement", "Warning: attributes in refinement need a value or `noautoinit`.") end + # Construction of the read property. If it's not correctly built just return. + if not build_write_property(modelbuilder, mclassdef, false) then return + + 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 + + # Build the attribute property + fun build_attribute_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) + do + var mclass = mclassdef.mclass + var attribute_name = "_" + name + + if not mclass.kind.need_init then + modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.") + end + 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 + end + + # Build the read method property to get the value of the attribute + # Return `true` if the property was correctly created else return `false`. + # Warning the signature of the property is not set. This step is done by `build_signature`. + fun build_read_property(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool + do + var mclass = mclassdef.mclass + + var readname = name + var mreadprop = modelbuilder.try_get_mproperty_by_name(self, mclassdef, readname).as(nullable MMethod) + if mreadprop == null then + var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) + 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 false + end + else + if mreadprop.is_broken then return false + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return false + 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 + set_doc(mreadpropdef, modelbuilder) + if mpropdef != null then mpropdef.mdoc = mreadpropdef.mdoc + + return true + end + + # Build the write method property to set the attribute value + # Return `true` if the property was correctly created else return `false`. + # Warning the signature of the property is not set. + fun build_write_property(modelbuilder: ModelBuilder, mclassdef: MClassDef, is_same_visibility: Bool): Bool + do + var mclass = mclassdef.mclass + var writename = name + "=" var atwritable = self.get_single_annotation("writable", modelbuilder) if atwritable != null then @@ -1304,7 +1353,7 @@ redef class AAttrPropdef writename = atwritable.arg_as_id(modelbuilder) or else writename end end - var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod) + var mwriteprop = modelbuilder.try_get_mproperty_by_name(self, mclassdef, writename).as(nullable MMethod) var nwkwredef: nullable Token = null if atwritable != null then nwkwredef = atwritable.n_kwredef if mwriteprop == null then @@ -1312,42 +1361,64 @@ redef class AAttrPropdef if atwritable != null then mvisibility = new_property_visibility(modelbuilder, mclassdef, atwritable.n_visibility) else - mvisibility = mreadprop.visibility + mvisibility = mreadpropdef.mproperty.visibility # By default, use protected visibility at most - if mvisibility > protected_visibility then mvisibility = protected_visibility + if mvisibility > protected_visibility and not is_same_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.deprecation = mreadprop.deprecation + 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 false + end + mwriteprop.deprecation = mreadpropdef.mproperty.deprecation else - if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return + if mwriteprop.is_broken then return false + if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return false if atwritable != null then check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop) end end mclassdef.mprop2npropdef[mwriteprop] = self + var attr_mpropdef = mpropdef + 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.") + return true + end + + # Build the lazy attribute property + # Return `true` if the property was correctly created else return `false`. + fun build_lazy_property(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool + do + var mclass = mclassdef.mclass + + var atlazy = self.get_single_annotation("lazy", modelbuilder) + var atlateinit = self.get_single_annotation("lateinit", modelbuilder) + if atlazy != null or atlateinit != null then + if atlazy != null and atlateinit != null then + modelbuilder.error(atlazy, "Error: `lazy` incompatible with `lateinit`.") + return false end - else if atabstract != null then - # By default, abstract attribute are not autoinit - noinit = true + if not has_value then + if atlazy != null then + modelbuilder.error(atlazy, "Error: `lazy` attributes need a value.") + else if atlateinit != null then + modelbuilder.error(atlateinit, "Error: `lateinit` attributes need a value.") + end + has_value = true + return false + end + create_lazy end + return true end redef fun build_signature(modelbuilder) @@ -1362,7 +1433,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 @@ -1382,62 +1453,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 AAsCastExpr 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: 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 = 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 @@ -1455,31 +1476,198 @@ redef class AAttrPropdef mpropdef.static_mtype = mtype end - do - var msignature = new MSignature(new Array[MParameter], mtype) - mreadpropdef.msignature = msignature - end + build_read_signature - 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, mwritetype, false) - var msignature = new MSignature([mparameter], null) - mwritepropdef.msignature = msignature - end + if self.mwritepropdef != null then build_write_signature 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 + # Build the read method signature + # `except`: mreadpropdef != null + # `expect`: mtype != null + fun build_read_signature + is + expect(mreadpropdef != null and mtype != null) + do + var msignature = new MSignature(new Array[MParameter], mtype) + mreadpropdef.msignature = msignature + end + + # Build the write method signature + # `except`: mwritepropdef != null + # `expect`: mtype != null + fun build_write_signature + is + expect(mwritepropdef != null and mtype != null) + do + var mwritetype = mtype.as(not null) + if is_optional then + mwritetype = mwritetype.as_nullable + end + var mparameter = new MParameter(name, mwritetype, false) + var msignature = new MSignature([mparameter], null) + mwritepropdef.msignature = msignature + end + + # Create a new setter for the attribute. + # + # `modelbuilder`: It's used to link the new `mwritepropdef` and `self` + # `visibility`: Is the setter has the same visibilty of the `mreadpropdef`. + # If `not is_same_visibility and mreadpropdef.mproperty.visibility > protected_visibility` the `mwritepropdef` visibility will be set to protected. + fun create_setter(modelbuilder: ModelBuilder, is_same_visibility: nullable Bool): AAttrPropdef + is + expect(mreadpropdef != null) # Use to define the visibility, the mclassdef and the doc of the `mwritepropdef` + do + if mwritepropdef != null then return self # Self already has a `mwritepropdef` + var same_visibility = false + if is_same_visibility != null then same_visibility = is_same_visibility + + self.build_write_property(modelbuilder, mreadpropdef.mclassdef, same_visibility) + self.build_write_signature + return self + end + + # Set the default `self` value + # + # `expr`: Represents the default value of the attribute. If `expr isa ABlockExpr` `self.n_block` will be set. + fun define_default(expr: AExpr): AAttrPropdef + do + self.has_value = true + if expr isa ABlockExpr then + self.n_block = expr + else + self.n_expr = expr + end + return self + end + + # Set `self` as optional + fun define_as_optional: AAttrPropdef + is + expect(has_value) + do + is_optional = true + return self + end + + # Create the lazy attribute. + # + # see `mlazypropdef` for more information about this property. + fun create_lazy: AAttrPropdef + is + expect(has_value and mpropdef != null) # The only way to get a null `mpropdef` is when the attribute is defined as `abstract`. But if the attribute has a value, it cannot be abstract. + do + if self.mlazypropdef != null then return self # Self already has a `mlazypropdef` + is_lazy = true + var mlazyprop = new MAttribute(mpropdef.mclassdef, "lazy _" + name, self.location, none_visibility) + mlazyprop.is_fictive = true + var mlazypropdef = new MAttributeDef(mpropdef.mclassdef, mlazyprop, self.location) + mlazypropdef.is_fictive = true + self.mlazypropdef = mlazypropdef + return self + 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 @@ -1493,11 +1681,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 @@ -1614,12 +1802,13 @@ redef class ATypePropdef 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_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 @@ -1649,11 +1838,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 @@ -1675,7 +1863,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 @@ -1689,8 +1877,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