+ 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