model: Make the `anchor` parameter of `MType::anchor_to` nullable
[nit.git] / src / model / model.nit
index b148eef..03d82b1 100644 (file)
@@ -75,7 +75,7 @@ redef class Model
        # Collections of classes grouped by their short name
        private var mclasses_by_name = new MultiHashMap[String, MClass]
 
-       # Return all class named `name`.
+       # Return all classes named `name`.
        #
        # If such a class does not exist, null is returned
        # (instead of an empty array)
@@ -103,6 +103,9 @@ redef class Model
        # The only null type
        var null_type = new MNullType(self)
 
+       # The only bottom type
+       var bottom_type: MBottomType = null_type.as_notnull
+
        # Build an ordered tree with from `concerns`
        fun concerns_tree(mconcerns: Collection[MConcern]): ConcernsTree do
                var seen = new HashSet[MConcern]
@@ -159,7 +162,7 @@ redef class MModule
                return self.in_importation <= mclass.intro_mmodule
        end
 
-       # Full hierarchy of introduced ans imported classes.
+       # Full hierarchy of introduced and imported classes.
        #
        # Create a new hierarchy got by flattening the classes for the module
        # and its imported modules.
@@ -252,8 +255,8 @@ redef class MModule
        # The primitive type `String`
        var string_type: MClassType = self.get_primitive_class("String").mclass_type is lazy
 
-       # The primitive type `NativeString`
-       var native_string_type: MClassType = self.get_primitive_class("NativeString").mclass_type is lazy
+       # The primitive type `CString`
+       var c_string_type: MClassType = self.get_primitive_class("CString").mclass_type is lazy
 
        # A primitive type of `Array`
        fun array_type(elt_type: MType): MClassType do return array_class.get_mtype([elt_type])
@@ -301,14 +304,14 @@ redef class MModule
                                cladef.add_in_hierarchy
                                return c
                        end
-                       print("Fatal Error: no primitive class {name} in {self}")
+                       print_error("Fatal Error: no primitive class {name} in {self}")
                        exit(1)
                        abort
                end
                if cla.length != 1 then
                        var msg = "Fatal Error: more than one primitive class {name} in {self}:"
                        for c in cla do msg += " {c.full_name}"
-                       print msg
+                       print_error msg
                        #exit(1)
                end
                return cla.first
@@ -327,7 +330,7 @@ redef class MModule
                        if res == null then
                                res = mprop
                        else if res != mprop then
-                               print("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}")
+                               print_error("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}")
                                abort
                        end
                end
@@ -519,12 +522,12 @@ class MClass
 
        # The principal static type of the class.
        #
-       # For non-generic class, mclass_type is the only `MClassType` based
+       # For non-generic class, `mclass_type` is the only `MClassType` based
        # on self.
        #
        # For a generic class, the arguments are the formal parameters.
-       # i.e.: for the class Array[E:Object], the `mclass_type` is Array[E].
-       # If you want Array[Object] the see `MClassDef::bound_mtype`
+       # i.e.: for the class `Array[E:Object]`, the `mclass_type` is `Array[E]`.
+       # If you want `Array[Object]`, see `MClassDef::bound_mtype`.
        #
        # For generic classes, the mclass_type is also the way to get a formal
        # generic parameter type.
@@ -603,7 +606,7 @@ class MClassDef
        # ENSURE: `bound_mtype.mclass == self.mclass`
        var bound_mtype: MClassType
 
-       redef var location: Location
+       redef var location
 
        redef fun visibility do return mclass.visibility
 
@@ -720,8 +723,11 @@ class MClassDef
        # All properties introduced by the classdef
        var intro_mproperties = new Array[MProperty]
 
-       # All property definitions in the class (introductions and redefinitions)
+       # All property introductions and redefinitions in `self` (not inheritance).
        var mpropdefs = new Array[MPropDef]
+
+       # All property introductions and redefinitions (not inheritance) in `self` by its associated property.
+       var mpropdefs_by_property = new HashMap[MProperty, MPropDef]
 end
 
 # A global static type
@@ -832,19 +838,19 @@ abstract class MType
                end
                #print "4.is {sub} a {sup}? <- no more resolution"
 
-               if sub isa MBottomType then
+               if sub isa MBottomType or sub isa MErrorType then
                        return true
                end
 
-               assert sub isa MClassType else print "{sub} <? {sub}" # It is the only remaining type
+               assert sub isa MClassType else print_error "{sub} <? {sup}" # It is the only remaining type
 
                # Handle sup-type when the sub-type is class-based (other cases must have be identified before).
-               if sup isa MFormalType or sup isa MNullType or sup isa MBottomType then
+               if sup isa MFormalType or sup isa MNullType or sup isa MBottomType or sup isa MErrorType then
                        # These types are not super-types of Class-based types.
                        return false
                end
 
-               assert sup isa MClassType else print "got {sup} {sub.inspect}" # It is the only remaining type
+               assert sup isa MClassType else print_error "got {sup} {sub.inspect}" # It is the only remaining type
 
                # Now both are MClassType, we need to dig
 
@@ -895,15 +901,16 @@ abstract class MType
        #
        # Explanation of the example:
        # In H, T is set to B, because "H super G[B]", and U is bound to Y,
-        # because "redef type U: Y". Therefore, Map[T, U] is bound to
+       # because "redef type U: Y". Therefore, Map[T, U] is bound to
        # Map[B, Y]
        #
+       # REQUIRE: `self.need_anchor implies anchor != null`
        # ENSURE: `not self.need_anchor implies result == self`
        # ENSURE: `not result.need_anchor`
-       fun anchor_to(mmodule: MModule, anchor: MClassType): MType
+       fun anchor_to(mmodule: MModule, anchor: nullable MClassType): MType
        do
                if not need_anchor then return self
-               assert not anchor.need_anchor
+               assert anchor != null and not anchor.need_anchor
                # Just resolve to the anchor and clear all the virtual types
                var res = self.resolve_for(anchor, null, mmodule, true)
                assert not res.need_anchor
@@ -1033,7 +1040,7 @@ abstract class MType
        # The result is returned exactly as declared in the "type" property (verbatim).
        # So it could be another formal type.
        #
-       # In case of conflicts or inconsistencies in the model, the method returns a `MBottomType`.
+       # In case of conflicts or inconsistencies in the model, the method returns a `MErrorType`.
        fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType do return self
 
        # Resolve the formal type to its simplest equivalent form.
@@ -1048,9 +1055,24 @@ abstract class MType
        # By default, return self.
        # See the redefinitions for specific behavior in each kind of type.
        #
-       # In case of conflicts or inconsistencies in the model, the method returns a `MBottomType`.
+       # In case of conflicts or inconsistencies in the model, the method returns a `MErrorType`.
        fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do return self
 
+       # Is the type a `MErrorType` or contains an `MErrorType`?
+       #
+       # `MErrorType` are used in result with conflict or inconsistencies.
+       #
+       # See `is_legal_in` to check conformity with generic bounds.
+       fun is_ok: Bool do return true
+
+       # Is the type legal in a given `mmodule` (with an optional `anchor`)?
+       #
+       # A type is valid if:
+       #
+       # * it does not contain a `MErrorType` (see `is_ok`).
+       # * its generic formal arguments are within their bounds.
+       fun is_legal_in(mmodule: MModule, anchor: nullable MClassType): Bool do return is_ok
+
        # Can the type be resolved?
        #
        # In order to resolve open types, the formal types must make sence.
@@ -1118,6 +1140,7 @@ abstract class MType
        # * H[G[A], B] -> 3
        #
        # Formal types have a depth of 1.
+       # Only `MClassType` and `MFormalType` nodes are counted.
        fun depth: Int
        do
                return 1
@@ -1131,6 +1154,7 @@ abstract class MType
        # * H[G[A], B] -> 4
        #
        # Formal types have a length of 1.
+       # Only `MClassType` and `MFormalType` nodes are counted.
        fun length: Int
        do
                return 1
@@ -1197,7 +1221,7 @@ class MClassType
 
        redef fun need_anchor do return false
 
-       redef fun anchor_to(mmodule: MModule, anchor: MClassType): MClassType
+       redef fun anchor_to(mmodule, anchor): MClassType
        do
                return super.as(MClassType)
        end
@@ -1349,6 +1373,24 @@ class MGenericType
                return true
        end
 
+       redef fun is_ok
+       do
+               for t in arguments do if not t.is_ok then return false
+               return super
+       end
+
+       redef fun is_legal_in(mmodule, anchor)
+       do
+               var mtype
+               if need_anchor then
+                       assert anchor != null
+                       mtype = anchor_to(mmodule, anchor)
+               else
+                       mtype = self
+               end
+               if not mtype.is_ok then return false
+               return mtype.is_subtype(mmodule, null, mtype.mclass.intro.bound_mtype)
+       end
 
        redef fun depth
        do
@@ -1392,9 +1434,11 @@ class MVirtualType
 
        redef fun model do return self.mproperty.intro_mclassdef.mmodule.model
 
-       redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+       redef fun lookup_bound(mmodule, resolved_receiver)
        do
-               return lookup_single_definition(mmodule, resolved_receiver).bound or else new MBottomType(model)
+               # There is two possible invalid cases: the vt does not exists in resolved_receiver or the bound is broken
+               if not resolved_receiver.has_mproperty(mmodule, mproperty) then return new MErrorType(model)
+               return lookup_single_definition(mmodule, resolved_receiver).bound or else new MErrorType(model)
        end
 
        private fun lookup_single_definition(mmodule: MModule, resolved_receiver: MType): MVirtualTypeDef
@@ -1430,7 +1474,7 @@ class MVirtualType
 
                var prop = lookup_single_definition(mmodule, resolved_receiver)
                var res = prop.bound
-               if res == null then return new MBottomType(model)
+               if res == null then return new MErrorType(model)
 
                # Recursively lookup the fixed result
                res = res.lookup_fixed(mmodule, resolved_receiver)
@@ -1451,6 +1495,9 @@ class MVirtualType
        do
                if not cleanup_virtual then return self
                assert can_resolve_for(mtype, anchor, mmodule)
+
+               if mproperty.is_selftype then return mtype
+
                # self is a virtual type declared (or inherited) in mtype
                # The point of the function it to get the bound of the virtual type that make sense for mtype
                # But because mtype is maybe a virtual/formal type, we need to get a real receiver first
@@ -1552,7 +1599,8 @@ class MParameterType
                                return res
                        end
                end
-               abort
+               # Cannot found `self` in `resolved_receiver`
+               return new MErrorType(model)
        end
 
        # A PT is fixed when:
@@ -1665,6 +1713,10 @@ abstract class MProxyType
                return self.mtype.can_resolve_for(mtype, anchor, mmodule)
        end
 
+       redef fun is_ok do return mtype.is_ok
+
+       redef fun is_legal_in(mmodule, anchor) do return mtype.is_legal_in(mmodule, anchor)
+
        redef fun lookup_fixed(mmodule, resolved_receiver)
        do
                var t = mtype.lookup_fixed(mmodule, resolved_receiver)
@@ -1763,7 +1815,7 @@ class MNullType
        redef fun c_name do return "null"
        redef fun as_nullable do return self
 
-       redef var as_notnull = new MBottomType(model) is lazy
+       redef var as_notnull: MBottomType = new MBottomType(model) is lazy
        redef fun need_anchor do return false
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
        redef fun can_resolve_for(mtype, anchor, mmodule) do return true
@@ -1778,9 +1830,10 @@ end
 # The special universal most specific type.
 #
 # This type is intended to be only used internally for type computation or analysis and should not be exposed to the user.
-# The bottom type can de used to denote things that are absurd, dead, or the absence of knowledge.
+# The bottom type can de used to denote things that are dead (no instance).
 #
 # Semantically it is the singleton `null.as_notnull`.
+# Is also means that `self.as_nullable == null`.
 class MBottomType
        super MType
        redef var model
@@ -1800,6 +1853,31 @@ class MBottomType
        redef fun collect_mtypes(mmodule) do return new HashSet[MClassType]
 end
 
+# A special type used as a silent error marker when building types.
+#
+# This type is intended to be only used internally for type operation and should not be exposed to the user.
+# The error type can de used to denote things that are conflicting or inconsistent.
+#
+# Some methods on types can return a `MErrorType` to denote a broken or a conflicting result.
+# Use `is_ok` to check if a type is (or contains) a `MErrorType` .
+class MErrorType
+       super MType
+       redef var model
+       redef fun to_s do return "error"
+       redef fun full_name do return "error"
+       redef fun c_name do return "error"
+       redef fun need_anchor do return false
+       redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
+       redef fun can_resolve_for(mtype, anchor, mmodule) do return true
+       redef fun is_ok do return false
+
+       redef fun collect_mclassdefs(mmodule) do return new HashSet[MClassDef]
+
+       redef fun collect_mclasses(mmodule) do return new HashSet[MClass]
+
+       redef fun collect_mtypes(mmodule) do return new HashSet[MClassType]
+end
+
 # A signature of a method
 class MSignature
        super MType
@@ -2053,14 +2131,27 @@ abstract class MProperty
                #print "select prop {mproperty} for {mtype} in {self}"
                # First, select all candidates
                var candidates = new Array[MPROPDEF]
-               for mpropdef in self.mpropdefs do
-                       # If the definition is not imported by the module, then skip
-                       if not mmodule.in_importation <= mpropdef.mclassdef.mmodule then continue
-                       # If the definition is not inherited by the type, then skip
-                       if not mtype.is_subtype(mmodule, null, mpropdef.mclassdef.bound_mtype) then continue
-                       # Else, we keep it
-                       candidates.add(mpropdef)
+
+               # Here we have two strategies: iterate propdefs or iterate classdefs.
+               var mpropdefs = self.mpropdefs
+               if mpropdefs.length <= 1 or mpropdefs.length < mtype.collect_mclassdefs(mmodule).length then
+                       # Iterate on all definitions of `self`, keep only those inherited by `mtype` in `mmodule`
+                       for mpropdef in mpropdefs do
+                               # If the definition is not imported by the module, then skip
+                               if not mmodule.in_importation <= mpropdef.mclassdef.mmodule then continue
+                               # If the definition is not inherited by the type, then skip
+                               if not mtype.is_subtype(mmodule, null, mpropdef.mclassdef.bound_mtype) then continue
+                               # Else, we keep it
+                               candidates.add(mpropdef)
+                       end
+               else
+                       # Iterate on all super-classdefs of `mtype`, keep only the definitions of `self`, if any.
+                       for mclassdef in mtype.collect_mclassdefs(mmodule) do
+                               var p = mclassdef.mpropdefs_by_property.get_or_null(self)
+                               if p != null then candidates.add p
+                       end
                end
+
                # Fast track for only one candidate
                if candidates.length <= 1 then
                        self.lookup_definitions_cache[mmodule, mtype] = candidates
@@ -2136,7 +2227,7 @@ abstract class MProperty
                        end
                end
                if res.is_empty then
-                       print "All lost! {candidates.join(", ")}"
+                       print_error "All lost! {candidates.join(", ")}"
                        # FIXME: should be abort!
                end
                return res
@@ -2247,6 +2338,9 @@ class MVirtualTypeProp
 
        # The formal type associated to the virtual type property
        var mvirtualtype = new MVirtualType(self)
+
+       # Is `self` the special virtual type `SELF`?
+       var is_selftype: Bool is lazy do return name == "SELF"
 end
 
 # A definition of a property (local property)
@@ -2277,6 +2371,7 @@ abstract class MPropDef
        do
                mclassdef.mpropdefs.add(self)
                mproperty.mpropdefs.add(self)
+               mclassdef.mpropdefs_by_property[mproperty] = self
                if mproperty.intro_mclassdef == mclassdef then
                        assert not isset mproperty._intro
                        mproperty.intro = self