model: introduce MConcern
[nit.git] / src / model / model.nit
index 78035f5..7481338 100644 (file)
 #
 # TODO: better doc
 #
-# TODO: liearization, closures, extern stuff
+# TODO: liearization, extern stuff
 # FIXME: better handling of the types
 module model
 
 import poset
 import location
-import model_base
+import mmodule
+import mdoc
+import ordered_tree
 private import more_collections
 
 redef class Model
@@ -105,6 +107,35 @@ redef class Model
 
        # The only null type
        var null_type: MNullType = new MNullType(self)
+
+       # Build an ordered tree with from `concerns`
+       fun concerns_tree(mconcerns: Collection[MConcern]): ConcernsTree do
+               var seen = new HashSet[MConcern]
+               var res = new ConcernsTree
+
+               var todo = new Array[MConcern]
+               todo.add_all mconcerns
+
+               while not todo.is_empty do
+                       var c = todo.pop
+                       if seen.has(c) then continue
+                       var pc = c.parent_concern
+                       if pc == null then
+                               res.add(null, c)
+                       else
+                               res.add(pc, c)
+                               todo.add(pc)
+                       end
+                       seen.add(c)
+               end
+
+               return res
+       end
+end
+
+# An OrderedTree that can be easily refined for display purposes
+class ConcernsTree
+       super OrderedTree[MConcern]
 end
 
 redef class MModule
@@ -222,7 +253,12 @@ redef class MModule
                        print("Fatal Error: no primitive class {name}")
                        exit(1)
                end
-               assert cla.length == 1 else print cla.join(", ")
+               if cla.length != 1 then
+                       var msg = "Fatal Error: more than one primitive class {name}:"
+                       for c in cla do msg += " {c.full_name}"
+                       print msg
+                       exit(1)
+               end
                return cla.first
        end
 
@@ -287,6 +323,8 @@ end
 # belong to a hierarchy since the property and the
 # hierarchy of a class depends of a module.
 class MClass
+       super MEntity
+
        # The module that introduce the class
        # While classes are not bound to a specific module,
        # the introducing module is used for naming an visibility
@@ -294,7 +332,7 @@ class MClass
 
        # The short name of the class
        # In Nit, the name of a class cannot evolve in refinements
-       var name: String
+       redef var name: String
 
        # The canonical name of the class
        # Example: `"owner::module::MyClass"`
@@ -412,6 +450,8 @@ end
 # class. Unlike `MClass`, a `MClassDef` is a local definition that belong to
 # a specific module
 class MClassDef
+       super MEntity
+
        # The module where the definition is
        var mmodule: MModule
 
@@ -453,6 +493,9 @@ class MClassDef
                self.to_s = "{mmodule}#{mclass}"
        end
 
+       # Actually the name of the `mclass`
+       redef fun name do return mclass.name
+
        # All declared super-types
        # FIXME: quite ugly but not better idea yet
        var supertypes: Array[MClassType] = new Array[MClassType]
@@ -542,6 +585,7 @@ end
 #  * foo(anchor, mmodule, othertype)
 #  * foo(othertype, mmodule, anchor)
 abstract class MType
+       super MEntity
 
        # The model of the type
        fun model: Model is abstract
@@ -549,8 +593,8 @@ abstract class MType
        # Return true if `self` is an subtype of `sup`.
        # The typing is done using the standard typing policy of Nit.
        #
-       # REQUIRE: `anchor == null` implies `not self.need_anchor and not sup.need_anchor`
-       # REQUIRE: `anchor != null` implies `self.can_resolve_for(anchor, null, mmodule) and sup.can_resolve_for(anchor, null, mmodule)`
+       # REQUIRE: `anchor == null implies not self.need_anchor and not sup.need_anchor`
+       # REQUIRE: `anchor != null implies self.can_resolve_for(anchor, null, mmodule) and sup.can_resolve_for(anchor, null, mmodule)`
        fun is_subtype(mmodule: MModule, anchor: nullable MClassType, sup: MType): Bool
        do
                var sub = self
@@ -674,8 +718,8 @@ abstract class MType
         # because "redef type U: Y". Therefore, Map[T, U] is bound to
        # Map[B, Y]
        #
-       # ENSURE: `not self.need_anchor` implies `(return) == self`
-       # ENSURE: `not (return).need_anchor`
+       # ENSURE: `not self.need_anchor implies result == self`
+       # ENSURE: `not result.need_anchor`
        fun anchor_to(mmodule: MModule, anchor: MClassType): MType
        do
                if not need_anchor then return self
@@ -700,8 +744,8 @@ abstract class MType
        # H[Int]  supertype_to  G  #->  G[Int, Bool]
        #
        # REQUIRE: `super_mclass` is a super-class of `self`
-       # REQUIRE: `self.need_anchor` implies `anchor != null and self.can_resolve_for(anchor, null, mmodule)`
-       # ENSURE: `(return).mclass = super_mclass`
+       # REQUIRE: `self.need_anchor implies anchor != null and self.can_resolve_for(anchor, null, mmodule)`
+       # ENSURE: `result.mclass = super_mclass`
        fun supertype_to(mmodule: MModule, anchor: nullable MClassType, super_mclass: MClass): MClassType
        do
                if super_mclass.arity == 0 then return super_mclass.mclass_type
@@ -792,7 +836,7 @@ abstract class MType
        # two function instead of one seems also to be a bad idea.
        #
        # REQUIRE: `can_resolve_for(mtype, anchor, mmodule)`
-       # ENSURE: `not self.need_anchor` implies `(return) == self`
+       # ENSURE: `not self.need_anchor implies result == self`
        fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MType is abstract
 
        # Can the type be resolved?
@@ -812,9 +856,9 @@ abstract class MType
        #    B[E] is a red hearing only the E is important,
        #    E make sense in A
        #
-       # REQUIRE: `anchor != null` implies `not anchor.need_anchor`
-       # REQUIRE: `mtype.need_anchor` implies `anchor != null and mtype.can_resolve_for(anchor, null, mmodule)`
-       # ENSURE: `not self.need_anchor` implies `(return) == true`
+       # REQUIRE: `anchor != null implies not anchor.need_anchor`
+       # REQUIRE: `mtype.need_anchor implies anchor != null and mtype.can_resolve_for(anchor, null, mmodule)`
+       # ENSURE: `not self.need_anchor implies result == true`
        fun can_resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule): Bool is abstract
 
        # Return the nullable version of the type
@@ -908,7 +952,7 @@ class MClassType
        end
 
        # The formal arguments of the type
-       # ENSURE: `(return).length == self.mclass.arity`
+       # ENSURE: `result.length == self.mclass.arity`
        var arguments: Array[MType] = new Array[MType]
 
        redef fun to_s do return mclass.to_s
@@ -1353,15 +1397,13 @@ class MNullType
        redef fun collect_mtypes(mmodule) do return new HashSet[MClassType]
 end
 
-# A signature of a method (or a closure)
+# A signature of a method
 class MSignature
        super MType
 
        # The each parameter (in order)
        var mparameters: Array[MParameter]
 
-       var mclosures = new Array[MParameter]
-
        # The return type (null for a procedure)
        var return_mtype: nullable MType
 
@@ -1374,10 +1416,6 @@ class MSignature
                        var d = p.mtype.depth
                        if d > dmax then dmax = d
                end
-               for p in mclosures do
-                       var d = p.mtype.depth
-                       if d > dmax then dmax = d
-               end
                return dmax + 1
        end
 
@@ -1389,9 +1427,6 @@ class MSignature
                for p in mparameters do
                        res += p.mtype.length
                end
-               for p in mclosures do
-                       res += p.mtype.length
-               end
                return res
        end
 
@@ -1421,7 +1456,7 @@ class MSignature
 
        redef fun to_s
        do
-               var b = new Buffer
+               var b = new FlatBuffer
                if not mparameters.is_empty then
                        b.append("(")
                        for i in [0..mparameters.length[ do
@@ -1455,9 +1490,6 @@ class MSignature
                        ret = ret.resolve_for(mtype, anchor, mmodule, cleanup_virtual)
                end
                var res = new MSignature(params, ret)
-               for p in self.mclosures do
-                       res.mclosures.add(p.resolve_for(mtype, anchor, mmodule, cleanup_virtual))
-               end
                return res
        end
 end
@@ -1473,6 +1505,15 @@ class MParameter
        # Is the parameter a vararg?
        var is_vararg: Bool
 
+       redef fun to_s
+       do
+               if is_vararg then
+                       return "{name}: {mtype}..."
+               else
+                       return "{name}: {mtype}"
+               end
+       end
+
        fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MParameter
        do
                if not self.mtype.need_anchor then return self
@@ -1494,6 +1535,8 @@ end
 # of any dynamic type).
 # For instance, a call site "x.foo" is associated to a `MProperty`.
 abstract class MProperty
+       super MEntity
+
        # The associated MPropDef subclass.
        # The two specialization hierarchy are symmetric.
        type MPROPDEF: MPropDef
@@ -1504,7 +1547,7 @@ abstract class MProperty
        var intro_mclassdef: MClassDef
 
        # The (short) name of the property
-       var name: String
+       redef var name: String
 
        # The canonical name of the property
        # Example: "owner::my_module::MyClass::my_method"
@@ -1573,37 +1616,7 @@ abstract class MProperty
                end
 
                # Second, filter the most specific ones
-               var res = new Array[MPROPDEF]
-               for pd1 in candidates do
-                       var cd1 = pd1.mclassdef
-                       var c1 = cd1.mclass
-                       var keep = true
-                       for pd2 in candidates do
-                               if pd2 == pd1 then continue # do not compare with self!
-                               var cd2 = pd2.mclassdef
-                               var c2 = cd2.mclass
-                               if c2.mclass_type == c1.mclass_type then
-                                       if cd2.mmodule.in_importation <= cd1.mmodule then
-                                               # cd2 refines cd1; therefore we skip pd1
-                                               keep = false
-                                               break
-                                       end
-                               else if cd2.bound_mtype.is_subtype(mmodule, null, cd1.bound_mtype) then
-                                       # cd2 < cd1; therefore we skip pd1
-                                       keep = false
-                                       break
-                               end
-                       end
-                       if keep then
-                               res.add(pd1)
-                       end
-               end
-               if res.is_empty then
-                       print "All lost! {candidates.join(", ")}"
-                       # FIXME: should be abort!
-               end
-               self.lookup_definitions_cache[mmodule, mtype] = res
-               return res
+               return select_most_specific(mmodule, candidates)
        end
 
        private var lookup_definitions_cache: HashMap2[MModule, MType, Array[MPROPDEF]] = new HashMap2[MModule, MType, Array[MPROPDEF]]
@@ -1616,13 +1629,13 @@ abstract class MProperty
        # If you want the really most specific property, then look at `lookup_next_definition`
        #
        # FIXME: Move to `MPropDef`?
-       fun lookup_super_definitions(mmodule: MModule, mtype: MType): Array[MPropDef]
+       fun lookup_super_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
        do
                assert not mtype.need_anchor
                if mtype isa MNullableType then mtype = mtype.mtype
 
                # First, select all candidates
-               var candidates = new Array[MPropDef]
+               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
@@ -1637,7 +1650,14 @@ abstract class MProperty
                if candidates.length <= 1 then return candidates
 
                # Second, filter the most specific ones
-               var res = new Array[MPropDef]
+               return select_most_specific(mmodule, candidates)
+       end
+
+       # Return an array containing olny the most specific property definitions
+       # This is an helper function for `lookup_definitions` and `lookup_super_definitions`
+       private fun select_most_specific(mmodule: MModule, candidates: Array[MPROPDEF]): Array[MPROPDEF]
+       do
+               var res = new Array[MPROPDEF]
                for pd1 in candidates do
                        var cd1 = pd1.mclassdef
                        var c1 = cd1.mclass
@@ -1647,12 +1667,12 @@ abstract class MProperty
                                var cd2 = pd2.mclassdef
                                var c2 = cd2.mclass
                                if c2.mclass_type == c1.mclass_type then
-                                       if cd2.mmodule.in_importation <= cd1.mmodule then
+                                       if cd2.mmodule.in_importation < cd1.mmodule then
                                                # cd2 refines cd1; therefore we skip pd1
                                                keep = false
                                                break
                                        end
-                               else if cd2.bound_mtype.is_subtype(mmodule, null, cd1.bound_mtype) then
+                               else if cd2.bound_mtype.is_subtype(mmodule, null, cd1.bound_mtype) and cd2.bound_mtype != cd1.bound_mtype then
                                        # cd2 < cd1; therefore we skip pd1
                                        keep = false
                                        break
@@ -1680,6 +1700,7 @@ abstract class MProperty
        # REQUIRE: `mtype.has_mproperty(mmodule, self)`
        fun lookup_first_definition(mmodule: MModule, mtype: MType): MPROPDEF
        do
+               assert mtype.has_mproperty(mmodule, self)
                return lookup_all_definitions(mmodule, mtype).first
        end
 
@@ -1730,6 +1751,10 @@ class MMethod
                super
        end
 
+       # Is the property defined at the top_level of the module?
+       # Currently such a property are stored in `Object`
+       var is_toplevel: Bool writable = false
+
        # Is the property a constructor?
        # Warning, this property can be inherited by subclasses with or without being a constructor
        # therefore, you should use `is_init_for` the verify if the property is a legal constructor for a given class
@@ -1779,6 +1804,7 @@ end
 # Unlike `MProperty`, a `MPropDef` is a local definition that belong to a
 # specific class definition (which belong to a specific module)
 abstract class MPropDef
+       super MEntity
 
        # The associated `MProperty` subclass.
        # the two specialization hierarchy are symmetric
@@ -1806,6 +1832,9 @@ abstract class MPropDef
                self.to_s = "{mclassdef}#{mproperty}"
        end
 
+       # Actually the name of the `mproperty`
+       redef fun name do return mproperty.name
+
        # Internal name combining the module, the class and the property
        # Example: "mymodule#MyClass#mymethod"
        redef var to_s: String
@@ -1847,8 +1876,14 @@ class MMethodDef
        # The signature attached to the property definition
        var msignature: nullable MSignature writable = null
 
-       # The the method definition abstract?
+       # Is the method definition abstract?
        var is_abstract: Bool writable = false
+
+       # Is the method definition intern?
+       var is_intern writable = false
+
+       # Is the method definition extern?
+       var is_extern writable = false
 end
 
 # A local definition of an attribute
@@ -1903,10 +1938,28 @@ class MClassKind
                self.to_s = s
                self.need_init = need_init
        end
+
+       # Can a class of kind `self` specializes a class of kine `other`?
+       fun can_specialize(other: MClassKind): Bool
+       do
+               if other == interface_kind then return true # everybody can specialize interfaces
+               if self == interface_kind or self == enum_kind then
+                       # no other case for interfaces
+                       return false
+               else if self == extern_kind then
+                       # only compatible with themselve
+                       return self == other
+               else if other == enum_kind or other == extern_kind then
+                       # abstract_kind and concrete_kind are incompatible
+                       return false
+               end
+               # remain only abstract_kind and concrete_kind
+               return true
+       end
 end
 
 fun abstract_kind: MClassKind do return once new MClassKind("abstract class", true)
 fun concrete_kind: MClassKind do return once new MClassKind("class", true)
 fun interface_kind: MClassKind do return once new MClassKind("interface", false)
 fun enum_kind: MClassKind do return once new MClassKind("enum", false)
-fun extern_kind: MClassKind do return once new MClassKind("extern", false)
+fun extern_kind: MClassKind do return once new MClassKind("extern class", false)