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]
# 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)
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.
# The primitive type `String`
var string_type: MClassType = self.get_primitive_class("String").mclass_type is lazy
- # The primitive type `NativeString`
- var native_string_type: MClassType = self.get_primitive_class("NativeString").mclass_type is lazy
+ # The primitive type `CString`
+ var c_string_type: MClassType = self.get_primitive_class("CString").mclass_type is lazy
# A primitive type of `Array`
fun array_type(elt_type: MType): MClassType do return array_class.get_mtype([elt_type])
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} in {self}")
+ 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} in {self}:"
for c in cla do msg += " {c.full_name}"
- print msg
+ print_error msg
#exit(1)
end
return cla.first
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}")
+ print_error("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}")
abort
end
end
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
+ 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`
# The visibility of the class
# In Nit, the visibility of a class cannot evolve in refinements
- var visibility: MVisibility
+ redef var visibility
init
do
# 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.
# Is `self` and abstract class?
var is_abstract: Bool is lazy do return kind == abstract_kind
+
+ redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
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"
+ # Example: "mymodule$MyClass"
redef var to_s is noinit
init
assert not isset mclass._intro
mclass.intro = self
end
- self.to_s = "{mmodule}#{mclass}"
+ self.to_s = "{mmodule}${mclass}"
end
# Actually the name of the `mclass`
redef fun name do return mclass.name
- # The module and class name separated by a '#'.
+ # The module and class name separated by a '$'.
#
# The short-name of the class is used for introduction.
- # Example: "my_module#MyClass"
+ # Example: "my_module$MyClass"
#
# The full-name of the class is used for refinement.
- # Example: "my_module#intro_module::MyClass"
+ # Example: "my_module$intro_module::MyClass"
redef var full_name is lazy do
if is_intro then
- # public gives 'p#A'
- # private gives 'p::m#A'
- return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}"
+ # 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}"
+ # public gives 'q::n$p::A'
+ # private gives 'q::n$p::m::A'
+ return "{mmodule.full_name}${mclass.full_name}"
else if mclass.visibility > private_visibility then
- # public gives 'p::n#A'
- return "{mmodule.full_name}#{mclass.name}"
+ # public gives 'p::n$A'
+ return "{mmodule.full_name}${mclass.name}"
else
- # private gives 'p::n#::m::A' (redundant p is omitted)
- return "{mmodule.full_name}#::{mclass.intro_mmodule.name}::{mclass.name}"
+ # private gives 'p::n$::m::A' (redundant p is omitted)
+ return "{mmodule.full_name}$::{mclass.intro_mmodule.name}::{mclass.name}"
end
end
# 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]
end
# A global static type
end
#print "4.is {sub} a {sup}? <- no more resolution"
- if sub isa MBottomType then
+ if sub isa MBottomType or sub isa MErrorType then
return true
end
- assert sub isa MClassType else print "{sub} <? {sub}" # It is the only remaining type
+ 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 then
+ 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 else print "got {sup} {sub.inspect}" # 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
# The result is returned exactly as declared in the "type" property (verbatim).
# So it could be another formal type.
#
- # In case of conflicts or inconsistencies in the model, the method returns a `MBottomType`.
+ # 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 `MBottomType`.
+ # 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.
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
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
# 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 or else new MBottomType(model)
+ # 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
var prop = lookup_single_definition(mmodule, resolved_receiver)
var res = prop.bound
- if res == null then return new MBottomType(model)
+ if res == null then return new MErrorType(model)
# Recursively lookup the fixed result
res = res.lookup_fixed(mmodule, resolved_receiver)
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
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
return res
end
end
- abort
+ # Cannot found `self` in `resolved_receiver`
+ return new MErrorType(model)
end
# A PT is fixed when:
# The base type
var mtype: MType
+ 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 mtype.as_nullable
return self.mtype.can_resolve_for(mtype, anchor, mmodule)
end
+ 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)
# 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.
+# 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 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 (short) name of the property
redef var name
+ redef var location
+
+ redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
+
# 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.
+ # It is currently the short-`name` prefixed by the short-name of the class and the full-name of the module.
# Example: "my_package::my_module::MyClass::my_method"
+ #
+ # The full-name of the module is needed because two distinct modules of the same package can
+ # still refine the same class and introduce homonym properties.
+ #
+ # For public properties not introduced by refinement, the module name is not used.
+ #
+ # Example: `my_package::MyClass::My_method`
redef var full_name is lazy do
- return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+ if intro_mclassdef.is_intro then
+ return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+ else
+ return "{intro_mclassdef.mmodule.full_name}::{intro_mclassdef.mclass.name}::{name}"
+ end
end
redef var c_name is lazy do
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
#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
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
# 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`
# * a property "p::m::A::x"
# * redefined in a refinement of a class "q::n::B"
# * in a module "r::o"
- # * so "r::o#q::n::B#p::m::A::x"
+ # * so "r::o$q::n::B$p::m::A::x"
#
# Fortunately, the full-name is simplified when entities are repeated.
- # For the previous case, the simplest form is "p#A#x".
+ # For the previous case, the simplest form is "p$A$x".
redef var full_name is lazy do
var res = new FlatBuffer
- # The first part is the mclassdef. Worst case is "r::o#q::n::B"
+ # The first part is the mclassdef. Worst case is "r::o$q::n::B"
res.append mclassdef.full_name
- res.append "#"
+ res.append "$"
if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
# intro are unambiguous in a class
# 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 mproperty.intro_mclassdef.mmodule.namespace_for(mproperty.visibility)
res.append "::"
else if mproperty.visibility <= private_visibility then
# Same package ("p"=="q"), but private visibility,
redef fun model do return mclassdef.model
# Internal name combining the module, the class and the property
- # Example: "mymodule#MyClass#mymethod"
+ # Example: "mymodule$MyClass$mymethod"
redef var to_s is noinit
# Is self the definition that introduce the property?