X-Git-Url: http://nitlanguage.org diff --git a/src/model/model.nit b/src/model/model.nit index 03d82b14..c86bf96 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -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) @@ -146,6 +159,14 @@ class ConcernsTree super OrderedTree[MConcern] end +redef class MGroup + redef var is_test is lazy do + var parent = self.parent + if parent != null and parent.is_test then return true + return name == "tests" + end +end + redef class MModule # All the classes introduced in the module var intro_mclasses = new Array[MClass] @@ -154,6 +175,14 @@ redef class MModule # (introduction and refinement) var mclassdefs = new Array[MClassDef] + private var mclassdef_sorter: MClassDefSorter is lazy do + return new MClassDefSorter(self) + end + + private var mpropdef_sorter: MPropDefSorter is lazy do + return new MPropDefSorter(self) + end + # Does the current module has a given class `mclass`? # Return true if the mmodule introduces, refines or imports a class. # Visibility is not considered. @@ -169,7 +198,7 @@ redef class MModule # Visibility is not considered. # # Note: this function is expensive and is usually used for the main - # module of a program only. Do not use it to do you own subtype + # module of a program only. Do not use it to do your own subtype # functions. fun flatten_mclass_hierarchy: POSet[MClass] do @@ -201,8 +230,7 @@ redef class MModule # 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) + mclassdef_sorter.sort(mclassdefs) end # Sort a given array of property definitions using the linearization order of the module @@ -210,8 +238,7 @@ redef class MModule # 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) + mpropdef_sorter.sort(mpropdefs) end private var flatten_mclass_hierarchy_cache: nullable POSet[MClass] = null @@ -355,21 +382,19 @@ private class MPropDefSorter super Comparator redef type COMPARED: 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) + return mmodule.mclassdef_sorter.compare(a, b) end end # A named class # -# `MClass` are global to the model; it means that a `MClass` is not bound to a -# specific `MModule`. +# `MClass`es are global to the model; it means that a `MClass` is not bound +# to a specific `MModule`. # # This characteristic helps the reasoning about classes in a program since a # single `MClass` object always denote the same class. @@ -473,11 +498,13 @@ class MClass end # The kind of the class (interface, abstract class, etc.) - # In Nit, the kind of a class cannot evolve in refinements + # + # In Nit, the kind of a class cannot evolve in refinements. var kind: MClassKind # The visibility of the class - # In Nit, the visibility of a class cannot evolve in refinements + # + # In Nit, the visibility of a class cannot evolve in refinements. redef var visibility init @@ -501,12 +528,12 @@ class MClass # Warning: such a definition may not exist in the early life of the object. # In this case, the method will abort. # - # Use `try_intro` instead + # Use `try_intro` instead. var intro: MClassDef is noinit - # The definition that introduces the class or null if not yet known. + # The definition that introduces the class or `null` if not yet known. # - # See `intro` + # SEE: `intro` fun try_intro: nullable MClassDef do if isset _intro then return _intro else return null end @@ -569,7 +596,14 @@ class MClass # Is `self` and abstract class? var is_abstract: Bool is lazy do return kind == abstract_kind - redef fun mdoc_or_fallback do return intro.mdoc_or_fallback + redef var is_test is lazy do return intro.is_test + + redef fun mdoc_or_fallback + do + # Don’t use `intro.mdoc_or_fallback` because it would create an infinite + # recursion. + return intro.mdoc + end end @@ -728,6 +762,8 @@ class MClassDef # All property introductions and redefinitions (not inheritance) in `self` by its associated property. var mpropdefs_by_property = new HashMap[MProperty, MPropDef] + + redef fun mdoc_or_fallback do return mdoc or else mclass.mdoc_or_fallback end # A global static type @@ -859,7 +895,7 @@ abstract class MType if anchor == null then anchor = sub # UGLY: any anchor will work var resolved_sub = sub.anchor_to(mmodule, anchor) var res = resolved_sub.collect_mclasses(mmodule).has(sup.mclass) - if res == false then return false + if not res then return false if not sup isa MGenericType then return true var sub2 = sub.supertype_to(mmodule, anchor, sup.mclass) assert sub2.mclass == sup.mclass @@ -867,7 +903,7 @@ abstract class MType var sub_arg = sub2.arguments[i] var sup_arg = sup.arguments[i] res = sub_arg.is_subtype(mmodule, anchor, sup_arg) - if res == false then return false + if not res then return false end return true end @@ -1301,6 +1337,7 @@ class MClassType private var collect_mclasses_cache = new HashMap[MModule, Set[MClass]] private var collect_mtypes_cache = new HashMap[MModule, Set[MClassType]] + redef fun mdoc_or_fallback do return mclass.mdoc_or_fallback end # A type based on a generic class. @@ -1464,8 +1501,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 @@ -1479,13 +1516,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 @@ -1531,6 +1565,8 @@ class MVirtualType redef fun full_name do return self.mproperty.full_name redef fun c_name do return self.mproperty.c_name + + redef fun mdoc_or_fallback do return mproperty.mdoc_or_fallback end # The type associated to a formal parameter generic type of a class @@ -1604,9 +1640,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. @@ -1625,13 +1659,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) @@ -1775,6 +1803,8 @@ class MNullableType if t == mtype then return self return t.as_nullable end + + redef fun mdoc_or_fallback do return mtype.mdoc_or_fallback end # A non-null version of a formal type. @@ -1957,16 +1987,34 @@ class MSignature var b = new FlatBuffer if not mparameters.is_empty then b.append("(") + var last_mtype = null for i in [0..mparameters.length[ do var mparameter = mparameters[i] + + # Group types that are common to contiguous parameters + if mparameter.mtype != last_mtype and last_mtype != null then + b.append(": ") + b.append(last_mtype.to_s) + end + if i > 0 then b.append(", ") b.append(mparameter.name) - b.append(": ") - b.append(mparameter.mtype.to_s) + if mparameter.is_vararg then + b.append(": ") + b.append(mparameter.mtype.to_s) b.append("...") + last_mtype = null + else + last_mtype = mparameter.mtype end end + + if last_mtype != null then + b.append(": ") + b.append(last_mtype.to_s) + end + b.append(")") end var ret = self.return_mtype @@ -2055,7 +2103,12 @@ abstract class MProperty redef var location - redef fun mdoc_or_fallback do return intro.mdoc_or_fallback + redef fun mdoc_or_fallback + do + # Don’t use `intro.mdoc_or_fallback` because it would create an infinite + # recursion. + return intro.mdoc + end # The canonical name of the property. # @@ -2286,6 +2339,20 @@ abstract class MProperty end private var lookup_all_definitions_cache = new HashMap2[MModule, MType, Array[MPROPDEF]] + + redef var is_test is lazy do return intro.is_test + + # Does self have the `before` annotation? + var is_before: Bool is lazy do return intro.is_before + + # Does self have the `before_all` annotation? + var is_before_all: Bool is lazy do return intro.is_before_all + + # Does self have the `after` annotation? + var is_after: Bool is lazy do return intro.is_after + + # Does self have the `after_all` annotation? + var is_after_all: Bool is lazy do return intro.is_after_all end # A global method @@ -2320,6 +2387,29 @@ class MMethod # 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" + + # Is this method a getter (auto or not)? + # + # See `getter_for`. + fun is_getter: Bool do return getter_for != null + + # The attribute this getter is for + # + # Return `null` is this method is not a getter. + var getter_for: nullable MAttribute = null is writable + + # Is this method a setter (auto or not)? + # + # See `setter_for`. + fun is_setter: Bool do return setter_for != null + + # The attribute this setter is for + # + # Return `null` is this method is not a setter. + var setter_for: nullable MAttribute = null is writable + + # Is this method a getter or a setter? + fun is_accessor: Bool do return is_getter or is_setter end # A global attribute @@ -2328,6 +2418,21 @@ class MAttribute redef type MPROPDEF: MAttributeDef + # Does this attribute have a getter (auto or not)? + # + # See `getter`. + fun has_getter: Bool do return getter != null + + # The getter of this attribute (if any) + var getter: nullable MProperty = null is writable + + # Does this attribute have a setter (auto or not)? + # + # See `setter`. + fun has_setter: Bool do return setter != null + + # The setter of this attribute (if any) + var setter: nullable MProperty = null is writable end # A global virtual type @@ -2363,7 +2468,7 @@ abstract class MPropDef # The associated global property var mproperty: MPROPERTY - redef var location: Location + redef var location redef fun visibility do return mproperty.visibility @@ -2420,11 +2525,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 @@ -2442,10 +2545,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 @@ -2477,6 +2578,20 @@ abstract class MPropDef assert has_next_property: i.is_ok return i.item end + + redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback + + # Does self have the `before` annotation? + var is_before = false is writable + + # Does self have the `before_all` annotation? + var is_before_all = false is writable + + # Does self have the `after` annotation? + var is_after = false is writable + + # Does self have the `after_all` annotation? + var is_after_all = false is writable end # A local definition of a method @@ -2560,37 +2675,116 @@ 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 # TODO: private init because enumeration. - # Can a class of kind `self` specializes a class of kine `other`? + # Can a class of kind `self` specializes a class of kind `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 + if other == interface_kind then + # everybody can specialize interfaces + return true + else if self == interface_kind or self == enum_kind then + # no other case for interfaces and enums return false + else if self == subset_kind then + # A subset may specialize anything, except another subset. + # TODO: Allow sub-subsets once we can handle them. + return other != subset_kind else if self == extern_kind then # only compatible with themselves return self == other - else if other == enum_kind or other == extern_kind then - # abstract_kind and concrete_kind are incompatible - return false + else + # assert self == abstract_kind or self == concrete_kind + return other == abstract_kind or other == concrete_kind end - # remain only abstract_kind and concrete_kind - return true end 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) +# The class kind `subset` +fun subset_kind: MClassKind do return once new MClassKind("subset", true, false, 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