src/model: generalize visibility to all MEntities
[nit.git] / src / model / model.nit
index cd1e570..d4ca515 100644 (file)
@@ -30,6 +30,15 @@ import mdoc
 import ordered_tree
 private import more_collections
 
+redef class MEntity
+       # The visibility of the MEntity.
+       #
+       # MPackages, MGroups and MModules are always public.
+       # The visibility of `MClass` and `MProperty` is defined by the keyword used.
+       # `MClassDef` and `MPropDef` return the visibility of `MClass` and `MProperty`.
+       fun visibility: MVisibility do return public_visibility
+end
+
 redef class Model
        # All known classes
        var mclasses = new Array[MClass]
@@ -119,7 +128,17 @@ redef class Model
        end
 end
 
-# An OrderedTree that can be easily refined for display purposes
+# An OrderedTree bound to MEntity.
+#
+# We introduce a new class so it can be easily refined by tools working
+# with a Model.
+class MEntityTree
+       super OrderedTree[MEntity]
+end
+
+# A MEntityTree borned to MConcern.
+#
+# TODO remove when nitdoc is fully merged with model_collect
 class ConcernsTree
        super OrderedTree[MConcern]
 end
@@ -206,6 +225,24 @@ redef class MModule
        # The primitive type `Int`
        var int_type: MClassType = self.get_primitive_class("Int").mclass_type is lazy
 
+       # The primitive type `Byte`
+       var byte_type: MClassType = self.get_primitive_class("Byte").mclass_type is lazy
+
+       # The primitive type `Int8`
+       var int8_type: MClassType = self.get_primitive_class("Int8").mclass_type is lazy
+
+       # The primitive type `Int16`
+       var int16_type: MClassType = self.get_primitive_class("Int16").mclass_type is lazy
+
+       # The primitive type `UInt16`
+       var uint16_type: MClassType = self.get_primitive_class("UInt16").mclass_type is lazy
+
+       # The primitive type `Int32`
+       var int32_type: MClassType = self.get_primitive_class("Int32").mclass_type is lazy
+
+       # The primitive type `UInt32`
+       var uint32_type: MClassType = self.get_primitive_class("UInt32").mclass_type is lazy
+
        # The primitive type `Char`
        var char_type: MClassType = self.get_primitive_class("Char").mclass_type is lazy
 
@@ -257,14 +294,16 @@ redef class MModule
                        if name == "Bool" and self.model.get_mclasses_by_name("Object") != null then
                                # Bool is injected because it is needed by engine to code the result
                                # of the implicit casts.
-                               var c = new MClass(self, name, null, enum_kind, public_visibility)
-                               var cladef = new MClassDef(self, c.mclass_type, new Location(null, 0,0,0,0))
+                               var loc = model.no_location
+                               var c = new MClass(self, name, loc, null, enum_kind, public_visibility)
+                               var cladef = new MClassDef(self, c.mclass_type, loc)
                                cladef.set_supertypes([object_type])
                                cladef.add_in_hierarchy
                                return c
                        end
                        print("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}:"
@@ -281,18 +320,15 @@ redef class MModule
                var props = self.model.get_mproperties_by_name(name)
                if props == null then return null
                var res: nullable MMethod = null
+               var recvtype = recv.intro.bound_mtype
                for mprop in props do
                        assert mprop isa MMethod
-                       var intro = mprop.intro_mclassdef
-                       for mclassdef in recv.mclassdefs do
-                               if not self.in_importation.greaters.has(mclassdef.mmodule) then continue
-                               if not mclassdef.in_hierarchy.greaters.has(intro) then continue
-                               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}")
-                                       abort
-                               end
+                       if not recvtype.has_mproperty(self, mprop) then continue
+                       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}")
+                               abort
                        end
                end
                return res
@@ -358,7 +394,9 @@ class MClass
 
        # The short name of the class
        # In Nit, the name of a class cannot evolve in refinements
-       redef var name: String
+       redef var name
+
+       redef var location
 
        # The canonical name of the class
        #
@@ -436,7 +474,7 @@ class MClass
 
        # The visibility of the class
        # In Nit, the visibility of a class cannot evolve in refinements
-       var visibility: MVisibility
+       redef var visibility
 
        init
        do
@@ -514,6 +552,18 @@ class MClass
 
        # Is there a `new` factory to allow the pseudo instantiation?
        var has_new_factory = false is writable
+
+       # Is `self` a standard or abstract class kind?
+       var is_class: Bool is lazy do return kind == concrete_kind or kind == abstract_kind
+
+       # Is `self` an interface kind?
+       var is_interface: Bool is lazy do return kind == interface_kind
+
+       # Is `self` an enum kind?
+       var is_enum: Bool is lazy do return kind == enum_kind
+
+       # Is `self` and abstract class?
+       var is_abstract: Bool is lazy do return kind == abstract_kind
 end
 
 
@@ -550,12 +600,13 @@ class MClassDef
        # ENSURE: `bound_mtype.mclass == self.mclass`
        var bound_mtype: MClassType
 
-       # The origin of the definition
-       var location: Location
+       redef var location: Location
+
+       redef fun visibility do return mclass.visibility
 
        # Internal name combining the module and the class
-       # Example: "mymodule#MyClass"
-       redef var to_s: String is noinit
+       # Example: "mymodule$MyClass"
+       redef var to_s is noinit
 
        init
        do
@@ -566,41 +617,41 @@ class MClassDef
                        assert not isset mclass._intro
                        mclass.intro = self
                end
-               self.to_s = "{mmodule}#{mclass}"
+               self.to_s = "{mmodule}${mclass}"
        end
 
        # Actually the name of the `mclass`
        redef fun name do return mclass.name
 
-       # The module and class name separated by a '#'.
+       # The module and class name separated by a '$'.
        #
        # The short-name of the class is used for introduction.
-       # Example: "my_module#MyClass"
+       # Example: "my_module$MyClass"
        #
        # The full-name of the class is used for refinement.
-       # Example: "my_module#intro_module::MyClass"
+       # Example: "my_module$intro_module::MyClass"
        redef var full_name is lazy do
                if is_intro then
-                       # public gives 'p#A'
-                       # private gives 'p::m#A'
-                       return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}"
-               else if mclass.intro_mmodule.mproject != mmodule.mproject then
-                       # public gives 'q::n#p::A'
-                       # private gives 'q::n#p::m::A'
-                       return "{mmodule.full_name}#{mclass.full_name}"
+                       # public gives 'p$A'
+                       # private gives 'p::m$A'
+                       return "{mmodule.namespace_for(mclass.visibility)}${mclass.name}"
+               else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
+                       # public gives 'q::n$p::A'
+                       # private gives 'q::n$p::m::A'
+                       return "{mmodule.full_name}${mclass.full_name}"
                else if mclass.visibility > private_visibility then
-                       # public gives 'p::n#A'
-                       return "{mmodule.full_name}#{mclass.name}"
+                       # public gives 'p::n$A'
+                       return "{mmodule.full_name}${mclass.name}"
                else
-                       # private gives 'p::n#::m::A' (redundant p is omitted)
-                       return "{mmodule.full_name}#::{mclass.intro_mmodule.name}::{mclass.name}"
+                       # private gives 'p::n$::m::A' (redundant p is omitted)
+                       return "{mmodule.full_name}$::{mclass.intro_mmodule.name}::{mclass.name}"
                end
        end
 
        redef var c_name is lazy do
                if is_intro then
                        return "{mmodule.c_namespace_for(mclass.visibility)}___{mclass.c_name}"
-               else if mclass.intro_mmodule.mproject == mmodule.mproject and mclass.visibility > private_visibility then
+               else if mclass.intro_mmodule.mpackage == mmodule.mpackage and mclass.visibility > private_visibility then
                        return "{mmodule.c_name}___{mclass.name.to_cmangle}"
                else
                        return "{mmodule.c_name}___{mclass.c_name}"
@@ -778,19 +829,19 @@ abstract class MType
                end
                #print "4.is {sub} a {sup}? <- no more resolution"
 
-               assert sub isa MClassType else print "{sub} <? {sub}" # It is the only remaining type
-
-               # A unfixed formal type can only accept itself
-               if sup isa MFormalType then
-                       return false
+               if sub isa MBottomType then
+                       return true
                end
 
-               if sup isa MNullType then
-                       # `sup` accepts only null
+               assert sub isa MClassType else print "{sub} <? {sub}" # 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
+                       # These types are not super-types of Class-based types.
                        return false
                end
 
-               assert sup isa MClassType # It is the only remaining type
+               assert sup isa MClassType else print "got {sup} {sub.inspect}" # It is the only remaining type
 
                # Now both are MClassType, we need to dig
 
@@ -979,7 +1030,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 conflict, the method aborts.
+       # In case of conflicts or inconsistencies in the model, the method returns a `MBottomType`.
        fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType do return self
 
        # Resolve the formal type to its simplest equivalent form.
@@ -993,6 +1044,8 @@ 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`.
        fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do return self
 
        # Can the type be resolved?
@@ -1125,6 +1178,8 @@ class MClassType
 
        redef fun model do return self.mclass.intro_mmodule.model
 
+       redef fun location do return mclass.location
+
        # TODO: private init because strongly bounded to its mclass. see `mclass.mclass_type`
 
        # The formal arguments of the type
@@ -1247,10 +1302,10 @@ class MGenericType
 
        # The short-name of the class, then the full-name of each type arguments within brackets.
        # Example: `"Map[String, List[Int]]"`
-       redef var to_s: String is noinit
+       redef var to_s is noinit
 
        # The full-name of the class, then the full-name of each type arguments within brackets.
-       # Example: `"standard::Map[standard::String, standard::List[standard::Int]]"`
+       # Example: `"core::Map[core::String, core::List[core::Int]]"`
        redef var full_name is lazy do
                var args = new Array[String]
                for t in arguments do
@@ -1269,7 +1324,7 @@ class MGenericType
                return res.to_s
        end
 
-       redef var need_anchor: Bool is noinit
+       redef var need_anchor is noinit
 
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
        do
@@ -1330,11 +1385,13 @@ class MVirtualType
        # Its the definitions of this property that determine the bound or the virtual type.
        var mproperty: MVirtualTypeProp
 
+       redef fun location do return mproperty.location
+
        redef fun model do return self.mproperty.intro_mclassdef.mmodule.model
 
        redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
        do
-               return lookup_single_definition(mmodule, resolved_receiver).bound.as(not null)
+               return lookup_single_definition(mmodule, resolved_receiver).bound or else new MBottomType(model)
        end
 
        private fun lookup_single_definition(mmodule: MModule, resolved_receiver: MType): MVirtualTypeDef
@@ -1369,7 +1426,8 @@ class MVirtualType
                assert resolved_receiver isa MClassType # It is the only remaining type
 
                var prop = lookup_single_definition(mmodule, resolved_receiver)
-               var res = prop.bound.as(not null)
+               var res = prop.bound
+               if res == null then return new MBottomType(model)
 
                # Recursively lookup the fixed result
                res = res.lookup_fixed(mmodule, resolved_receiver)
@@ -1459,6 +1517,8 @@ class MParameterType
 
        redef fun model do return self.mclass.intro_mmodule.model
 
+       redef fun location do return mclass.location
+
        # The position of the parameter (0 for the first parameter)
        # FIXME: is `position` a better name?
        var rank: Int
@@ -1536,6 +1596,7 @@ class MParameterType
                end
                if resolved_receiver isa MNullableType then resolved_receiver = resolved_receiver.mtype
                if resolved_receiver isa MParameterType then
+                       assert anchor != null
                        assert resolved_receiver.mclass == anchor.mclass
                        resolved_receiver = anchor.arguments[resolved_receiver.rank]
                        if resolved_receiver isa MNullableType then resolved_receiver = resolved_receiver.mtype
@@ -1583,6 +1644,8 @@ abstract class MProxyType
        # The base type
        var mtype: MType
 
+       redef fun location do return mtype.location
+
        redef fun model do return self.mtype.model
        redef fun need_anchor do return mtype.need_anchor
        redef fun as_nullable do return mtype.as_nullable
@@ -1637,7 +1700,7 @@ class MNullableType
                self.to_s = "nullable {mtype}"
        end
 
-       redef var to_s: String is noinit
+       redef var to_s is noinit
 
        redef var full_name is lazy do return "nullable {mtype.full_name}"
 
@@ -1691,14 +1754,38 @@ end
 # The is only one null type per model, see `MModel::null_type`.
 class MNullType
        super MType
-       redef var model: Model
+       redef var model
        redef fun to_s do return "null"
        redef fun full_name do return "null"
        redef fun c_name do return "null"
        redef fun as_nullable do return self
 
-       # Aborts on `null`
-       redef fun as_notnull do abort # sorry...
+       redef var as_notnull = 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
+
+       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
+
+# 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.
+#
+# Semantically it is the singleton `null.as_notnull`.
+class MBottomType
+       super MType
+       redef var model
+       redef fun to_s do return "bottom"
+       redef fun full_name do return "bottom"
+       redef fun c_name do return "bottom"
+       redef fun as_nullable do return model.null_type
+       redef fun as_notnull do return self
        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
@@ -1717,6 +1804,15 @@ class MSignature
        # The each parameter (in order)
        var mparameters: Array[MParameter]
 
+       # Returns a parameter named `name`, if any.
+       fun mparameter_by_name(name: String): nullable MParameter
+       do
+               for p in mparameters do
+                       if p.name == name then return p
+               end
+               return null
+       end
+
        # The return type (null for a procedure)
        var return_mtype: nullable MType
 
@@ -1750,19 +1846,29 @@ class MSignature
                for i in [0..mparameters.length[ do
                        var parameter = mparameters[i]
                        if parameter.is_vararg then
-                               assert vararg_rank == -1
+                               if vararg_rank >= 0 then
+                                       # If there is more than one vararg,
+                                       # consider that additional arguments cannot be mapped.
+                                       vararg_rank = -1
+                                       break
+                               end
                                vararg_rank = i
                        end
                end
                self.vararg_rank = vararg_rank
        end
 
-       # The rank of the ellipsis (`...`) for vararg (starting from 0).
+       # The rank of the main ellipsis (`...`) for vararg (starting from 0).
        # value is -1 if there is no vararg.
        # Example: for "(a: Int, b: Bool..., c: Char)" #-> vararg_rank=1
+       #
+       # From a model POV, a signature can contain more than one vararg parameter,
+       # the `vararg_rank` just indicates the one that will receive the additional arguments.
+       # However, currently, if there is more that one vararg parameter, no one will be the main one,
+       # and additional arguments will be refused.
        var vararg_rank: Int is noinit
 
-       # The number or parameters
+       # The number of parameters
        fun arity: Int do return mparameters.length
 
        redef fun to_s
@@ -1810,7 +1916,7 @@ class MParameter
        super MEntity
 
        # The name of the parameter
-       redef var name: String
+       redef var name
 
        # The static type of the parameter
        var mtype: MType
@@ -1864,14 +1970,27 @@ abstract class MProperty
        var intro_mclassdef: MClassDef
 
        # The (short) name of the property
-       redef var name: String
+       redef var name
+
+       redef var location
 
        # The canonical name of the property.
        #
-       # It is the short-`name` prefixed by the short-name of the class and the full-name of the module.
-       # Example: "my_project::my_module::MyClass::my_method"
+       # It is currently the short-`name` prefixed by the short-name of the class and the full-name of the module.
+       # Example: "my_package::my_module::MyClass::my_method"
+       #
+       # The full-name of the module is needed because two distinct modules of the same package can
+       # still refine the same class and introduce homonym properties.
+       #
+       # For public properties not introduced by refinement, the module name is not used.
+       #
+       # Example: `my_package::MyClass::My_method`
        redef var full_name is lazy do
-               return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+               if intro_mclassdef.is_intro then
+                       return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+               else
+                       return "{intro_mclassdef.mmodule.full_name}::{intro_mclassdef.mclass.name}::{name}"
+               end
        end
 
        redef var c_name is lazy do
@@ -1880,7 +1999,7 @@ abstract class MProperty
        end
 
        # The visibility of the property
-       var visibility: MVisibility
+       redef var visibility
 
        # Is the property usable as an initializer?
        var is_autoinit = false is writable
@@ -2101,6 +2220,10 @@ class MMethod
        do
                return self.is_init
        end
+
+       # A specific method that is safe to call on null.
+       # Currently, only `==`, `!=` and `is_same_instance` are safe
+       fun is_null_safe: Bool do return name == "==" or name == "!=" or name == "is_same_instance"
 end
 
 # A global attribute
@@ -2141,8 +2264,9 @@ abstract class MPropDef
        # The associated global property
        var mproperty: MPROPERTY
 
-       # The origin of the definition
-       var location: Location
+       redef var location: Location
+
+       redef fun visibility do return mproperty.visibility
 
        init
        do
@@ -2152,7 +2276,7 @@ abstract class MPropDef
                        assert not isset mproperty._intro
                        mproperty.intro = self
                end
-               self.to_s = "{mclassdef}#{mproperty}"
+               self.to_s = "{mclassdef}${mproperty}"
        end
 
        # Actually the name of the `mproperty`
@@ -2166,31 +2290,31 @@ abstract class MPropDef
        #  * a property "p::m::A::x"
        #  * redefined in a refinement of a class "q::n::B"
        #  * in a module "r::o"
-       #  * so "r::o#q::n::B#p::m::A::x"
+       #  * so "r::o$q::n::B$p::m::A::x"
        #
        # Fortunately, the full-name is simplified when entities are repeated.
-       # For the previous case, the simplest form is "p#A#x".
+       # For the previous case, the simplest form is "p$A$x".
        redef var full_name is lazy do
                var res = new FlatBuffer
 
-               # The first part is the mclassdef. Worst case is "r::o#q::n::B"
+               # The first part is the mclassdef. Worst case is "r::o$q::n::B"
                res.append mclassdef.full_name
 
-               res.append "#"
+               res.append "$"
 
                if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
                        # intro are unambiguous in a class
                        res.append name
                else
                        # Just try to simplify each part
-                       if mclassdef.mmodule.mproject != mproperty.intro_mclassdef.mmodule.mproject then
+                       if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
                                # precise "p::m" only if "p" != "r"
-                               res.append mproperty.intro_mclassdef.mmodule.full_name
+                               res.append mproperty.intro_mclassdef.mmodule.namespace_for(mproperty.visibility)
                                res.append "::"
                        else if mproperty.visibility <= private_visibility then
-                               # Same project ("p"=="q"), but private visibility,
+                               # Same package ("p"=="q"), but private visibility,
                                # does the module part ("::m") need to be displayed
-                               if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mproject then
+                               if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mpackage then
                                        res.append "::"
                                        res.append mproperty.intro_mclassdef.mmodule.name
                                        res.append "::"
@@ -2230,11 +2354,11 @@ abstract class MPropDef
        redef fun model do return mclassdef.model
 
        # Internal name combining the module, the class and the property
-       # Example: "mymodule#MyClass#mymethod"
-       redef var to_s: String is noinit
+       # Example: "mymodule$MyClass$mymethod"
+       redef var to_s is noinit
 
        # Is self the definition that introduce the property?
-       fun is_intro: Bool do return mproperty.intro == self
+       fun is_intro: Bool do return isset mproperty._intro and mproperty.intro == self
 
        # Return the next definition in linearization of `mtype`.
        #
@@ -2334,7 +2458,7 @@ end
 # Note this class is basically an enum.
 # FIXME: use a real enum once user-defined enums are available
 class MClassKind
-       redef var to_s: String
+       redef var to_s
 
        # Is a constructor required?
        var need_init: Bool