Introduce or inherit default constructor

This is the last part of build_properties.

Property definitions

nitc :: modelize_property $ ModelBuilder :: process_default_constructors
	# 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