This is the last part of build_properties
.
# Introduce or inherit default constructor
# This is the last part of `build_properties`.
private fun process_default_constructors(nclassdef: AClassdef)
do
var mclassdef = nclassdef.mclassdef.as(not null)
# Are we a refinement
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", 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
mprop.is_init = true
self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
the_root_init_mmethod = mprop
end
# Is there already a constructor defined?
var defined_init: nullable MMethodDef = null
for mpropdef in mclassdef.mpropdefs do
if not mpropdef isa MMethodDef then continue
if not mpropdef.mproperty.is_init then continue
if mpropdef.mproperty.is_root_init then
assert defined_init == null
defined_init = mpropdef
else if mpropdef.name == "defaultinit" then
return
end
end
if mclassdef.default_init != null 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]
var initializers = new Array[MProperty]
for npropdef in nclassdef.n_propdefs do
if npropdef isa AMethPropdef then
if not npropdef.is_autoinit then continue # Skip non tagged autoinit
var mpropdef = npropdef.mpropdef
if mpropdef == null then return # Skip broken method
var sig = mpropdef.msignature
if sig == null then continue # Skip broken method
mparameters.add_all sig.mparameters
initializers.add(mpropdef.mproperty)
mpropdef.mproperty.is_autoinit = true
end
if npropdef isa AAttrPropdef then
var mreadpropdef = npropdef.mreadpropdef
if mreadpropdef == null then return # Skip broken attribute
var msignature = mreadpropdef.msignature
if msignature == null then return # Skip broken attribute
if npropdef.noinit then continue # Skip noinit attribute
var atlateinit = npropdef.get_single_annotation("lateinit", self)
if atlateinit != null then
# For lateinit attributes, call the reader to force
# the lazy initialization of the attribute.
initializers.add(mreadpropdef.mproperty)
mreadpropdef.mproperty.is_autoinit = true
continue
end
if npropdef.has_value and not npropdef.is_optional then continue
var msetter = npropdef.mwritepropdef
if msetter == null then
# No setter, it is a readonly attribute, so just add it
var paramname = mreadpropdef.mproperty.name
var ret_type = msignature.return_mtype
if ret_type == null then return
var mparameter = new MParameter(paramname, ret_type, false)
mparameters.add(mparameter)
initializers.add(npropdef.mpropdef.mproperty)
npropdef.mpropdef.mproperty.is_autoinit = true
else
# Add the setter to the list
mparameters.add_all msetter.msignature.mparameters
initializers.add(msetter.mproperty)
msetter.mproperty.is_autoinit = true
end
end
end
var the_root_init_mmethod = self.the_root_init_mmethod
if the_root_init_mmethod == null then return
# Look for most-specific new-stype init definitions
var spropdefs = new ArraySet[MMethodDef]
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
end
# Look at the autoinit class-annotation
var autoinit = nclassdef.get_single_annotation("autoinit", self)
var noautoinit = nclassdef.get_single_annotation("noautoinit", self)
if autoinit != null then
# Just throws the collected initializers
mparameters.clear
initializers.clear
if noautoinit != null then
error(autoinit, "Error: `autoinit` and `noautoinit` are incompatible.")
end
if autoinit.n_args.is_empty then
error(autoinit, "Syntax Error: `autoinit` expects method identifiers, use `noautoinit` to clear all autoinits.")
end
# Get and check each argument
for narg in autoinit.n_args do
var id = narg.as_id
if id == null then
error(narg, "Syntax Error: `autoinit` expects method identifiers.")
return
end
# Search the property.
# To avoid bad surprises, try to get the setter first.
var p = try_get_mproperty_by_name(narg, mclassdef, id + "=")
if p == null then
p = try_get_mproperty_by_name(narg, mclassdef, id)
end
if p == null then
error(narg, "Error: unknown method `{id}`")
return
end
if not p.is_autoinit then
error(narg, "Error: `{p}` is not an autoinit method")
return
end
# Register the initializer and the parameters
initializers.add(p)
var pd = p.intro
if pd isa MMethodDef then
# Get the signature resolved for the current receiver
var sig = pd.msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mclassdef.mmodule, false)
mparameters.add_all(sig.mparameters)
else
# TODO attributes?
abort
end
end
else if spropdefs.not_empty then
# 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 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 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 defaultinit {manual} and automatic defaultinit {longest}.")
end
# part 2. compare
# Check for conflict in the order of initializers
# Each initializer list must me a prefix of the longest list
# If `noautoinit` is set, just ignore conflicts
if noautoinit == null then for spd in spropdefs do
var i = 0
for p in spd.initializers do
if p != longest.initializers[i] then
var proposal = new ArraySet[MProperty]
for spd2 in spropdefs do
proposal.add_all spd2.initializers
end
proposal.add_all initializers
self.error(nclassdef, "Error: cannot generate automatic init for class {mclassdef.mclass}. Conflict in the order in inherited initializers {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")}). Use `autoinit` to order initializers. eg `autoinit {proposal.join(", ")}`")
# TODO: invalidate the initializer to avoid more errors
return
end
i += 1
end
end
end
if noautoinit != null then
# If there is local or inherited initializers, then complain.
if initializers.is_empty and longest.initializers.is_empty then
warning(noautoinit, "useless-noautoinit", "Warning: the list of autoinit is already empty.")
end
# Just clear initializers
mparameters.clear
initializers.clear
else
# Combine the inherited list to what is collected
if longest.initializers.length > 0 then
mparameters.prepend longest.msignature.mparameters
initializers.prepend longest.initializers
end
end
end
# 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)
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
end
src/modelize/modelize_property.nit:156,2--389,4