# Analysis and verification of property definitions to instantiate model element
module modelize_property
-import modelize_class
+intrude import modelize_class
private import annotation
redef class ToolContext
+ # Run `AClassdef::build_property` on the classdefs of each module
var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase])
end
end
redef class ModelBuilder
- # Register the npropdef associated to each mpropdef
- # FIXME: why not refine the `MPropDef` class with a nullable attribute?
- var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
+ # Registration of the npropdef associated to each mpropdef.
+ #
+ # Public clients need to use `mpropdef2node` to access stuff.
+ private var mpropdef2npropdef = new HashMap[MPropDef, APropdef]
+
+ # Retrieve the associated AST node of a mpropertydef.
+ # This method is used to associate model entity with syntactic entities.
+ #
+ # If the property definition is not associated with a node, returns `null`.
+ fun mpropdef2node(mpropdef: MPropDef): nullable ANode
+ do
+ var res
+ res = mpropdef2npropdef.get_or_null(mpropdef)
+ if res != null then
+ # Run the phases on it
+ toolcontext.run_phases_on_npropdef(res)
+ return res
+ end
+ if mpropdef isa MMethodDef and mpropdef.mproperty.is_root_init then
+ res = mclassdef2nclassdef.get_or_null(mpropdef.mclassdef)
+ if res != null then return res
+ end
+ return null
+ end
+
+ # Retrieve all the attributes nodes localy definied
+ # FIXME think more about this method and how the separations separate/global and ast/model should be done.
+ fun collect_attr_propdef(mclassdef: MClassDef): Array[AAttrPropdef]
+ do
+ var res = new Array[AAttrPropdef]
+ var n = mclassdef2nclassdef.get_or_null(mclassdef)
+ if n == null then return res
+ for npropdef in n.n_propdefs do
+ if npropdef isa AAttrPropdef then
+ # Run the phases on it
+ toolcontext.run_phases_on_npropdef(npropdef)
+ res.add(npropdef)
+ end
+ end
+ return res
+ end
# Build the properties of `nclassdef`.
# REQUIRE: all superclasses are built.
build_properties(mclassdef2nclassdef[superclassdef])
end
+ mclassdef.build_self_type(self, nclassdef)
for nclassdef2 in nclassdef.all_defs do
for npropdef in nclassdef2.n_propdefs do
npropdef.build_property(self, mclassdef)
end
if npropdef isa AAttrPropdef then
if npropdef.mpropdef == null then return # Skip broken attribute
- var at = npropdef.get_single_annotation("noinit", self)
- if at != null then
- npropdef.noinit = true
- if npropdef.n_expr != null then
- self.error(at, "Error: `noinit` attributes cannot have an initial value")
- end
- continue # Skip noinit attributes
+ if npropdef.noinit then continue # Skip noinit attribute
+ var atautoinit = npropdef.get_single_annotation("autoinit", self)
+ if atautoinit != null then
+ # For autoinit attributes, call the reader to force
+ # the lazy initialization of the attribute.
+ initializers.add(npropdef.mreadpropdef.mproperty)
+ continue
end
- if npropdef.n_expr != null then continue
+ if npropdef.has_value then continue
var paramname = npropdef.mpropdef.mproperty.name.substring_from(1)
var ret_type = npropdef.mpropdef.static_mtype
if ret_type == 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.fatal_error(nclassdef.location, "Fatal error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?")
+ toolcontext.error(nclassdef.location, "Error: {mclassdef} does not specialize {the_root_init_mmethod.intro_mclassdef}. Possible duplication of the root class `Object`?")
+ return
end
# Search the longest-one and checks for conflict
# 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
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
end
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`.
# Extract visibility information of the main part of `mtype`
# It is a case-by case
var vis_type: nullable MVisibility = null # The own visibility of the type
- var mmodule_type: nullable MModule = null # The origial module of the type
+ var mmodule_type: nullable MModule = null # The original module of the type
mtype = mtype.as_notnullable
if mtype isa MClassType then
vis_type = mtype.mclass.visibility
end
redef class AClassdef
- var build_properties_is_done: Bool = false
- # The list of super-constructor to call at the start of the free constructor
- # FIXME: this is needed to implement the crazy constructor thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder
- var super_inits: nullable Collection[MMethod] = null
+ # 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
+end
+
redef class MClassDef
# What is the `APropdef` associated to a `MProperty`?
# Used to check multiple definition of a property.
var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
-end
-redef class Prod
- # Join the text of all tokens
- # Used to get the 'real name' of method definitions.
- fun collect_text: String
+ # Build the virtual type `SELF` only for introduction `MClassDef`
+ fun build_self_type(modelbuilder: ModelBuilder, nclassdef: AClassdef)
do
- var v = new TextCollectorVisitor
- v.enter_visit(self)
- assert v.text != ""
- return v.text
- end
-end
+ if not is_intro then return
-private class TextCollectorVisitor
- super Visitor
- var text: String = ""
- redef fun visit(n)
- do
- if n isa Token then text += n.text
- n.visit_all(self)
+ var name = "SELF"
+ var mprop = modelbuilder.try_get_mproperty_by_name(nclassdef, self, name)
+
+ # If SELF type is declared nowherer?
+ if mprop == null then return
+
+ # SELF is not a virtual type? it is weird but we ignore it
+ if not mprop isa MVirtualTypeProp then return
+
+ # Is this the intro of SELF in the library?
+ var intro = mprop.intro
+ var intro_mclassdef = intro.mclassdef
+ if intro_mclassdef == self then
+ var nintro = modelbuilder.mpropdef2npropdef[intro]
+
+ # SELF must be declared in Object, otherwise this will create conflicts
+ if intro_mclassdef.mclass.name != "Object" then
+ modelbuilder.error(nintro, "Error: the virtual type SELF must be declared in Object.")
+ end
+
+ # SELF must be public
+ if mprop.visibility != public_visibility then
+ modelbuilder.error(nintro, "Error: the virtual type SELF must be public.")
+ end
+
+ # SELF must not be fixed
+ if intro.is_fixed then
+ modelbuilder.error(nintro, "Error: the virtual type SELF cannot be fixed.")
+ end
+
+ return
+ end
+
+ # This class introduction inherits a SELF
+ # We insert an artificial property to update it
+ var mpropdef = new MVirtualTypeDef(self, mprop, self.location)
+ mpropdef.bound = mclass.mclass_type
end
end
return false
end
+ if mprop isa MMethod and mprop.is_root_init then return true
if kwredef == null then
if need_redef then
modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.")
return false
end
+
+ # Check for full-name conflicts in the project.
+ # A public property should have a unique qualified name `project::class::prop`.
+ if mprop.intro_mclassdef.mmodule.mgroup != null and mprop.visibility >= protected_visibility then
+ var others = modelbuilder.model.get_mproperties_by_name(mprop.name)
+ if others != null then for other in others do
+ if other != mprop and other.intro_mclassdef.mmodule.mgroup != null and other.intro_mclassdef.mmodule.mgroup.mproject == mprop.intro_mclassdef.mmodule.mgroup.mproject and other.intro_mclassdef.mclass.name == mprop.intro_mclassdef.mclass.name and other.visibility >= protected_visibility then
+ modelbuilder.advice(self, "full-name-conflict", "Warning: A property named `{other.full_name}` is already defined in module `{other.intro_mclassdef.mmodule}` for the class `{other.intro_mclassdef.mclass.name}`.")
+ break
+ end
+ end
+ end
else
if not need_redef then
modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.")
var ntype = self.n_type
if ntype != null then
self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
- if self.ret_type == null then return false # Skip errir
+ if self.ret_type == null then return false # Skip error
end
self.is_visited = true
# Can self be used as a root init?
- private fun look_like_a_root_init(modelbuilder: ModelBuilder): Bool
+ private fun look_like_a_root_init(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
do
# Need the `init` keyword
if n_kwinit == null then return false
# Need to by anonymous
if self.n_methid != null then return false
- # No parameters
- if self.n_signature.n_params.length > 0 then return false
- # Cannot be private or something
- if not self.n_visibility isa APublicVisibility then return false
# No annotation on itself
if get_single_annotation("old_style_init", modelbuilder) != null then return false
# Nor on its module
var old = amoddecl.get_single_annotation("old_style_init", modelbuilder)
if old != null then return false
end
+ # No parameters
+ if self.n_signature.n_params.length > 0 then
+ modelbuilder.advice(self, "old-init", "Warning: init with signature in {mclassdef}")
+ return false
+ end
+ # Cannot be private or something
+ if not self.n_visibility isa APublicVisibility then
+ modelbuilder.advice(self, "old-init", "Warning: non-public init in {mclassdef}")
+ return false
+ end
return true
end
name = "init"
name_node = n_kwinit
else if n_kwnew != null then
- name = "init"
+ name = "new"
name_node = n_kwnew
else
abort
end
end
+ 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 mprop == null and look_like_a_root_init(modelbuilder) then
+ if mprop == null and look_like_a_root_init then
mprop = modelbuilder.the_root_init_mmethod
var nb = n_block
if nb isa ABlockExpr and nb.n_expr.is_empty and n_doc == null then
if mprop == null then
var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
mprop = new MMethod(mclassdef, name, mvisibility)
- if look_like_a_root_init(modelbuilder) and modelbuilder.the_root_init_mmethod == null then
+ 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 parent isa ATopClassdef then mprop.is_toplevel = true
- if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then return
+ self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop)
else
- if not mprop.is_root_init and not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
+ if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
end
+
+ # Check name conflicts in the local class for constructors.
+ if is_init then
+ for p, n in mclassdef.mprop2npropdef do
+ if p != mprop and p isa MMethod and p.name == name then
+ check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, p)
+ break
+ end
+ end
+ end
+
mclassdef.mprop2npropdef[mprop] = self
var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
self.mpropdef = mpropdef
modelbuilder.mpropdef2npropdef[mpropdef] = self
if mpropdef.is_intro then
- modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
+ modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 4)
else
- modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
+ modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 4)
end
end
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
+
# Retrieve info from the signature AST
var param_names = new Array[String] # Names of parameters from the AST
var param_types = new Array[MType] # Types of parameters from the AST
msignature = mpropdef.mproperty.intro.msignature
if msignature == null then return # Skip error
+ # The local signature is adapted to use the local formal types, if any.
+ msignature = msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+
# Check inherited signature arity
if param_names.length != msignature.arity then
var node: ANode
modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}")
return
end
- else if mpropdef.mproperty.is_init then
+ else if mpropdef.mproperty.is_init and not mpropdef.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)
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
+
msignature = new MSignature(mparameters, ret_type)
mpropdef.msignature = msignature
mpropdef.is_abstract = self.get_single_annotation("abstract", modelbuilder) != null
for i in [0..mysignature.arity[ do
var myt = mysignature.mparameters[i].mtype
var prt = msignature.mparameters[i].mtype
- if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
- not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
- modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.")
+ var node = nsig.n_params[i]
+ if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then
+ modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.")
end
end
end
if precursor_ret_type != null then
+ var node: nullable ANode = null
+ if nsig != null then node = nsig.n_type
+ if node == null then node = self
if ret_type == null then
# Inherit the return type
ret_type = precursor_ret_type
- else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
- modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.")
+ else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then
+ modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.")
end
end
end
# Is the node tagged `noinit`?
var noinit = false
- # Is the node taggeg lazy?
+ # Is the node tagged lazy?
var is_lazy = false
- # The guard associated to a lasy attribute.
+ # Has the node a default value?
+ # Could be through `n_expr` or `n_block`
+ var has_value = false
+
+ # The guard associated to a lazy attribute.
# Because some engines does not have a working `isset`,
- # this additionnal attribute is used to guard the lazy initialization.
+ # this additional attribute is used to guard the lazy initialization.
# TODO: to remove once isset is correctly implemented
var mlazypropdef: nullable MAttributeDef
set_doc(mreadpropdef, modelbuilder)
mpropdef.mdoc = mreadpropdef.mdoc
+ has_value = n_expr != null or n_block != null
+
+ var atnoinit = self.get_single_annotation("noinit", modelbuilder)
+ if atnoinit != null then
+ noinit = true
+ if has_value then
+ modelbuilder.error(atnoinit, "Error: `noinit` attributes cannot have an initial value")
+ return
+ end
+ end
+
var atlazy = self.get_single_annotation("lazy", modelbuilder)
- if atlazy != null then
- if n_expr == null then
- modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
+ var atautoinit = self.get_single_annotation("autoinit", modelbuilder)
+ if atlazy != null or atautoinit != null then
+ if atlazy != null and atautoinit != null then
+ modelbuilder.error(atlazy, "Error: lazy incompatible with autoinit")
+ return
+ end
+ if not has_value then
+ if atlazy != null then
+ modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
+ else if atautoinit != null then
+ modelbuilder.error(atautoinit, "Error: a autoinit attribute needs a value")
+ end
+ return
end
is_lazy = true
var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
var atreadonly = self.get_single_annotation("readonly", modelbuilder)
if atreadonly != null then
- if n_expr == null then
+ if not has_value then
modelbuilder.error(atreadonly, "Error: a readonly attribute needs a value")
end
# No setter, so just leave
redef fun build_signature(modelbuilder)
do
var mpropdef = self.mpropdef
- if mpropdef == null then return # Error thus skiped
+ if mpropdef == null then return # Error thus skipped
var mclassdef = mpropdef.mclassdef
var mmodule = mclassdef.mmodule
var mtype: nullable MType = null
if mtype == null then return
end
- # Inherit the type from the getter (usually an abstact getter)
- if mtype == null and mreadpropdef != null and not mreadpropdef.is_intro then
+ var inherited_type: nullable MType = null
+ # Inherit the type from the getter (usually an abstract getter)
+ if mreadpropdef != null and not mreadpropdef.is_intro then
var msignature = mreadpropdef.mproperty.intro.msignature
- if msignature == null then return # Error, thus skiped
- mtype = msignature.return_mtype
+ if msignature == null then return # Error, thus skipped
+ inherited_type = msignature.return_mtype
+ if inherited_type != null then
+ # The inherited type is adapted to use the local formal types, if any.
+ inherited_type = inherited_type.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+ if mtype == null then mtype = inherited_type
+ end
end
var nexpr = self.n_expr
if mtype == null then return
end
- else if ntype != null then
+ else if ntype != null and inherited_type == mtype then
if nexpr isa ANewExpr then
var xmtype = modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type)
if xmtype == mtype then
redef fun check_signature(modelbuilder)
do
var mpropdef = self.mpropdef
- if mpropdef == null then return # Error thus skiped
- var mclassdef = mpropdef.mclassdef
- var mmodule = mclassdef.mmodule
+ if mpropdef == null then return # Error thus skipped
var ntype = self.n_type
var mtype = self.mpropdef.static_mtype
- if mtype == null then return # Error thus skiped
+ if mtype == null then return # Error thus skipped
# Lookup for signature in the precursor
# FIXME all precursors should be considered
for i in [0..mysignature.arity[ do
var myt = mysignature.mparameters[i].mtype
var prt = msignature.mparameters[i].mtype
- if not myt.is_subtype(mmodule, mclassdef.bound_mtype, prt) or
- not prt.is_subtype(mmodule, mclassdef.bound_mtype, myt) then
- var node: ANode
- if nsig != null then node = nsig else node = self
+ var node: ANode
+ if nsig != null then node = nsig else node = self
+ if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then
modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.")
end
end
end
if precursor_ret_type != null then
+ var node: ANode
+ if nsig != null then node = nsig else node = self
if ret_type == null then
# Inherit the return type
ret_type = precursor_ret_type
- else if not ret_type.is_subtype(mmodule, mclassdef.bound_mtype, precursor_ret_type) then
- var node: ANode
- if nsig != null then node = nsig else node = self
+ else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then
modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.")
end
end
var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
self.mpropdef = mpropdef
modelbuilder.mpropdef2npropdef[mpropdef] = self
+ if mpropdef.is_intro then
+ modelbuilder.toolcontext.info("{mpropdef} introduces new type {mprop.full_name}", 4)
+ else
+ modelbuilder.toolcontext.info("{mpropdef} redefines type {mprop.full_name}", 4)
+ end
set_doc(mpropdef, modelbuilder)
var atfixed = get_single_annotation("fixed", modelbuilder)
redef fun build_signature(modelbuilder)
do
var mpropdef = self.mpropdef
- if mpropdef == null then return # Error thus skiped
+ if mpropdef == null then return # Error thus skipped
var mclassdef = mpropdef.mclassdef
var mmodule = mclassdef.mmodule
var mtype: nullable MType = null
redef fun check_signature(modelbuilder)
do
var mpropdef = self.mpropdef
- if mpropdef == null then return # Error thus skiped
+ if mpropdef == null then return # Error thus skipped
var bound = self.mpropdef.bound
- if bound == null then return # Error thus skiped
+ if bound == null then return # Error thus skipped
modelbuilder.check_visibility(n_type, bound, mpropdef)
# Check redefinitions
bound = mpropdef.bound.as(not null)
for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
- var supbound = p.bound.as(not null)
+ var supbound = p.bound
+ if supbound == null then break # broken super bound, skip error
if p.is_fixed then
modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
break
modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.")
break
end
- if not bound.is_subtype(mmodule, anchor, supbound) then
+ if not modelbuilder.check_subtype(n_type, mmodule, anchor, bound, supbound) then
modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")
break
end