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
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?
if mpropdef.mproperty.is_root_init then
assert defined_init == null
defined_init = mpropdef
- else if mpropdef.mproperty.name == "init" then
+ else if mpropdef.mproperty.name == "autoinit" then
# An explicit old-style init named "init", so return
return
end
end
+ if mclassdef.auto_init != null then
+ return
+ end
+
if not nclassdef isa AStdClassdef then return
# Collect undefined attributes
var sig = mpropdef.msignature
if sig == null then continue # Skip broken method
- for param in sig.mparameters do
- var ret_type = param.mtype
- var mparameter = new MParameter(param.name, ret_type, false)
- mparameters.add(mparameter)
- end
+ mparameters.add_all sig.mparameters
initializers.add(mpropdef.mproperty)
mpropdef.mproperty.is_autoinit = true
end
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
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.auto_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
abort
end
end
- else
+ else if spropdefs.not_empty then
+ # Search for inherited manual autoinit
+ 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
+ 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)
+ 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}.")
end
# part 2. compare
# Check for conflict in the order of initializers
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, "autoinit", 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.auto_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
-
- # 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`.
var vt = t.mproperty
# Because `vt` is possibly unchecked, we have to do the bound-lookup manually
var defs = vt.lookup_definitions(mmodule, recv)
- # TODO something to manage correctly bound conflicts
- assert not defs.is_empty
+ if defs.is_empty then return false
nexts = new Array[MType]
for d in defs do
var next = defs.first.bound
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
+
+ # 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
redef class MClassDef
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
else 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"
+ end
else if n_kwnew != null then
name = "new"
name_node = n_kwnew
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
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
+ end
set_doc(mpropdef, modelbuilder)
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
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
# Is the node tagged lazy?
var is_lazy = false
+ # Is the node tagged optional?
+ var is_optional = false
+
# Has the node a default value?
# Could be through `n_expr` or `n_block`
var has_value = false
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
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
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")
+ 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")
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")
+ var cla: nullable MClass
+ if nexpr.is_bytestring then
+ cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bytes")
+ else if nexpr.is_re then
+ cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Regex")
+ else if nexpr.is_string then
+ cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
+ else
+ abort
+ end
if cla != null then mtype = cla.mclass_type
else
modelbuilder.error(self, "Error: untyped attribute `{mreadpropdef}`. Implicit typing allowed only for literals and new.")
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
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)
for c in name.chars do if c >= 'a' and c<= 'z' then
- modelbuilder.warning(n_id, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.")
+ modelbuilder.warning(n_qid, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.")
break
end
- if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return
else
- if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return
assert mprop isa MVirtualTypeProp
check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
end
- mclassdef.mprop2npropdef[mprop] = self
var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location)
self.mpropdef = mpropdef
- modelbuilder.mpropdef2npropdef[mpropdef] = self
if mpropdef.is_intro then
modelbuilder.toolcontext.info("{mpropdef} introduces new type {mprop.full_name}", 4)
else
modelbuilder.toolcontext.info("{mpropdef} redefines type {mprop.full_name}", 4)
end
+ if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, not mpropdef.is_intro, mprop) then
+ mpropdef.is_broken =true
+ end
+ mclassdef.mprop2npropdef[mprop] = self
+ modelbuilder.mpropdef2npropdef[mpropdef] = self
set_doc(mpropdef, modelbuilder)
var atfixed = get_single_annotation("fixed", modelbuilder)