Merge branch 'bench'
[nit.git] / src / model / model.nit
index fb35980..80d643f 100644 (file)
@@ -32,6 +32,7 @@ module model
 import poset
 import location
 import model_base
+private import more_collections
 
 redef class Model
        # All known classes
@@ -148,6 +149,31 @@ redef class MModule
                return res
        end
 
+       # Sort a given array of classes using the linerarization order of the module
+       # The most general is first, the most specific is last
+       fun linearize_mclasses(mclasses: Array[MClass])
+       do
+               self.flatten_mclass_hierarchy.sort(mclasses)
+       end
+
+       # Sort a given array of class definitions using the linerarization order of the module
+       # the refinement link is stronger than the specialisation link
+       # The most general is first, the most specific is last
+       fun linearize_mclassdefs(mclassdefs: Array[MClassDef])
+       do
+               var sorter = new MClassDefSorter(self)
+               sorter.sort(mclassdefs)
+       end
+
+       # Sort a given array of property definitions using the linerarization order of the module
+       # the refinement link is stronger than the specialisation link
+       # The most general is first, the most specific is last
+       fun linearize_mpropdefs(mpropdefs: Array[MPropDef])
+       do
+               var sorter = new MPropDefSorter(self)
+               sorter.sort(mpropdefs)
+       end
+
        private var flatten_mclass_hierarchy_cache: nullable POSet[MClass] = null
 
        # The primitive type Object, the root of the class hierarchy
@@ -219,6 +245,32 @@ redef class MModule
        end
 end
 
+private class MClassDefSorter
+       super AbstractSorter[MClassDef]
+       var mmodule: MModule
+       redef fun compare(a, b)
+       do
+               var ca = a.mclass
+               var cb = b.mclass
+               if ca != cb then return mmodule.flatten_mclass_hierarchy.compare(ca, cb)
+               return mmodule.model.mclassdef_hierarchy.compare(a, b)
+       end
+end
+
+private class MPropDefSorter
+       super AbstractSorter[MPropDef]
+       var mmodule: MModule
+       redef fun compare(pa, pb)
+       do
+               var a = pa.mclassdef
+               var b = pb.mclassdef
+               var ca = a.mclass
+               var cb = b.mclass
+               if ca != cb then return mmodule.flatten_mclass_hierarchy.compare(ca, cb)
+               return mmodule.model.mclassdef_hierarchy.compare(a, b)
+       end
+end
+
 # A named class
 #
 # MClass are global to the model; it means that a MClass is not bound to a
@@ -699,8 +751,6 @@ abstract class MType
 
        # Return the nullable version of the type
        # If the type is already nullable then self is returned
-       #
-       # FIXME: DO NOT WORK YET
        fun as_nullable: MType
        do
                var res = self.as_nullable_cache
@@ -878,10 +928,10 @@ class MGenericType
        end
 
        # Recursively print the type of the arguments within brackets.
-       # Example: "Map[String,List[Int]]"
+       # Example: "Map[String, List[Int]]"
        redef fun to_s
        do
-               return "{mclass}[{arguments.join(",")}]"
+               return "{mclass}[{arguments.join(", ")}]"
        end
 
        redef var need_anchor: Bool
@@ -944,7 +994,6 @@ class MVirtualType
 
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
        do
-               if not cleanup_virtual then return self
                # 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
@@ -953,9 +1002,25 @@ class MVirtualType
                # Now, we can get the bound
                var verbatim_bound = lookup_bound(mmodule, resolved_reciever)
                # The bound is exactly as declared in the "type" property, so we must resolve it again
-               var res = verbatim_bound.resolve_for(mtype, anchor, mmodule, true)
+               var res = verbatim_bound.resolve_for(mtype, anchor, mmodule, cleanup_virtual)
                #print "{class_name}: {self}/{mtype}/{anchor} -> {self}/{resolved_reciever}/{anchor} -> {verbatim_bound}/{mtype}/{anchor} -> {res}"
-               return res
+
+               # What to return here? There is a bunch a special cases:
+               # If 'cleanup_virtual' we must return the resolved type, since we cannot return self
+               if cleanup_virtual then return res
+               # If the reciever is a intern class, then the virtual type cannot be redefined since there is no possible subclass. self is just fixed. so simply return the resolution
+               if resolved_reciever isa MNullableType then resolved_reciever = resolved_reciever.mtype
+               if resolved_reciever.as(MClassType).mclass.kind == enum_kind then return res
+               # If the resolved type isa MVirtualType, it means that self was bound to it, and cannot be unbound. self is just fixed. so return the resolution.
+               if res isa MVirtualType then return res
+               # It the resolved type isa intern class, then there is no possible valid redefinition is any potentiel subclass. self is just fixed. so simply return the resolution
+               if res isa MClassType and res.mclass.kind == enum_kind then return res
+               # TODO: Add 'fixed' virtual type in the specification.
+               # TODO: What if bound to a MParameterType?
+               # Note that Nullable types can always be redefined by the non nullable version, so there is no specific case on it.
+
+               # If anything apply, then `self' cannot be resolved, so return self
+               return self
        end
 
        redef fun to_s do return self.mproperty.to_s
@@ -1076,7 +1141,6 @@ class MParameterType
 end
 
 # A type prefixed with "nullable"
-# FIXME Stub implementation
 class MNullableType
        super MType
 
@@ -1446,19 +1510,53 @@ abstract class MProperty
        end
 
        # Return the most specific definition in the linearization of `mtype`.
-       # If mtype does not know mproperty then null is returned.
        #
        # If you want to know the next properties in the linearization,
        # look at `MPropDef::lookup_next_definition`.
        #
-       # FIXME: NOT YET IMPLEMENTED
+       # FIXME: the linearisation is still unspecified
        #
        # REQUIRE: not mtype.need_anchor
-       fun lookup_first_definition(mmodule: MModule, mtype: MType): nullable MPROPDEF
+       # REQUIRE: mtype.has_mproperty(mmodule, self)
+       fun lookup_first_definition(mmodule: MModule, mtype: MType): MPROPDEF
+       do
+               return lookup_all_definitions(mmodule, mtype).first
+       end
+
+       # Return all definitions in a linearisation order
+       # Most speficic first, most general last
+       fun lookup_all_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
        do
                assert not mtype.need_anchor
-               return null
+               if mtype isa MNullableType then mtype = mtype.mtype
+
+               var cache = self.lookup_all_definitions_cache[mmodule, mtype]
+               if cache != null then return cache
+
+               #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)
+               end
+               # Fast track for only one candidate
+               if candidates.length <= 1 then
+                       self.lookup_all_definitions_cache[mmodule, mtype] = candidates
+                       return candidates
+               end
+
+               mmodule.linearize_mpropdefs(candidates)
+               candidates = candidates.reversed
+               self.lookup_all_definitions_cache[mmodule, mtype] = candidates
+               return candidates
        end
+
+       private var lookup_all_definitions_cache: HashMap2[MModule, MType, Array[MPROPDEF]] = new HashMap2[MModule, MType, Array[MPROPDEF]]
 end
 
 # A global method
@@ -1558,17 +1656,21 @@ abstract class MPropDef
        fun is_intro: Bool do return mproperty.intro == self
 
        # Return the next definition in linearization of `mtype`.
-       # If there is no next method then null is returned.
        #
        # This method is used to determine what method is called by a super.
        #
-       # FIXME: NOT YET IMPLEMENTED
-       #
        # REQUIRE: not mtype.need_anchor
-       fun lookup_next_definition(mmodule: MModule, mtype: MType): nullable MPROPDEF
+       fun lookup_next_definition(mmodule: MModule, mtype: MType): MPROPDEF
        do
                assert not mtype.need_anchor
-               return null
+
+               var mpropdefs = self.mproperty.lookup_all_definitions(mmodule, mtype)
+               var i = mpropdefs.iterator
+               while i.is_ok and i.item != self do i.next
+               assert has_property: i.is_ok
+               i.next
+               assert has_next_property: i.is_ok
+               return i.item
        end
 end