model: Add `can_customize_isa` and `can_init` attributes to class kinds
[nit.git] / src / model / model.nit
index 2f16bc5..11592d7 100644 (file)
@@ -50,6 +50,13 @@ redef class Model
        #
        # Each classdef is associated with its super-classdefs in regard to
        # its module of definition.
+       #
+       # ~~~
+       # var m = new ModelDiamond
+       # assert     m.mclassdef_hierarchy.has_edge(m.mclassdef_b, m.mclassdef_a)
+       # assert not m.mclassdef_hierarchy.has_edge(m.mclassdef_a, m.mclassdef_b)
+       # assert not m.mclassdef_hierarchy.has_edge(m.mclassdef_b, m.mclassdef_c)
+       # ~~~
        var mclassdef_hierarchy = new POSet[MClassDef]
 
        # Class-type hierarchy restricted to the introduction.
@@ -81,6 +88,12 @@ redef class Model
        # (instead of an empty array)
        #
        # Visibility or modules are not considered
+       #
+       # ~~~
+       # var m = new ModelStandalone
+       # assert m.get_mclasses_by_name("Object") == [m.mclass_o]
+       # assert m.get_mclasses_by_name("Fail") == null
+       # ~~~
        fun get_mclasses_by_name(name: String): nullable Array[MClass]
        do
                return mclasses_by_name.get_or_null(name)
@@ -1478,8 +1491,8 @@ class MVirtualType
 
        # A VT is fixed when:
        # * the VT is (re-)defined with the annotation `is fixed`
-       # * the VT is (indirectly) bound to an enum class (see `enum_kind`) since there is no subtype possible
-       # * the receiver is an enum class since there is no subtype possible
+       # * the receiver is an enum class since there is no subtype that can
+       #   redefine this virtual type
        redef fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType
        do
                assert not resolved_receiver.need_anchor
@@ -1493,13 +1506,10 @@ class MVirtualType
                # Recursively lookup the fixed result
                res = res.lookup_fixed(mmodule, resolved_receiver)
 
-               # 1. For a fixed VT, return the resolved bound
+               # For a fixed VT, return the resolved bound
                if prop.is_fixed then return res
 
-               # 2. For a enum boud, return the bound
-               if res isa MClassType and res.mclass.kind == enum_kind then return res
-
-               # 3. for a enum receiver return the bound
+               # For a enum receiver return the bound
                if resolved_receiver.mclass.kind == enum_kind then return res
 
                return self
@@ -1620,9 +1630,7 @@ class MParameterType
        end
 
        # A PT is fixed when:
-       # * Its bound is a enum class (see `enum_kind`).
-       #   The PT is just useless, but it is still a case.
-       # * More usually, the `resolved_receiver` is a subclass of `self.mclass`,
+       # * The `resolved_receiver` is a subclass of `self.mclass`,
        #   so it is necessarily fixed in a `super` clause, either with a normal type
        #   or with another PT.
        #   See `resolve_for` for examples about related issues.
@@ -1641,13 +1649,7 @@ class MParameterType
                #print "{class_name}: {self}/{mtype}/{anchor}?"
 
                if mtype isa MGenericType and mtype.mclass == self.mclass then
-                       var res = mtype.arguments[self.rank]
-                       if anchor != null and res.need_anchor then
-                               # Maybe the result can be resolved more if are bound to a final class
-                               var r2 = res.anchor_to(mmodule, anchor)
-                               if r2 isa MClassType and r2.mclass.kind == enum_kind then return r2
-                       end
-                       return res
+                       return mtype.arguments[self.rank]
                end
 
                # self is a parameter type of mtype (or of a super-class of mtype)
@@ -2441,11 +2443,9 @@ abstract class MPropDef
                                        res.append "::"
                                end
                        end
-                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
-                               # precise "B" only if not the same class than "A"
-                               res.append mproperty.intro_mclassdef.name
-                               res.append "::"
-                       end
+                       # precise "B" because it is not the same class than "A"
+                       res.append mproperty.intro_mclassdef.name
+                       res.append "::"
                        # Always use the property name "x"
                        res.append mproperty.name
                end
@@ -2463,10 +2463,8 @@ abstract class MPropDef
                                res.append mproperty.intro_mclassdef.mmodule.c_name
                                res.append "__"
                        end
-                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
-                               res.append mproperty.intro_mclassdef.name.to_cmangle
-                               res.append "__"
-                       end
+                       res.append mproperty.intro_mclassdef.name.to_cmangle
+                       res.append "__"
                        res.append mproperty.name.to_cmangle
                end
                return res.to_s
@@ -2583,6 +2581,12 @@ end
 class MClassKind
        redef var to_s
 
+       # Can a class of kind `self` define a membership predicate?
+       var can_customize_isa: Bool
+
+       # Can a class of kind `self` define a constructor?
+       var can_init: Bool
+
        # Is a constructor required?
        var need_init: Bool
 
@@ -2608,12 +2612,79 @@ class MClassKind
 end
 
 # The class kind `abstract`
-fun abstract_kind: MClassKind do return once new MClassKind("abstract class", true)
+fun abstract_kind: MClassKind do return once new MClassKind("abstract class", false, true, true)
 # The class kind `concrete`
-fun concrete_kind: MClassKind do return once new MClassKind("class", true)
+fun concrete_kind: MClassKind do return once new MClassKind("class", false, true, true)
 # The class kind `interface`
-fun interface_kind: MClassKind do return once new MClassKind("interface", false)
+fun interface_kind: MClassKind do return once new MClassKind("interface", false, true, false)
 # The class kind `enum`
-fun enum_kind: MClassKind do return once new MClassKind("enum", false)
+fun enum_kind: MClassKind do return once new MClassKind("enum", false, true, false)
 # The class kind `extern`
-fun extern_kind: MClassKind do return once new MClassKind("extern class", false)
+fun extern_kind: MClassKind do return once new MClassKind("extern class", false, true, false)
+
+# A standalone pre-constructed model used to test various model-related methods.
+#
+# When instantiated, a standalone model is already filled with entities that are exposed as attributes.
+class ModelStandalone
+       super Model
+
+       redef var location = new Location.opaque_file("ModelStandalone")
+
+       # The first module
+       var mmodule0 = new MModule(self, null, "module0", location)
+
+       # The root Object class
+       var mclass_o = new MClass(mmodule0, "Object", location, null, interface_kind, public_visibility)
+
+       # The introduction of `mclass_o`
+       var mclassdef_o = new MClassDef(mmodule0, mclass_o.mclass_type, location)
+end
+
+# A standalone model with the common class diamond-hierarchy ABCD
+class ModelDiamond
+       super ModelStandalone
+
+       # A, a simple subclass of Object
+       var mclass_a = new MClass(mmodule0, "A", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_a`
+       var mclassdef_a: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_a.mclass_type, location)
+               res.set_supertypes([mclass_o.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # B, a subclass of A (`mclass_a`)
+       var mclass_b = new MClass(mmodule0, "B", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_b`
+       var mclassdef_b: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_b.mclass_type, location)
+               res.set_supertypes([mclass_a.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # C, another subclass of A (`mclass_a`)
+       var mclass_c = new MClass(mmodule0, "C", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_c`
+       var mclassdef_c: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_c.mclass_type, location)
+               res.set_supertypes([mclass_a.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+
+       # D, a multiple subclass of B (`mclass_b`) and C (`mclass_c`)
+       var mclass_d = new MClass(mmodule0, "D", location, null, concrete_kind, public_visibility)
+
+       # The introduction of `mclass_d`
+       var mclassdef_d: MClassDef do
+               var res = new MClassDef(mmodule0, mclass_d.mclass_type, location)
+               res.set_supertypes([mclass_b.mclass_type, mclass_c.mclass_type])
+               res.add_in_hierarchy
+               return res
+       end
+end