# 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
# 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
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
- private var bool_type_cache: nullable MClassType
+ # 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
+
+ # 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
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
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
# 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
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
#
# 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`
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
+
+ # 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
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
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]
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?
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
# 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 # It is the only remaining type
+ assert sub isa MClassType else print "{sub} <? {sub}" # It is the only remaining type
+
+ # A unfixed formal type can only accept itself
+ if sup isa MFormalType then
+ return false
+ end
if sup isa MNullType then
# `sup` accepts only null
# types to their bounds.
#
# Example
+ #
# class A end
# class B super A end
# class X end
# super G[B]
# redef type U: Y
# end
+ #
# Map[T,U] anchor_to H #-> Map[B,Y]
#
# Explanation of the example:
# 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)`
#
# ## 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]
#
# ## 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`.
#
#
# 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`)
#
# ENSURE: `not self.need_anchor implies result == self`
fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MType is abstract
+ # Resolve formal type to its verbatim bound.
+ # If the type is not formal, just return self
+ #
+ # The result is returned exactly as declared in the "type" property (verbatim).
+ # So it could be another formal type.
+ #
+ # 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.
# 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)`
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
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 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
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: `"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 fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
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: MProperty
+ var mproperty: MVirtualTypeProp
redef fun model do return self.mproperty.intro_mclassdef.mmodule.model
- # Lookup the bound for a given resolved_receiver
- # The result may be a other virtual type (or a parameter type)
- #
- # The result is returned exactly as declared in the "type" property (verbatim).
- #
- # In case of conflict, the method aborts.
- fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+ redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+ do
+ return lookup_single_definition(mmodule, resolved_receiver).bound.as(not null)
+ end
+
+ private fun lookup_single_definition(mmodule: MModule, resolved_receiver: MType): MVirtualTypeDef
do
assert not resolved_receiver.need_anchor
var props = self.mproperty.lookup_definitions(mmodule, resolved_receiver)
if props.is_empty then
abort
else if props.length == 1 then
- return props.first.as(MVirtualTypeDef).bound.as(not null)
+ return props.first
end
var types = new ArraySet[MType]
+ var res = props.first
for p in props do
- types.add(p.as(MVirtualTypeDef).bound.as(not null))
+ types.add(p.bound.as(not null))
+ if not res.is_fixed then res = p
end
if types.length == 1 then
- return types.first
+ return res
end
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)
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
# 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`.
# 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 to_s do return name
- # Resolve the bound for a given resolved_receiver
- # The result may be a other virtual type (or a parameter type)
- fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+ 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
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)
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!
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)
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
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`.
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
+
+ redef var as_notnull = 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
+
+ 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
+
+# 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 absurd, dead, or the absence of knowledge.
+#
+# Semantically it is the singleton `null.as_notnull`.
+class MBottomType
+ super MType
+ redef var model: 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
# 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
# Example: for "(a: Int, b: Bool..., c: Char)" #-> vararg_rank=1
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
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
# 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_package::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)
# 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
#
# 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]
#
# 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]
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
# 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.full_name
+ 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
+ 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