X-Git-Url: http://nitlanguage.org diff --git a/src/model/model.nit b/src/model/model.nit index 754f57a..dc9b978 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -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] @@ -41,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. @@ -66,12 +82,18 @@ redef class Model # Collections of classes grouped by their short name private var mclasses_by_name = new MultiHashMap[String, MClass] - # Return all class named `name`. + # Return all classes named `name`. # # If such a class does not exist, null is returned # (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) @@ -94,6 +116,9 @@ redef class Model # The only null type var null_type = new MNullType(self) + # The only bottom type + var bottom_type: MBottomType = null_type.as_notnull + # Build an ordered tree with from `concerns` fun concerns_tree(mconcerns: Collection[MConcern]): ConcernsTree do var seen = new HashSet[MConcern] @@ -119,7 +144,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 @@ -132,6 +167,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. @@ -140,14 +183,14 @@ redef class MModule return self.in_importation <= mclass.intro_mmodule end - # Full hierarchy of introduced ans imported classes. + # Full hierarchy of introduced and imported classes. # # Create a new hierarchy got by flattening the classes for the module # and its imported modules. # 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 @@ -179,8 +222,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 @@ -188,8 +230,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 @@ -206,6 +247,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 @@ -215,8 +274,8 @@ redef class MModule # The primitive type `String` var string_type: MClassType = self.get_primitive_class("String").mclass_type is lazy - # The primitive type `NativeString` - var native_string_type: MClassType = self.get_primitive_class("NativeString").mclass_type is lazy + # The primitive type `CString` + var c_string_type: MClassType = self.get_primitive_class("CString").mclass_type is lazy # A primitive type of `Array` fun array_type(elt_type: MType): MClassType do return array_class.get_mtype([elt_type]) @@ -251,23 +310,27 @@ redef class MModule fun get_primitive_class(name: String): MClass do var cla = self.model.get_mclasses_by_name(name) - if cla == null then + # Filter classes by introducing module + if cla != null then cla = [for c in cla do if self.in_importation <= c.intro_mmodule then c] + if cla == null or cla.is_empty then 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}") + print_error("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}:" + var msg = "Fatal Error: more than one primitive class {name} in {self}:" for c in cla do msg += " {c.full_name}" - print msg + print_error msg #exit(1) end return cla.first @@ -279,18 +342,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_error("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}") + abort end end return res @@ -314,21 +374,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. @@ -350,13 +408,16 @@ 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 + # the introducing module is used for naming and visibility. var intro_mmodule: MModule # 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 # @@ -378,6 +439,29 @@ class MClass # is empty if the class is not generic var mparameters = new Array[MParameterType] + # A string version of the signature a generic class. + # + # eg. `Map[K: nullable Object, V: nullable Object]` + # + # If the class in non generic the name is just given. + # + # eg. `Object` + fun signature_to_s: String + do + if arity == 0 then return name + var res = new FlatBuffer + res.append name + res.append "[" + for i in [0..arity[ do + if i > 0 then res.append ", " + res.append mparameters[i].name + res.append ": " + res.append intro.bound_mtype.arguments[i].to_s + end + res.append "]" + return res.to_s + end + # Initialize `mparameters` from their names. protected fun setup_parameter_names(parameter_names: nullable Array[String]) is autoinit @@ -406,12 +490,14 @@ 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 - var visibility: MVisibility + # + # In Nit, the visibility of a class cannot evolve in refinements. + redef var visibility init do @@ -434,12 +520,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 @@ -455,12 +541,12 @@ class MClass # The principal static type of the class. # - # For non-generic class, mclass_type is the only `MClassType` based + # For non-generic class, `mclass_type` is the only `MClassType` based # on self. # # For a generic class, the arguments are the formal parameters. - # i.e.: for the class Array[E:Object], the `mclass_type` is Array[E]. - # If you want Array[Object] the see `MClassDef::bound_mtype` + # i.e.: for the class `Array[E:Object]`, the `mclass_type` is `Array[E]`. + # If you want `Array[Object]`, see `MClassDef::bound_mtype`. # # For generic classes, the mclass_type is also the way to get a formal # generic parameter type. @@ -489,6 +575,27 @@ 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 + + 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 @@ -525,12 +632,13 @@ class MClassDef # ENSURE: `bound_mtype.mclass == self.mclass` var bound_mtype: MClassType - # The origin of the definition - var location: Location + redef var 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 @@ -541,41 +649,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}" @@ -641,8 +749,13 @@ class MClassDef # All properties introduced by the classdef var intro_mproperties = new Array[MProperty] - # All property definitions in the class (introductions and redefinitions) + # All property introductions and redefinitions in `self` (not inheritance). var mpropdefs = new Array[MPropDef] + + # 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 @@ -706,6 +819,8 @@ abstract class MType if sup isa MNullableType then sup_accept_null = true sup = sup.mtype + else if sup isa MNotNullType then + sup = sup.mtype else if sup isa MNullType then sup_accept_null = true end @@ -713,16 +828,20 @@ abstract class MType # Can `sub` provide null or not? # Thus we can match with `sup_accept_null` # Also discard the nullable marker if it exists + var sub_reject_null = false if sub isa MNullableType then if not sup_accept_null then return false sub = sub.mtype + else if sub isa MNotNullType then + sub_reject_null = true + sub = sub.mtype else if sub isa MNullType then return sup_accept_null end # Now the case of direct null and nullable is over. # If `sub` is a formal type, then it is accepted if its bound is accepted - while sub isa MParameterType or sub isa MVirtualType do + while sub isa MFormalType do #print "3.is {sub} a {sup}?" # A unfixed formal type can only accept itself @@ -730,12 +849,16 @@ abstract class MType assert anchor != null sub = sub.lookup_bound(mmodule, anchor) + if sub_reject_null then sub = sub.as_notnull #print "3.is {sub} a {sup}?" # Manage the second layer of null/nullable if sub isa MNullableType then - if not sup_accept_null then return false + if not sup_accept_null and not sub_reject_null then return false + sub = sub.mtype + else if sub isa MNotNullType then + sub_reject_null = true sub = sub.mtype else if sub isa MNullType then return sup_accept_null @@ -743,19 +866,19 @@ abstract class MType end #print "4.is {sub} a {sup}? <- no more resolution" - assert sub isa MClassType # It is the only remaining type - - # A unfixed formal type can only accept itself - if sup isa MParameterType or sup isa MVirtualType then - return false + if sub isa MBottomType or sub isa MErrorType then + return true end - if sup isa MNullType then - # `sup` accepts only null + assert sub isa MClassType else print_error "{sub} 3 # # Formal types have a depth of 1. + # Only `MClassType` and `MFormalType` nodes are counted. fun depth: Int do return 1 @@ -1031,6 +1182,7 @@ abstract class MType # * H[G[A], B] -> 4 # # Formal types have a length of 1. + # Only `MClassType` and `MFormalType` nodes are counted. fun length: Int do return 1 @@ -1081,6 +1233,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 @@ -1095,7 +1249,7 @@ class MClassType redef fun need_anchor do return false - redef fun anchor_to(mmodule: MModule, anchor: MClassType): MClassType + redef fun anchor_to(mmodule, anchor): MClassType do return super.as(MClassType) end @@ -1175,6 +1329,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. @@ -1203,10 +1358,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 @@ -1225,7 +1380,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 @@ -1247,6 +1402,24 @@ class MGenericType return true end + redef fun is_ok + do + for t in arguments do if not t.is_ok then return false + return super + end + + redef fun is_legal_in(mmodule, anchor) + do + var mtype + if need_anchor then + assert anchor != null + mtype = anchor_to(mmodule, anchor) + else + mtype = self + end + if not mtype.is_ok then return false + return mtype.is_subtype(mmodule, null, mtype.mclass.intro.bound_mtype) + end redef fun depth do @@ -1268,19 +1441,33 @@ class MGenericType end end +# A formal type (either virtual of parametric). +# +# The main issue with formal types is that they offer very little information on their own +# and need a context (anchor and mmodule) to be useful. +abstract class MFormalType + super MType + + redef var as_notnull = new MNotNullType(self) is lazy +end + # A virtual formal type. class MVirtualType - super MType + super MFormalType # The property associated with the type. # 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 + redef fun lookup_bound(mmodule, resolved_receiver) do - return lookup_single_definition(mmodule, resolved_receiver).bound.as(not null) + # There is two possible invalid cases: the vt does not exists in resolved_receiver or the bound is broken + if not resolved_receiver.has_mproperty(mmodule, mproperty) then return new MErrorType(model) + return lookup_single_definition(mmodule, resolved_receiver).bound or else new MErrorType(model) end private fun lookup_single_definition(mmodule: MModule, resolved_receiver: MType): MVirtualTypeDef @@ -1306,27 +1493,25 @@ 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 - resolved_receiver = resolved_receiver.as_notnullable + resolved_receiver = resolved_receiver.undecorate 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 MErrorType(model) # 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 @@ -1336,6 +1521,9 @@ class MVirtualType do if not cleanup_virtual then return self assert can_resolve_for(mtype, anchor, mmodule) + + if mproperty.is_selftype then return mtype + # 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 @@ -1369,6 +1557,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 @@ -1398,13 +1588,15 @@ end # Note that parameter types are shared among class refinements. # Therefore parameter only have an internal name (see `to_s` for details). class MParameterType - super MType + super MFormalType # The generic class where the parameter belong var mclass: MClass 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 @@ -1420,7 +1612,7 @@ class MParameterType redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType do assert not resolved_receiver.need_anchor - resolved_receiver = resolved_receiver.as_notnullable + resolved_receiver = resolved_receiver.undecorate assert resolved_receiver isa MClassType # It is the only remaining type var goalclass = self.mclass if resolved_receiver.mclass == goalclass then @@ -1435,20 +1627,19 @@ class MParameterType return res end end - abort + # Cannot found `self` in `resolved_receiver` + return new MErrorType(model) 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. redef fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do assert not resolved_receiver.need_anchor - resolved_receiver = resolved_receiver.as_notnullable + resolved_receiver = resolved_receiver.undecorate assert resolved_receiver isa MClassType # It is the only remaining type var res = self.resolve_for(resolved_receiver.mclass.mclass_type, resolved_receiver, mmodule, false) return res @@ -1460,13 +1651,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) @@ -1482,6 +1667,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 @@ -1520,33 +1706,26 @@ class MParameterType end end -# A type prefixed with "nullable" -class MNullableType +# A type that decorates another type. +# +# The point of this class is to provide a common implementation of sevices that just forward to the original type. +# Specific decorator are expected to redefine (or to extend) the default implementation as this suit them. +abstract class MProxyType super MType - - # The base type of the nullable type + # The base type var mtype: MType - redef fun model do return self.mtype.model - - init - do - self.to_s = "nullable {mtype}" - end - - redef var to_s: String is noinit - - redef var full_name is lazy do return "nullable {mtype.full_name}" - - redef var c_name is lazy do return "nullable__{mtype.c_name}" + 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 self - redef fun as_notnullable do return mtype + redef fun as_nullable do return mtype.as_nullable + redef fun as_notnull do return mtype.as_notnull + redef fun undecorate do return mtype.undecorate redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do var res = self.mtype.resolve_for(mtype, anchor, mmodule, cleanup_virtual) - return res.as_nullable + return res end redef fun can_resolve_for(mtype, anchor, mmodule) @@ -1554,12 +1733,14 @@ class MNullableType return self.mtype.can_resolve_for(mtype, anchor, mmodule) end - # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_nullable` + redef fun is_ok do return mtype.is_ok + + redef fun is_legal_in(mmodule, anchor) do return mtype.is_legal_in(mmodule, anchor) + redef fun lookup_fixed(mmodule, resolved_receiver) do var t = mtype.lookup_fixed(mmodule, resolved_receiver) - if t == mtype then return self - return t.as_nullable + return t end redef fun depth do return self.mtype.depth @@ -1585,16 +1766,76 @@ class MNullableType end end +# A type prefixed with "nullable" +class MNullableType + super MProxyType + + init + do + self.to_s = "nullable {mtype}" + end + + redef var to_s is noinit + + redef var full_name is lazy do return "nullable {mtype.full_name}" + + redef var c_name is lazy do return "nullable__{mtype.c_name}" + + redef fun as_nullable do return self + redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) + do + var res = super + return res.as_nullable + end + + # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_nullable` + redef fun lookup_fixed(mmodule, resolved_receiver) + do + var t = super + if t == mtype then return self + return t.as_nullable + end +end + +# A non-null version of a formal type. +# +# When a formal type in bounded to a nullable type, this is the type of the not null version of it. +class MNotNullType + super MProxyType + + redef fun to_s do return "not null {mtype}" + redef var full_name is lazy do return "not null {mtype.full_name}" + redef var c_name is lazy do return "notnull__{mtype.c_name}" + + redef fun as_notnull do return self + + redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) + do + var res = super + return res.as_notnull + end + + # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_notnull` + redef fun lookup_fixed(mmodule, resolved_receiver) + do + var t = super + if t == mtype then return self + return t.as_notnull + end +end + # The type of the only value null # # 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 + + redef var as_notnull: MBottomType = 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 @@ -1606,6 +1847,57 @@ class MNullType 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 dead (no instance). +# +# Semantically it is the singleton `null.as_notnull`. +# Is also means that `self.as_nullable == null`. +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 + + 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 + +# A special type used as a silent error marker when building types. +# +# This type is intended to be only used internally for type operation and should not be exposed to the user. +# The error type can de used to denote things that are conflicting or inconsistent. +# +# Some methods on types can return a `MErrorType` to denote a broken or a conflicting result. +# Use `is_ok` to check if a type is (or contains) a `MErrorType` . +class MErrorType + super MType + redef var model + redef fun to_s do return "error" + redef fun full_name do return "error" + redef fun c_name do return "error" + 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 is_ok do return false + + 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 + # A signature of a method class MSignature super MType @@ -1613,6 +1905,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 @@ -1646,19 +1947,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 @@ -1666,16 +1977,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 @@ -1706,7 +2035,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 @@ -1760,14 +2089,34 @@ abstract class MProperty var intro_mclassdef: MClassDef # The (short) name of the property - redef var name: String + redef var name + + redef var location + + 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. # - # 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 @@ -1776,7 +2125,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 @@ -1817,7 +2166,7 @@ abstract class MProperty fun lookup_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF] do assert not mtype.need_anchor - mtype = mtype.as_notnullable + mtype = mtype.undecorate var cache = self.lookup_definitions_cache[mmodule, mtype] if cache != null then return cache @@ -1825,14 +2174,27 @@ abstract class MProperty #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) + + # Here we have two strategies: iterate propdefs or iterate classdefs. + var mpropdefs = self.mpropdefs + if mpropdefs.length <= 1 or mpropdefs.length < mtype.collect_mclassdefs(mmodule).length then + # Iterate on all definitions of `self`, keep only those inherited by `mtype` in `mmodule` + for mpropdef in 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 + else + # Iterate on all super-classdefs of `mtype`, keep only the definitions of `self`, if any. + for mclassdef in mtype.collect_mclassdefs(mmodule) do + var p = mclassdef.mpropdefs_by_property.get_or_null(self) + if p != null then candidates.add p + end end + # Fast track for only one candidate if candidates.length <= 1 then self.lookup_definitions_cache[mmodule, mtype] = candidates @@ -1857,7 +2219,7 @@ abstract class MProperty fun lookup_super_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF] do assert not mtype.need_anchor - mtype = mtype.as_notnullable + mtype = mtype.undecorate # First, select all candidates var candidates = new Array[MPROPDEF] @@ -1908,7 +2270,7 @@ abstract class MProperty end end if res.is_empty then - print "All lost! {candidates.join(", ")}" + print_error "All lost! {candidates.join(", ")}" # FIXME: should be abort! end return res @@ -1935,7 +2297,7 @@ abstract class MProperty # REQUIRE: `mtype.has_mproperty(mmodule, self)` fun lookup_all_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF] do - mtype = mtype.as_notnullable + mtype = mtype.undecorate var cache = self.lookup_all_definitions_cache[mmodule, mtype] if cache != null then return cache @@ -1967,6 +2329,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 @@ -1997,6 +2373,33 @@ 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" + + # 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 @@ -2005,6 +2408,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 @@ -2015,6 +2433,9 @@ class MVirtualTypeProp # The formal type associated to the virtual type property var mvirtualtype = new MVirtualType(self) + + # Is `self` the special virtual type `SELF`? + var is_selftype: Bool is lazy do return name == "SELF" end # A definition of a property (local property) @@ -2037,18 +2458,20 @@ abstract class MPropDef # The associated global property var mproperty: MPROPERTY - # The origin of the definition - var location: Location + redef var location + + redef fun visibility do return mproperty.visibility init do mclassdef.mpropdefs.add(self) mproperty.mpropdefs.add(self) + mclassdef.mpropdefs_by_property[mproperty] = self if mproperty.intro_mclassdef == mclassdef then 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` @@ -2062,41 +2485,39 @@ 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 "::" 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 @@ -2114,10 +2535,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 @@ -2126,11 +2545,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`. # @@ -2149,6 +2568,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 @@ -2230,39 +2663,118 @@ 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 + + # 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