X-Git-Url: http://nitlanguage.org diff --git a/src/model/model.nit b/src/model/model.nit index 9c7a877..11592d7 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,19 +82,21 @@ 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 - if mclasses_by_name.has_key(name) then - return mclasses_by_name[name] - else - return null - end + return mclasses_by_name.get_or_null(name) end # Collections of properties grouped by their short name @@ -92,16 +110,15 @@ redef class Model # Visibility or modules are not considered fun get_mproperties_by_name(name: String): nullable Array[MProperty] do - if not mproperties_by_name.has_key(name) then - return null - else - return mproperties_by_name[name] - end + return mproperties_by_name.get_or_null(name) end # 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] @@ -127,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 @@ -140,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. @@ -148,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 @@ -187,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 @@ -196,38 +230,64 @@ 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 # The primitive type `Object`, the root of the class hierarchy - fun object_type: MClassType - do - var res = self.object_type_cache - if res != null then return res - res = self.get_primitive_class("Object").mclass_type - self.object_type_cache = res - return res - end - - private var object_type_cache: nullable MClassType + var object_type: MClassType = self.get_primitive_class("Object").mclass_type is lazy # The type `Pointer`, super class to all extern classes var pointer_type: MClassType = self.get_primitive_class("Pointer").mclass_type is lazy # The primitive type `Bool` - fun bool_type: MClassType - do - var res = self.bool_type_cache - if res != null then return res - res = self.get_primitive_class("Bool").mclass_type - self.bool_type_cache = res - return res - end + var bool_type: MClassType = self.get_primitive_class("Bool").mclass_type is lazy + + # 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 + + # The primitive type `Float` + var float_type: MClassType = self.get_primitive_class("Float").mclass_type is lazy + + # The primitive type `String` + var string_type: MClassType = self.get_primitive_class("String").mclass_type is lazy - private var bool_type_cache: nullable MClassType + # 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]) + + # The primitive class `Array` + var array_class: MClass = self.get_primitive_class("Array") is lazy + + # A primitive type of `NativeArray` + fun native_array_type(elt_type: MType): MClassType do return native_array_class.get_mtype([elt_type]) + + # The primitive class `NativeArray` + var native_array_class: MClass = self.get_primitive_class("NativeArray") is lazy # The primitive type `Sys`, the main type of the program, if any fun sys_type: nullable MClassType @@ -250,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 @@ -278,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 @@ -313,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. @@ -349,19 +408,27 @@ 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 + # + # It is the name of the class prefixed by the full_name of the `intro_mmodule` # Example: `"owner::module::MyClass"` - fun full_name: String - do - return "{self.intro_mmodule.full_name}::{name}" + redef var full_name is lazy do + return "{self.intro_mmodule.namespace_for(visibility)}::{name}" + end + + redef var c_name is lazy do + return "{intro_mmodule.c_namespace_for(visibility)}__{name.to_cmangle}" end # The number of generic formal parameters @@ -372,6 +439,30 @@ 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 do @@ -399,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 @@ -426,8 +519,17 @@ 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. var intro: MClassDef is noinit + # The definition that introduces the class or `null` if not yet known. + # + # SEE: `intro` + fun try_intro: nullable MClassDef do + if isset _intro then return _intro else return null + end + # Return the class `self` in the class hierarchy of the module `mmodule`. # # SEE: `MModule::flatten_mclass_hierarchy` @@ -439,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. @@ -470,6 +572,28 @@ class MClass end private var get_mtype_cache = new HashMap[Array[MType], MGenericType] + + # 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 fun mdoc_or_fallback + do + # Don’t use `intro.mdoc_or_fallback` because it would create an infinite + # recursion. + return intro.mdoc + end end @@ -506,12 +630,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 @@ -522,12 +647,47 @@ 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 short-name of the class is used for introduction. + # Example: "my_module$MyClass" + # + # The full-name of the class is used for refinement. + # 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.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}" + else + # 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.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}" + end + end + redef fun model do return mmodule.model # All declared super-types @@ -582,13 +742,18 @@ class MClassDef var in_hierarchy: nullable POSetElement[MClassDef] = null # Is the definition the one that introduced `mclass`? - fun is_intro: Bool do return mclass.intro == self + fun is_intro: Bool do return isset mclass._intro and mclass.intro == self # 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 @@ -652,6 +817,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 @@ -659,16 +826,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 @@ -676,12 +847,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 @@ -689,19 +864,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 @@ -977,6 +1180,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 @@ -1027,6 +1231,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 @@ -1035,9 +1241,13 @@ class MClassType redef fun to_s do return mclass.to_s + redef fun full_name do return mclass.full_name + + redef fun c_name do return mclass.c_name + 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 @@ -1117,6 +1327,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. @@ -1143,11 +1354,31 @@ class MGenericType self.to_s = "{mclass}[{arguments.join(", ")}]" end - # Recursively print the type of the arguments within brackets. + # 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: `"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 + args.add t.full_name + end + return "{mclass.full_name}[{args.join(", ")}]" + end + + redef var c_name is lazy do + var res = mclass.c_name + # Note: because the arity is known, a prefix notation is enough + for t in arguments do + res += "__" + res += t.c_name + end + 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 @@ -1169,6 +1400,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 @@ -1190,19 +1439,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 @@ -1228,27 +1491,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 @@ -1258,6 +1519,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 @@ -1287,6 +1551,12 @@ class MVirtualType end redef fun to_s do return self.mproperty.to_s + + 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 @@ -1316,13 +1586,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 @@ -1331,10 +1603,14 @@ class MParameterType redef fun to_s do return name + redef var full_name is lazy do return "{mclass.full_name}::{name}" + + redef var c_name is lazy do return mclass.c_name + "__" + "#{name}".to_cmangle + 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 @@ -1349,20 +1625,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 @@ -1374,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) @@ -1396,6 +1665,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 @@ -1434,29 +1704,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 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) @@ -1464,12 +1731,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 @@ -1495,14 +1764,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 @@ -1514,6 +1845,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 @@ -1521,6 +1903,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 @@ -1554,19 +1945,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 @@ -1614,7 +2015,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 @@ -1631,6 +2032,8 @@ class MParameter end end + # Returns a new parameter with the `mtype` resolved. + # See `MType::resolve_for` for details. fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MParameter do if not self.mtype.need_anchor then return self @@ -1666,17 +2069,46 @@ abstract class MProperty var intro_mclassdef: MClassDef # The (short) name of the property - redef var name: String + redef var name - # The canonical name of the property - # Example: "owner::my_module::MyClass::my_method" - fun full_name: String + redef var location + + redef fun mdoc_or_fallback do - return "{self.intro_mclassdef.mmodule.full_name}::{self.intro_mclassdef.mclass.name}::{name}" + # 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 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 + 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 + # FIXME use `namespace_for` + return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.name.to_cmangle}__{name.to_cmangle}" 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 init do @@ -1708,10 +2140,13 @@ abstract class MProperty # If mtype does not know mproperty then an empty array is returned. # # If you want the really most specific property, then look at `lookup_first_definition` + # + # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter) + # ENSURE: `not mtype.has_mproperty(mmodule, self) == result.is_empty` 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 @@ -1719,14 +2154,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 @@ -1746,11 +2194,12 @@ abstract class MProperty # # If you want the really most specific property, then look at `lookup_next_definition` # - # FIXME: Move to `MPropDef`? + # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter) + # ENSURE: `not mtype.has_mproperty(mmodule, self) implies result.is_empty` 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] @@ -1801,7 +2250,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 @@ -1814,24 +2263,28 @@ abstract class MProperty # # FIXME: the linearization is still unspecified # - # REQUIRE: `not mtype.need_anchor` + # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter) # 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 # Return all definitions in a linearization order # Most specific first, most general last + # + # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter) + # REQUIRE: `mtype.has_mproperty(mmodule, self)` fun lookup_all_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF] do - assert not mtype.need_anchor - mtype = mtype.as_notnullable + mtype = mtype.undecorate var cache = self.lookup_all_definitions_cache[mmodule, mtype] if cache != null then return cache + assert not mtype.need_anchor + assert mtype.has_mproperty(mmodule, self) + #print "select prop {mproperty} for {mtype} in {self}" # First, select all candidates var candidates = new Array[MPROPDEF] @@ -1886,6 +2339,10 @@ 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" end # A global attribute @@ -1904,6 +2361,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) @@ -1926,31 +2386,98 @@ abstract class MPropDef # The associated global property var mproperty: MPROPERTY - # The origin of the definition - var location: Location + redef var location: 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` redef fun name do return mproperty.name + # The full-name of mpropdefs combine the information about the `classdef` and the `mproperty`. + # + # Therefore the combination of identifiers is awful, + # the worst case being + # + # * 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" + # + # Fortunately, the full-name is simplified when entities are repeated. + # 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" + res.append mclassdef.full_name + + 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.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then + # precise "p::m" only if "p" != "r" + res.append mproperty.intro_mclassdef.mmodule.namespace_for(mproperty.visibility) + res.append "::" + else if mproperty.visibility <= private_visibility then + # 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.mpackage then + res.append "::" + res.append mproperty.intro_mclassdef.mmodule.name + res.append "::" + end + 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 + return res.to_s + end + + redef var c_name is lazy do + var res = new FlatBuffer + res.append mclassdef.c_name + res.append "___" + if mclassdef.mclass == mproperty.intro_mclassdef.mclass then + res.append name.to_cmangle + else + if mclassdef.mmodule != mproperty.intro_mclassdef.mmodule then + res.append mproperty.intro_mclassdef.mmodule.c_name + res.append "__" + end + res.append mproperty.intro_mclassdef.name.to_cmangle + res.append "__" + res.append mproperty.name.to_cmangle + end + return res.to_s + end + 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`. # @@ -1969,6 +2496,8 @@ 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 end # A local definition of a method @@ -2050,14 +2579,20 @@ 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 @@ -2077,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