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]
#
# 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.
# 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
# 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]
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
# (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.
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
# 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
# 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
+
+ # 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
- private var bool_type_cache: nullable MClassType
+ # 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
+ # 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
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
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.
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
# 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
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
#
# 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`
# 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.
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 var is_test is lazy do return intro.is_test
+
+ redef fun mdoc_or_fallback
+ do
+ # Don’t use `intro.mdoc_or_fallback` because it would create an infinite
+ # recursion.
+ return intro.mdoc
+ end
end
# 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
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
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
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.
# 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
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
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} <? {sup}" # It is the only remaining type
+
+ # Handle sup-type when the sub-type is class-based (other cases must have be identified before).
+ if sup isa MFormalType or sup isa MNullType or sup isa MBottomType or sup isa MErrorType then
+ # These types are not super-types of Class-based types.
return false
end
- assert sup isa MClassType # It is the only remaining type
+ assert sup isa MClassType else print_error "got {sup} {sub.inspect}" # It is the only remaining type
# Now both are MClassType, we need to dig
if anchor == null then anchor = sub # UGLY: any anchor will work
var resolved_sub = sub.anchor_to(mmodule, anchor)
var res = resolved_sub.collect_mclasses(mmodule).has(sup.mclass)
- if res == false then return false
+ if not res then return false
if not sup isa MGenericType then return true
var sub2 = sub.supertype_to(mmodule, anchor, sup.mclass)
assert sub2.mclass == sup.mclass
var sub_arg = sub2.arguments[i]
var sup_arg = sup.arguments[i]
res = sub_arg.is_subtype(mmodule, anchor, sup_arg)
- if res == false then return false
+ if not res then return false
end
return true
end
#
# Explanation of the example:
# In H, T is set to B, because "H super G[B]", and U is bound to Y,
- # because "redef type U: Y". Therefore, Map[T, U] is bound to
+ # because "redef type U: Y". Therefore, Map[T, U] is bound to
# Map[B, Y]
#
+ # REQUIRE: `self.need_anchor implies anchor != null`
# ENSURE: `not self.need_anchor implies result == self`
# ENSURE: `not result.need_anchor`
- fun anchor_to(mmodule: MModule, anchor: MClassType): MType
+ fun anchor_to(mmodule: MModule, anchor: nullable MClassType): MType
do
if not need_anchor then return self
- assert not anchor.need_anchor
+ assert anchor != null and not anchor.need_anchor
# Just resolve to the anchor and clear all the virtual types
var res = self.resolve_for(anchor, null, mmodule, true)
assert not res.need_anchor
# 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.
+ # In case of conflicts or inconsistencies in the model, the method returns a `MErrorType`.
fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType do return self
# Resolve the formal type to its simplest equivalent form.
#
# By default, return self.
# See the redefinitions for specific behavior in each kind of type.
+ #
+ # In case of conflicts or inconsistencies in the model, the method returns a `MErrorType`.
fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do return self
+ # Is the type a `MErrorType` or contains an `MErrorType`?
+ #
+ # `MErrorType` are used in result with conflict or inconsistencies.
+ #
+ # See `is_legal_in` to check conformity with generic bounds.
+ fun is_ok: Bool do return true
+
+ # Is the type legal in a given `mmodule` (with an optional `anchor`)?
+ #
+ # A type is valid if:
+ #
+ # * it does not contain a `MErrorType` (see `is_ok`).
+ # * its generic formal arguments are within their bounds.
+ fun is_legal_in(mmodule: MModule, anchor: nullable MClassType): Bool do return is_ok
+
# Can the type be resolved?
#
# In order to resolve open types, the formal types must make sence.
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
# * H[G[A], B] -> 3
#
# Formal types have a depth of 1.
+ # Only `MClassType` and `MFormalType` nodes are counted.
fun depth: Int
do
return 1
# * 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
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
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
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.
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
- redef var need_anchor: Bool 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 is noinit
redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
do
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
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
# 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
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
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
# 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
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
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
#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)
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
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)
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
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
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
# 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
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
var b = new FlatBuffer
if not mparameters.is_empty then
b.append("(")
+ var last_mtype = null
for i in [0..mparameters.length[ do
var mparameter = mparameters[i]
+
+ # Group types that are common to contiguous parameters
+ if mparameter.mtype != last_mtype and last_mtype != null then
+ b.append(": ")
+ b.append(last_mtype.to_s)
+ end
+
if i > 0 then b.append(", ")
b.append(mparameter.name)
- b.append(": ")
- b.append(mparameter.mtype.to_s)
+
if mparameter.is_vararg then
+ b.append(": ")
+ b.append(mparameter.mtype.to_s)
b.append("...")
+ last_mtype = null
+ else
+ last_mtype = mparameter.mtype
end
end
+
+ if last_mtype != null then
+ b.append(": ")
+ b.append(last_mtype.to_s)
+ end
+
b.append(")")
end
var ret = self.return_mtype
super MEntity
# The name of the parameter
- redef var name: String
+ redef var name
# The static type of the parameter
var mtype: MType
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
var intro_mclassdef: MClassDef
# The (short) name of the property
- redef var name: String
+ redef var name
+
+ redef var location
- # The canonical name of the property
- # Example: "owner::my_module::MyClass::my_method"
- fun full_name: String
+ 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
# 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
#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
#
# 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]
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
#
# 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]
end
private var lookup_all_definitions_cache = new HashMap2[MModule, MType, Array[MPROPDEF]]
+
+ redef var is_test is lazy do return intro.is_test
+
+ # Does self have the `before` annotation?
+ var is_before: Bool is lazy do return intro.is_before
+
+ # Does self have the `before_all` annotation?
+ var is_before_all: Bool is lazy do return intro.is_before_all
+
+ # Does self have the `after` annotation?
+ var is_after: Bool is lazy do return intro.is_after
+
+ # Does self have the `after_all` annotation?
+ var is_after_all: Bool is lazy do return intro.is_after_all
end
# A global method
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
# 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)
# 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`.
#
assert has_next_property: i.is_ok
return i.item
end
+
+ redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback
+
+ # Does self have the `before` annotation?
+ var is_before = false is writable
+
+ # Does self have the `before_all` annotation?
+ var is_before_all = false is writable
+
+ # Does self have the `after` annotation?
+ var is_after = false is writable
+
+ # Does self have the `after_all` annotation?
+ var is_after_all = false is writable
end
# A local definition of a method
# Note this class is basically an enum.
# FIXME: use a real enum once user-defined enums are available
class MClassKind
- redef var to_s: String
+ redef var to_s
+
+ # Can a class of kind `self` define a membership predicate?
+ var can_customize_isa: Bool
+
+ # Can a class of kind `self` define a constructor?
+ var can_init: Bool
# Is a constructor required?
var need_init: Bool
# TODO: private init because enumeration.
- # Can a class of kind `self` specializes a class of kine `other`?
+ # Can a class of kind `self` specializes a class of kind `other`?
fun can_specialize(other: MClassKind): Bool
do
- if other == interface_kind then return true # everybody can specialize interfaces
- if self == interface_kind or self == enum_kind then
- # no other case for interfaces
+ if other == interface_kind then
+ # everybody can specialize interfaces
+ return true
+ else if self == interface_kind or self == enum_kind then
+ # no other case for interfaces and enums
return false
+ else if self == subset_kind then
+ # A subset may specialize anything, except another subset.
+ # TODO: Allow sub-subsets once we can handle them.
+ return other != subset_kind
else if self == extern_kind then
# only compatible with themselves
return self == other
- else if other == enum_kind or other == extern_kind then
- # abstract_kind and concrete_kind are incompatible
- return false
+ else
+ # assert self == abstract_kind or self == concrete_kind
+ return other == abstract_kind or other == concrete_kind
end
- # remain only abstract_kind and concrete_kind
- return true
end
end
# The class kind `abstract`
-fun abstract_kind: MClassKind do return once new MClassKind("abstract class", true)
+fun abstract_kind: MClassKind do return once new MClassKind("abstract class", false, true, true)
# The class kind `concrete`
-fun concrete_kind: MClassKind do return once new MClassKind("class", true)
+fun concrete_kind: MClassKind do return once new MClassKind("class", false, true, true)
# The class kind `interface`
-fun interface_kind: MClassKind do return once new MClassKind("interface", false)
+fun interface_kind: MClassKind do return once new MClassKind("interface", false, true, false)
# The class kind `enum`
-fun enum_kind: MClassKind do return once new MClassKind("enum", false)
+fun enum_kind: MClassKind do return once new MClassKind("enum", false, true, false)
# The class kind `extern`
-fun extern_kind: MClassKind do return once new MClassKind("extern class", false)
+fun extern_kind: MClassKind do return once new MClassKind("extern class", false, true, false)
+# The class kind `subset`
+fun subset_kind: MClassKind do return once new MClassKind("subset", true, false, false)
+
+# A standalone pre-constructed model used to test various model-related methods.
+#
+# When instantiated, a standalone model is already filled with entities that are exposed as attributes.
+class ModelStandalone
+ super Model
+
+ redef var location = new Location.opaque_file("ModelStandalone")
+
+ # The first module
+ var mmodule0 = new MModule(self, null, "module0", location)
+
+ # The root Object class
+ var mclass_o = new MClass(mmodule0, "Object", location, null, interface_kind, public_visibility)
+
+ # The introduction of `mclass_o`
+ var mclassdef_o = new MClassDef(mmodule0, mclass_o.mclass_type, location)
+end
+
+# A standalone model with the common class diamond-hierarchy ABCD
+class ModelDiamond
+ super ModelStandalone
+
+ # A, a simple subclass of Object
+ var mclass_a = new MClass(mmodule0, "A", location, null, concrete_kind, public_visibility)
+
+ # The introduction of `mclass_a`
+ var mclassdef_a: MClassDef do
+ var res = new MClassDef(mmodule0, mclass_a.mclass_type, location)
+ res.set_supertypes([mclass_o.mclass_type])
+ res.add_in_hierarchy
+ return res
+ end
+
+ # B, a subclass of A (`mclass_a`)
+ var mclass_b = new MClass(mmodule0, "B", location, null, concrete_kind, public_visibility)
+
+ # The introduction of `mclass_b`
+ var mclassdef_b: MClassDef do
+ var res = new MClassDef(mmodule0, mclass_b.mclass_type, location)
+ res.set_supertypes([mclass_a.mclass_type])
+ res.add_in_hierarchy
+ return res
+ end
+
+ # C, another subclass of A (`mclass_a`)
+ var mclass_c = new MClass(mmodule0, "C", location, null, concrete_kind, public_visibility)
+
+ # The introduction of `mclass_c`
+ var mclassdef_c: MClassDef do
+ var res = new MClassDef(mmodule0, mclass_c.mclass_type, location)
+ res.set_supertypes([mclass_a.mclass_type])
+ res.add_in_hierarchy
+ return res
+ end
+
+ # D, a multiple subclass of B (`mclass_b`) and C (`mclass_c`)
+ var mclass_d = new MClass(mmodule0, "D", location, null, concrete_kind, public_visibility)
+
+ # The introduction of `mclass_d`
+ var mclassdef_d: MClassDef do
+ var res = new MClassDef(mmodule0, mclass_d.mclass_type, location)
+ res.set_supertypes([mclass_b.mclass_type, mclass_c.mclass_type])
+ res.add_in_hierarchy
+ return res
+ end
+end