src: Update init
[nit.git] / src / modelize / modelize_property.nit
index fe755a5..8529ed6 100644 (file)
@@ -42,6 +42,17 @@ redef class ModelBuilder
        # Public clients need to use `mpropdef2node` to access stuff.
        private var mpropdef2npropdef = new HashMap[MPropDef, APropdef]
 
+       # Associate a `npropdef` with its `mpropdef`
+       #
+       # Be careful, this method is unsafe, no checking is done when it's used.
+       # The safe way to add method it's to use the `build_property`
+       #
+       # See `mpropdef2npropdef`
+       fun unsafe_add_mpropdef2npropdef(mpropdef: MPropDef,npropdef: APropdef)
+       do
+               mpropdef2npropdef[mpropdef] = npropdef
+       end
+
        # Retrieve the associated AST node of a mpropertydef.
        # This method is used to associate model entity with syntactic entities.
        #
@@ -79,7 +90,6 @@ redef class ModelBuilder
        end
 
        # Build the properties of `nclassdef`.
-       # REQUIRE: all superclasses are built.
        private fun build_properties(nclassdef: AClassdef)
        do
                # Force building recursively
@@ -110,7 +120,7 @@ redef class ModelBuilder
                                if not check_virtual_types_circularity(npropdef, mpropdef.mproperty, mclassdef.bound_mtype, mclassdef.mmodule) then
                                        # Invalidate the bound
                                        mpropdef.is_broken = true
-                                       mpropdef.bound = new MBottomType(mclassdef.mmodule.model)
+                                       mpropdef.bound = new MErrorType(mclassdef.mmodule.model)
                                end
                        end
                        for npropdef in nclassdef2.n_propdefs do
@@ -144,13 +154,12 @@ redef class ModelBuilder
                # 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", mclassdef.mclass.visibility)
+                       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
-                       mpropdef.new_msignature = msignature
                        mprop.is_init = true
                        self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3)
                        the_root_init_mmethod = mprop
@@ -164,15 +173,13 @@ redef class ModelBuilder
                        if mpropdef.mproperty.is_root_init then
                                assert defined_init == null
                                defined_init = mpropdef
-                       else if mpropdef.mproperty.name == "autoinit" then
-                               # An explicit old-style init named "init", so return
+                       end
+                       if mpropdef.name == "defaultinit" then
                                return
                        end
                end
 
-               if mclassdef.auto_init != null then
-                       return
-               end
+               if mclassdef.default_init != null then return
 
                if not nclassdef isa AStdClassdef then return
 
@@ -186,7 +193,6 @@ redef class ModelBuilder
                                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
@@ -205,19 +211,21 @@ redef class ModelBuilder
                                        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
@@ -230,7 +238,7 @@ redef class ModelBuilder
                # Look for most-specific new-stype init definitions
                var spropdefs = new ArraySet[MMethodDef]
                for x in mclassdef.in_hierarchy.direct_greaters do
-                       var y = x.mclass.intro.auto_init
+                       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
@@ -287,7 +295,7 @@ redef class ModelBuilder
                                        abort
                                end
                        end
-               else
+               else if spropdefs.not_empty then
                        # Search the longest-one and checks for conflict
                        var longest = spropdefs.first
                        if spropdefs.length > 1 then
@@ -326,16 +334,9 @@ redef class ModelBuilder
                                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
@@ -343,15 +344,14 @@ redef class ModelBuilder
 
                # Create a specific new autoinit constructor
                do
-                       var mprop = new MMethod(mclassdef, "autoinit", public_visibility)
+                       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.auto_init = mpropdef
+                       mclassdef.default_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
        end
@@ -379,6 +379,8 @@ redef class ModelBuilder
                        # nothing to do.
                else if mtype isa MBottomType then
                        # nothing to do.
+               else if mtype isa MErrorType then
+                       # nothing to do.
                else
                        node.debug "Unexpected type {mtype}"
                        abort
@@ -488,12 +490,6 @@ end
 redef class MClass
        # The base init of the class.
        #
-       # 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
@@ -588,7 +584,7 @@ redef class APropdef
                        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
 
@@ -669,14 +665,13 @@ redef class ASignature
        # Visit and fill information about a signature
        private fun visit_signature(modelbuilder: ModelBuilder, mclassdef: MClassDef): Bool
        do
-               var mmodule = mclassdef.mmodule
                var param_names = self.param_names
                var param_types = self.param_types
                for np in self.n_params do
                        param_names.add(np.n_id.text)
                        var ntype = np.n_type
                        if ntype != null then
-                               var mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true)
+                               var mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true)
                                if mtype == null then return false # Skip error
                                for i in [0..param_names.length-param_types.length[ do
                                        param_types.add(mtype)
@@ -693,7 +688,7 @@ redef class ASignature
                end
                var ntype = self.n_type
                if ntype != null then
-                       self.ret_type = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true)
+                       self.ret_type = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true)
                        if self.ret_type == null then return false # Skip error
                end
 
@@ -707,14 +702,14 @@ redef class ASignature
                for np in self.n_params do
                        var ntype = np.n_type
                        if ntype != null then
-                               if modelbuilder.resolve_mtype(mclassdef.mmodule, mclassdef, ntype) == null then
+                               if modelbuilder.resolve_mtype(mclassdef, ntype) == null then
                                        res = false
                                end
                        end
                end
                var ntype = self.n_type
                if ntype != null then
-                       if modelbuilder.resolve_mtype(mclassdef.mmodule, mclassdef, ntype) == null then
+                       if modelbuilder.resolve_mtype(mclassdef, ntype) == null then
                                res = false
                        end
                end
@@ -768,25 +763,27 @@ redef class AMethPropdef
        do
                var n_kwinit = n_kwinit
                var n_kwnew = n_kwnew
-               var is_init = n_kwinit != null or n_kwnew != null
+               var is_new = n_kwnew != null
+               var is_init = n_kwinit != null or is_new
                var name: String
                var amethodid = self.n_methid
                var name_node: ANode
+               var is_old_style_init = false
                if amethodid == null then
-                       if not is_init then
-                               name = "main"
-                               name_node = self
-                       else if n_kwinit != null then
+                       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"
+                               var old_style_annot = get_single_annotation("old_style_init", modelbuilder)
+                               if  old_style_annot != null or self.n_signature.n_params.not_empty then
+                                       name = "defaultinit"
+                                       if old_style_annot != null then is_old_style_init = true
                                end
                        else if n_kwnew != null then
                                name = "new"
                                name_node = n_kwnew
                        else
-                               abort
+                               name = "main"
+                               name_node = self
                        end
                else if amethodid isa AIdMethid then
                        name = amethodid.n_id.text
@@ -814,7 +811,7 @@ redef class AMethPropdef
 
                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
@@ -824,23 +821,21 @@ redef class AMethPropdef
                end
                if mprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
-                       mprop = new MMethod(mclassdef, name, mvisibility)
+                       mprop = new MMethod(mclassdef, name, self.location, mvisibility)
                        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 mprop.is_new then mclassdef.mclass.has_new_factory = true
+                       mprop.is_new = is_new
+                       if is_new then mclassdef.mclass.has_new_factory = true
                        if name == "sys" then mprop.is_toplevel = true # special case for sys allowed in `new` factories
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then
                                mprop.is_broken = true
                                return
                        end
                else
-                       if mprop.is_broken then
-                               return
-                       end
+                       if mprop.is_broken 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
@@ -861,6 +856,15 @@ redef class AMethPropdef
                mclassdef.mprop2npropdef[mprop] = self
 
                var mpropdef = new MMethodDef(mclassdef, mprop, self.location)
+               if mpropdef.name == "defaultinit" and mclassdef.is_intro then
+                       assert mclassdef.default_init == null
+                       mpropdef.is_old_style_init = is_old_style_init
+                       mclassdef.default_init = mpropdef
+                       if mpropdef.is_intro then
+                               mpropdef.initializers.add mprop
+                               mpropdef.is_calling_init = true
+                       end
+               end
 
                set_doc(mpropdef, modelbuilder)
 
@@ -877,20 +881,11 @@ redef class AMethPropdef
        do
                var mpropdef = self.mpropdef
                if mpropdef == null then return # Error thus skiped
+               var mproperty = mpropdef.mproperty
                var mclassdef = mpropdef.mclassdef
                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
-
                var accept_special_last_parameter = self.n_methid == null or self.n_methid.accept_special_last_parameter
                var return_is_mandatory = self.n_methid != null and self.n_methid.return_is_mandatory
 
@@ -911,7 +906,7 @@ redef class AMethPropdef
                # FIXME: do not inherit from the intro, but from the most specific
                var msignature: nullable MSignature = null
                if not mpropdef.is_intro then
-                       msignature = mpropdef.mproperty.intro.msignature
+                       msignature = mproperty.intro.msignature
                        if msignature == null then return # Skip error
 
                        # The local signature is adapted to use the local formal types, if any.
@@ -921,14 +916,14 @@ redef class AMethPropdef
                        if param_names.length != msignature.arity then
                                var node: ANode
                                if nsig != null then node = nsig else node = self
-                               modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mpropdef.mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mpropdef.mproperty.full_name}`.")
+                               modelbuilder.error(node, "Redef Error: expected {msignature.arity} parameter(s) for `{mproperty.name}{msignature}`; got {param_names.length}. See introduction at `{mproperty.full_name}`.")
                                return
                        end
-               else if mpropdef.mproperty.is_init and not mpropdef.mproperty.is_new then
+               else if mproperty.is_init and not 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)
-                               var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
+                               var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mproperty.name)
                                if candidate != null then
                                        if msignature == null then
                                                msignature = candidate.intro.as(MMethodDef).msignature
@@ -965,14 +960,14 @@ redef class AMethPropdef
                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
+               if ret_type == null and mproperty.is_new then ret_type = mclassdef.mclass.mclass_type
 
                # Special checks for operator methods
                if not accept_special_last_parameter and mparameters.not_empty and mparameters.last.is_vararg then
-                       modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mpropdef.mproperty.name}`.")
+                       modelbuilder.error(self.n_signature.n_params.last, "Error: illegal variadic parameter `{mparameters.last}` for `{mproperty.name}`.")
                end
                if ret_type == null and return_is_mandatory then
-                       modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mpropdef.mproperty.name}`.")
+                       modelbuilder.error(self.n_methid, "Error: mandatory return type for `{mproperty.name}`.")
                end
 
                msignature = new MSignature(mparameters, ret_type)
@@ -1158,8 +1153,11 @@ redef class AAttrPropdef
        # Is the node tagged lazy?
        var is_lazy = false
 
-       # Has the node a default value?
-       # Could be through `n_expr` or `n_block`
+       # Is the node tagged optional?
+       var is_optional = false
+
+       # Does the node have a default value?
+       # Could be through `n_expr`, `n_block` or `is_lazy`
        var has_value = false
 
        # The guard associated to a lazy attribute.
@@ -1185,7 +1183,7 @@ redef class AAttrPropdef
                                modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.")
                        end
 
-                       var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
+                       var mprop = new MAttribute(mclassdef, "_" + name, self.location, private_visibility)
                        var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
                        self.mpropdef = mpropdef
                        modelbuilder.mpropdef2npropdef[mpropdef] = self
@@ -1195,14 +1193,24 @@ redef class AAttrPropdef
                var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
                if mreadprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
-                       mreadprop = new MMethod(mclassdef, readname, mvisibility)
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
+                       mreadprop = new MMethod(mclassdef, readname, self.location, mvisibility)
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then
+                               mreadprop.is_broken = true
+                               return
+                       end
                else
+                       if mreadprop.is_broken then return
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
                end
                mclassdef.mprop2npropdef[mreadprop] = self
 
+               var attr_mpropdef = mpropdef
+               if attr_mpropdef != null then
+                       mreadprop.getter_for = attr_mpropdef.mproperty
+                       attr_mpropdef.mproperty.getter = mreadprop
+               end
+
                var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
                self.mreadpropdef = mreadpropdef
                modelbuilder.mpropdef2npropdef[mreadpropdef] = self
@@ -1247,13 +1255,21 @@ redef class AAttrPropdef
                                return
                        end
                        is_lazy = true
-                       var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
+                       var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, self.location, none_visibility)
                        mlazyprop.is_fictive = true
                        var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
                        mlazypropdef.is_fictive = true
                        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
@@ -1286,10 +1302,14 @@ redef class AAttrPropdef
                                # By default, use protected visibility at most
                                if mvisibility > protected_visibility then mvisibility = protected_visibility
                        end
-                       mwriteprop = new MMethod(mclassdef, writename, mvisibility)
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
+                       mwriteprop = new MMethod(mclassdef, writename, self.location, mvisibility)
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then
+                               mwriteprop.is_broken = true
+                               return
+                       end
                        mwriteprop.deprecation = mreadprop.deprecation
                else
+                       if mwriteprop.is_broken then return
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
                        if atwritable != null then
                                check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
@@ -1297,6 +1317,11 @@ redef class AAttrPropdef
                end
                mclassdef.mprop2npropdef[mwriteprop] = self
 
+               if attr_mpropdef != null then
+                       mwriteprop.setter_for = attr_mpropdef.mproperty
+                       attr_mpropdef.mproperty.setter = mwriteprop
+               end
+
                var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
                self.mwritepropdef = mwritepropdef
                modelbuilder.mpropdef2npropdef[mwritepropdef] = self
@@ -1332,7 +1357,7 @@ redef class AAttrPropdef
 
                var ntype = self.n_type
                if ntype != null then
-                       mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true)
+                       mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true)
                        if mtype == null then return
                end
 
@@ -1352,55 +1377,12 @@ redef class AAttrPropdef
                var nexpr = self.n_expr
                if mtype == null 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
-                                               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 = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char")
-                                       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 = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String")
-                                       if cla != null then mtype = cla.mclass_type
-                               else
-                                       modelbuilder.error(self, "Error: untyped attribute `{mreadpropdef}`. Implicit typing allowed only for literals and new.")
-                               end
-
+                               mtype = infer_static_type(modelbuilder, nexpr, mclassdef, mmodule, mreadpropdef)
                                if mtype == null then return
                        end
                else if ntype != null and inherited_type == mtype then
                        if nexpr isa ANewExpr then
-                               var xmtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true)
+                               var xmtype = modelbuilder.resolve_mtype_unchecked(mclassdef, nexpr.n_type, true)
                                if xmtype == mtype then
                                        modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition")
                                end
@@ -1425,20 +1407,120 @@ redef class AAttrPropdef
 
                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
 
                var mlazypropdef = self.mlazypropdef
                if mlazypropdef != null then
-                       mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
+                       mlazypropdef.static_mtype = mmodule.bool_type
                end
                check_repeated_types(modelbuilder)
        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
+       end
+
        redef fun check_signature(modelbuilder)
        do
                var mpropdef = self.mpropdef
@@ -1452,11 +1534,11 @@ redef class AAttrPropdef
 
                # Check types
                if ntype != null then
-                       if modelbuilder.resolve_mtype(mmodule, mclassdef, ntype) == null then return
+                       if modelbuilder.resolve_mtype(mclassdef, ntype) == null then return
                end
                var nexpr = n_expr
                if nexpr isa ANewExpr then
-                       if modelbuilder.resolve_mtype(mmodule, mclassdef, nexpr.n_type) == null then return
+                       if modelbuilder.resolve_mtype(mclassdef, nexpr.n_type) == null then return
                end
 
                # Lookup for signature in the precursor
@@ -1573,12 +1655,13 @@ redef class ATypePropdef
                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)
+                       mprop = new MVirtualTypeProp(mclassdef, name, self.location, mvisibility)
                        for c in name.chars do if c >= 'a' and c<= 'z' then
                                modelbuilder.warning(n_qid, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.")
                                break
                        end
                else
+                       if mprop.is_broken then return
                        assert mprop isa MVirtualTypeProp
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
                end
@@ -1608,11 +1691,10 @@ redef class ATypePropdef
                var mpropdef = self.mpropdef
                if mpropdef == null then return # Error thus skipped
                var mclassdef = mpropdef.mclassdef
-               var mmodule = mclassdef.mmodule
                var mtype: nullable MType = null
 
                var ntype = self.n_type
-               mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, ntype, true)
+               mtype = modelbuilder.resolve_mtype_unchecked(mclassdef, ntype, true)
                if mtype == null then return
 
                mpropdef.bound = mtype
@@ -1634,7 +1716,7 @@ redef class ATypePropdef
                var anchor = mclassdef.bound_mtype
 
                var ntype = self.n_type
-               if modelbuilder.resolve_mtype(mmodule, mclassdef, ntype) == null then
+               if modelbuilder.resolve_mtype(mclassdef, ntype) == null then
                        mpropdef.bound = null
                        return
                end
@@ -1648,8 +1730,7 @@ redef class ATypePropdef
                                break
                        end
                        if p.mclassdef.mclass == mclassdef.mclass then
-                               # Still a warning to pass existing bad code
-                               modelbuilder.warning(n_type, "refine-type", "Redef Error: a virtual type cannot be refined.")
+                               modelbuilder.error(n_type, "Redef Error: a virtual type cannot be refined.")
                                break
                        end
                        if not modelbuilder.check_subtype(n_type, mmodule, anchor, bound, supbound) then