X-Git-Url: http://nitlanguage.org diff --git a/src/model/model.nit b/src/model/model.nit index cd4988a..62c3db4 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -74,11 +74,7 @@ redef class Model # Visibility or modules are not considered 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,11 +88,7 @@ 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 @@ -203,31 +195,40 @@ redef class MModule 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 - private var bool_type_cache: nullable MClassType + # 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 + + # The primitive type `NativeString` + var native_string_type: MClassType = self.get_primitive_class("NativeString").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,20 +251,26 @@ redef class MModule fun get_primitive_class(name: String): MClass do var cla = self.model.get_mclasses_by_name(name) - if cla == null then - if name == "Bool" 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)) + cladef.set_supertypes([object_type]) + cladef.add_in_hierarchy return c end - print("Fatal Error: no primitive class {name}") + print("Fatal Error: no primitive class {name} in {self}") exit(1) 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 - exit(1) + #exit(1) end return cla.first end @@ -354,10 +361,15 @@ class MClass redef var name: String # 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 @@ -368,6 +380,7 @@ class MClass # is empty if the class is not generic var mparameters = new Array[MParameterType] + # Initialize `mparameters` from their names. protected fun setup_parameter_names(parameter_names: nullable Array[String]) is autoinit do @@ -388,7 +401,7 @@ class MClass self.mparameters = mparametertypes var mclass_type = new MGenericType(self, mparametertypes) self.mclass_type = mclass_type - self.get_mtype_cache.add(mclass_type) + self.get_mtype_cache[mparametertypes] = mclass_type else self.mclass_type = new MClassType(self) end @@ -422,8 +435,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` @@ -458,17 +480,17 @@ class MClass do assert mtype_arguments.length == self.arity if self.arity == 0 then return self.mclass_type - for t in self.get_mtype_cache do - if t.arguments == mtype_arguments then - return t - end - end - var res = new MGenericType(self, mtype_arguments) - self.get_mtype_cache.add res + var res = get_mtype_cache.get_or_null(mtype_arguments) + if res != null then return res + res = new MGenericType(self, mtype_arguments) + self.get_mtype_cache[mtype_arguments.to_a] = res return res end - private var get_mtype_cache = new Array[MGenericType] + 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 end @@ -527,6 +549,41 @@ class MClassDef # 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.mproject != mmodule.mproject 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.mproject == mmodule.mproject 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 @@ -581,7 +638,7 @@ 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] @@ -631,24 +688,18 @@ abstract class MType do var sub = self if sub == sup then return true + + #print "1.is {sub} a {sup}? ====" + if anchor == null then assert not sub.need_anchor assert not sup.need_anchor else + # First, resolve the formal types to the simplest equivalent forms in the receiver assert sub.can_resolve_for(anchor, null, mmodule) + sub = sub.lookup_fixed(mmodule, anchor) assert sup.can_resolve_for(anchor, null, mmodule) - end - - # First, resolve the formal types to a common version in the receiver - # The trick here is that fixed formal type will be associated to the bound - # And unfixed formal types will be associated to a canonical formal type. - if sub isa MParameterType or sub isa MVirtualType then - assert anchor != null - sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, false) - end - if sup isa MParameterType or sup isa MVirtualType then - assert anchor != null - sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, false) + sup = sup.lookup_fixed(mmodule, anchor) end # Does `sup` accept null or not? @@ -657,6 +708,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 @@ -664,34 +717,50 @@ 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. - # A unfixed formal type can only accept itself - if sup isa MParameterType or sup isa MVirtualType then - return sub == sup - end - # If `sub` is a formal type, then it is accepted if its bound is accepted - if sub isa MParameterType or sub isa MVirtualType then + while sub isa MFormalType do + #print "3.is {sub} a {sup}?" + + # A unfixed formal type can only accept itself + if sub == sup then return true + assert anchor != null - sub = sub.anchor_to(mmodule, anchor) + 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 end end + #print "4.is {sub} a {sup}? <- no more resolution" + + assert sub isa MClassType else print "{sub} Map[B,Y] # # Explanation of the example: @@ -771,9 +842,13 @@ abstract class MType # In Nit, for each super-class of a type, there is a equivalent super-type. # # Example: + # + # ~~~nitish # class G[T, U] end # class H[V] super G[V, Bool] end + # # H[Int] supertype_to G #-> G[Int, Bool] + # ~~~ # # REQUIRE: `super_mclass` is a super-class of `self` # REQUIRE: `self.need_anchor implies anchor != null and self.can_resolve_for(anchor, null, mmodule)` @@ -807,9 +882,11 @@ abstract class MType # # ## Example 1 # - # class G[E] end - # class H[F] super G[F] end - # class X[Z] end + # ~~~ + # class G[E] end + # class H[F] super G[F] end + # class X[Z] end + # ~~~ # # * Array[E].resolve_for(H[Int]) #-> Array[Int] # * Array[E].resolve_for(G[Z], X[Int]) #-> Array[Z] @@ -827,30 +904,34 @@ abstract class MType # # ## Example 2 # - # class A[E] - # fun foo(e:E):E is abstract - # end - # class B super A[Int] end + # ~~~ + # class A[E] + # fun foo(e:E):E is abstract + # end + # class B super A[Int] end + # ~~~ # # The signature on foo is (e: E): E # If we resolve the signature for B, we get (e:Int):Int # # ## Example 3 # - # class A[E] - # fun foo(e:E) is abstract - # end - # class B[F] - # var a: A[Array[F]] - # fun bar do a.foo(x) # <- x is here - # end + # ~~~nitish + # class A[E] + # fun foo(e:E):E is abstract + # end + # class C[F] + # var a: A[Array[F]] + # fun bar do a.foo(x) # <- x is here + # end + # ~~~ # # The first question is: is foo available on `a`? # # The static type of a is `A[Array[F]]`, that is an open type. # in order to find a method `foo`, whe must look at a resolved type. # - # A[Array[F]].anchor_to(B[nullable Object]) #-> A[Array[nullable Object]] + # A[Array[F]].anchor_to(C[nullable Object]) #-> A[Array[nullable Object]] # # the method `foo` exists in `A[Array[nullable Object]]`, therefore `foo` exists for `a`. # @@ -858,7 +939,7 @@ abstract class MType # # the signature of `foo` is `foo(e:E)`, thus we must resolve the type E # - # E.resolve_for(A[Array[F]],B[nullable Object]) #-> Array[F] + # E.resolve_for(A[Array[F]],C[nullable Object]) #-> Array[F] # # The resolution can be done because `E` make sense for the class A (see `can_resolve_for`) # @@ -877,6 +958,20 @@ abstract class MType # # In case of conflict, the method aborts. fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType do return self + + # Resolve the formal type to its simplest equivalent form. + # + # Formal types are either free or fixed. + # When it is fixed, it means that it is equivalent with a simpler type. + # When a formal type is free, it means that it is only equivalent with itself. + # This method return the most simple equivalent type of `self`. + # + # This method is mainly used for subtype test in order to sanely compare fixed. + # + # By default, return self. + # See the redefinitions for specific behavior in each kind of type. + fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do return self + # Can the type be resolved? # # In order to resolve open types, the formal types must make sence. @@ -888,11 +983,15 @@ abstract class MType # class B[F] # end # - # * E.can_resolve_for(A[Int]) #-> true, E make sense in A - # * E.can_resolve_for(B[Int]) #-> false, E does not make sense in B - # * B[E].can_resolve_for(A[F], B[Object]) #-> true, - # B[E] is a red hearing only the E is important, - # E make sense in A + # ~~~nitish + # E.can_resolve_for(A[Int]) #-> true, E make sense in A + # + # E.can_resolve_for(B[Int]) #-> false, E does not make sense in B + # + # B[E].can_resolve_for(A[F], B[Object]) #-> true, + # # B[E] is a red hearing only the E is important, + # # E make sense in A + # ~~~ # # REQUIRE: `anchor != null implies not anchor.need_anchor` # REQUIRE: `mtype.need_anchor implies anchor != null and mtype.can_resolve_for(anchor, null, mmodule)` @@ -910,16 +1009,25 @@ abstract class MType return res end - # Return the not nullable version of the type - # Is the type is already not nullable, then self is returned. + # Remove the base type of a decorated (proxy) type. + # Is the type is not decorated, then self is returned. # - # Note: this just remove the `nullable` notation, but the result can still contains null. + # Most of the time it is used to return the not nullable version of a nullable type. + # In this case, this just remove the `nullable` notation, but the result can still contains null. # For instance if `self isa MNullType` or self is a formal type bounded by a nullable type. - fun as_notnullable: MType + # If you really want to exclude the `null` value, then use `as_notnull` + fun undecorate: MType do return self end + # Returns the not null version of the type. + # That is `self` minus the `null` value. + # + # For most types, this return `self`. + # For formal types, this returns a special `MNotNullType` + fun as_notnull: MType do return self + private var as_nullable_cache: nullable MType = null @@ -1002,6 +1110,10 @@ 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 @@ -1025,14 +1137,21 @@ class MClassType redef fun collect_mclasses(mmodule) do + if collect_mclasses_last_module == mmodule then return collect_mclasses_last_module_cache assert not self.need_anchor var cache = self.collect_mclasses_cache if not cache.has_key(mmodule) then self.collect_things(mmodule) end - return cache[mmodule] + var res = cache[mmodule] + collect_mclasses_last_module = mmodule + collect_mclasses_last_module_cache = res + return res end + private var collect_mclasses_last_module: nullable MModule = null + private var collect_mclasses_last_module_cache: Set[MClass] is noinit + redef fun collect_mtypes(mmodule) do assert not self.need_anchor @@ -1103,10 +1222,30 @@ 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 + # 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]]"` + 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 fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) @@ -1150,9 +1289,19 @@ 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. @@ -1178,6 +1327,7 @@ class MVirtualType var res = props.first for p in props do types.add(p.bound.as(not null)) + if not res.is_fixed then res = p end if types.length == 1 then return res @@ -1185,57 +1335,55 @@ class MVirtualType abort end - # Is the virtual type fixed for a given resolved_receiver? - fun is_fixed(mmodule: MModule, resolved_receiver: MType): Bool + # 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 + redef fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do assert not resolved_receiver.need_anchor - var props = self.mproperty.lookup_definitions(mmodule, resolved_receiver) - if props.is_empty then - abort - end - for p in props do - if p.as(MVirtualTypeDef).is_fixed then return true - end - return false + 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) + + # Recursively lookup the fixed result + res = res.lookup_fixed(mmodule, resolved_receiver) + + # 1. 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 + if resolved_receiver.mclass.kind == enum_kind then return res + + return self end redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do + if not cleanup_virtual then return self assert can_resolve_for(mtype, anchor, mmodule) # 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 #print "{class_name}: {self}/{mtype}/{anchor}?" - var resolved_reciever + var resolved_receiver if mtype.need_anchor then assert anchor != null - resolved_reciever = mtype.resolve_for(anchor, null, mmodule, true) + resolved_receiver = mtype.resolve_for(anchor, null, mmodule, true) else - resolved_reciever = mtype + resolved_receiver = mtype end # Now, we can get the bound - var verbatim_bound = lookup_bound(mmodule, resolved_reciever) + var verbatim_bound = lookup_bound(mmodule, resolved_receiver) # The bound is exactly as declared in the "type" property, so we must resolve it again var res = verbatim_bound.resolve_for(mtype, anchor, mmodule, cleanup_virtual) - #print "{class_name}: {self}/{mtype}/{anchor} -> {self}/{resolved_receiver}/{anchor} -> {verbatim_bound}/{mtype}/{anchor} -> {res}" - - # What to return here? There is a bunch a special cases: - # If 'cleanup_virtual' we must return the resolved type, since we cannot return self - if cleanup_virtual then return res - # If the receiver is a intern class, then the virtual type cannot be redefined since there is no possible subclass. self is just fixed. so simply return the resolution - if resolved_reciever isa MNullableType then resolved_reciever = resolved_reciever.mtype - if resolved_reciever.as(MClassType).mclass.kind == enum_kind then return res - # If the resolved type isa MVirtualType, it means that self was bound to it, and cannot be unbound. self is just fixed. so return the resolution. - if res isa MVirtualType then return res - # If we are final, just return the resolution - if is_fixed(mmodule, resolved_reciever) then return res - # If the resolved type isa intern class, then there is no possible valid redefinition in any potential subclass. self is just fixed. so simply return the resolution - if res isa MClassType and res.mclass.kind == enum_kind then return res - # TODO: What if bound to a MParameterType? - # Note that Nullable types can always be redefined by the non nullable version, so there is no specific case on it. - # If anything apply, then `self' cannot be resolved, so return self - return self + return res end redef fun can_resolve_for(mtype, anchor, mmodule) @@ -1248,6 +1396,10 @@ 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 end # The type associated to a formal parameter generic type of a class @@ -1262,12 +1414,14 @@ end # directly to the parameter types of the super-classes. # # Example: +# # class A[E] # fun e: E is abstract # end # class B[F] # super A[Array[F]] # end +# # In the class definition B[F], `F` is a valid type but `E` is not. # However, `self.e` is a valid method call, and the signature of `e` is # declared `e: E`. @@ -1275,7 +1429,7 @@ 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 @@ -1290,10 +1444,19 @@ 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.undecorate + assert resolved_receiver isa MClassType # It is the only remaining type var goalclass = self.mclass + if resolved_receiver.mclass == goalclass then + return resolved_receiver.arguments[self.rank] + end var supertypes = resolved_receiver.collect_mtypes(mmodule) for t in supertypes do if t.mclass == goalclass then @@ -1306,6 +1469,22 @@ class MParameterType abort 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`, + # 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.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 + end + redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do assert can_resolve_for(mtype, anchor, mmodule) @@ -1338,7 +1517,7 @@ class MParameterType resolved_receiver = anchor.arguments[resolved_receiver.rank] if resolved_receiver isa MNullableType then resolved_receiver = resolved_receiver.mtype end - assert resolved_receiver isa MClassType + assert resolved_receiver isa MClassType # It is the only remaining type # Eh! The parameter is in the current class. # So we return the corresponding argument, no mater what! @@ -1372,29 +1551,24 @@ 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 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) @@ -1402,6 +1576,12 @@ class MNullableType return self.mtype.can_resolve_for(mtype, anchor, mmodule) end + redef fun lookup_fixed(mmodule, resolved_receiver) + do + var t = mtype.lookup_fixed(mmodule, resolved_receiver) + return t + end + redef fun depth do return self.mtype.depth redef fun length do return self.mtype.length @@ -1425,6 +1605,64 @@ 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: 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 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`. @@ -1432,7 +1670,12 @@ class MNullType super MType redef var model: 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 + + # Aborts on `null` + redef fun as_notnull do abort # sorry... 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 @@ -1561,6 +1804,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 @@ -1598,16 +1843,25 @@ abstract class MProperty # The (short) name of the property redef var name: String - # The canonical name of the property - # Example: "owner::my_module::MyClass::my_method" - fun full_name: String - do - return "{self.intro_mclassdef.mmodule.full_name}::{self.intro_mclassdef.mclass.name}::{name}" + # 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" + redef var full_name is lazy do + return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}" + 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 + # Is the property usable as an initializer? + var is_autoinit = false is writable + init do intro_mclassdef.intro_mproperties.add(self) @@ -1638,10 +1892,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 @@ -1676,11 +1933,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] @@ -1744,24 +2002,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] @@ -1873,6 +2135,75 @@ abstract class MPropDef # 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.mproject != mproperty.intro_mclassdef.mmodule.mproject then + # precise "p::m" only if "p" != "r" + res.append mproperty.intro_mclassdef.mmodule.full_name + res.append "::" + else if mproperty.visibility <= private_visibility then + # Same project ("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 + 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 + # 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 + if mclassdef.mclass != mproperty.intro_mclassdef.mclass then + res.append mproperty.intro_mclassdef.name.to_cmangle + res.append "__" + end + 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