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
# 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
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.
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)
+ 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
#exit(1)
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("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}")
+ abort
end
end
return res
# The short name of the class
# In Nit, the name of a class cannot evolve in refinements
- redef var name: String
+ redef var name
# The canonical name of the class
#
# 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
#
# 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`
# 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
# Internal name combining the module and the class
# Example: "mymodule#MyClass"
- redef var to_s: String is noinit
+ redef var to_s is noinit
init
do
# public gives 'p#A'
# private gives 'p::m#A'
return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}"
- else if mclass.intro_mmodule.mproject != mmodule.mproject then
+ 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}"
redef var c_name is lazy do
if is_intro then
return "{mmodule.c_namespace_for(mclass.visibility)}___{mclass.c_name}"
- else if mclass.intro_mmodule.mproject == mmodule.mproject and mclass.visibility > private_visibility then
+ 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}"
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]
end
#print "4.is {sub} a {sup}? <- no more resolution"
- 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
+ if sub isa MBottomType then
+ return true
end
- if sup isa MNullType then
- # `sup` accepts only null
+ assert sub isa MClassType else print "{sub} <? {sub}" # 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
+ # 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 "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 conflict, the method aborts.
+ # In case of conflicts or inconsistencies in the model, the method returns a `MBottomType`.
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`.
fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do return self
# Can the type be resolved?
# The short-name of the class, then the full-name of each type arguments within brackets.
# Example: `"Map[String, List[Int]]"`
- redef var to_s: String is noinit
+ redef var to_s is noinit
# The full-name of the class, then the full-name of each type arguments within brackets.
- # Example: `"standard::Map[standard::String, standard::List[standard::Int]]"`
+ # 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
return res.to_s
end
- redef var need_anchor: Bool is noinit
+ redef var need_anchor is noinit
redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
do
redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
do
- return lookup_single_definition(mmodule, resolved_receiver).bound.as(not null)
+ return lookup_single_definition(mmodule, resolved_receiver).bound or else new MBottomType(model)
end
private fun lookup_single_definition(mmodule: MModule, resolved_receiver: MType): MVirtualTypeDef
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 MBottomType(model)
# Recursively lookup the fixed result
res = res.lookup_fixed(mmodule, resolved_receiver)
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
self.to_s = "nullable {mtype}"
end
- redef var to_s: String is noinit
+ redef var to_s is noinit
redef var full_name is lazy do return "nullable {mtype.full_name}"
# 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
- # Aborts on `null`
- redef fun as_notnull do abort # sorry...
+ 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
+ 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
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
super MEntity
# The name of the parameter
- redef var name: String
+ redef var name
# The static type of the parameter
var mtype: MType
var intro_mclassdef: MClassDef
# The (short) name of the property
- redef var name: String
+ redef var name
# The canonical name of the property.
#
# It is the short-`name` prefixed by the short-name of the class and the full-name of the module.
- # Example: "my_project::my_module::MyClass::my_method"
+ # 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
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
res.append name
else
# Just try to simplify each part
- if mclassdef.mmodule.mproject != mproperty.intro_mclassdef.mmodule.mproject then
+ 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 project ("p"=="q"), but private visibility,
+ # 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.mproject then
+ 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 "::"
# Internal name combining the module, the class and the property
# Example: "mymodule#MyClass#mymethod"
- redef var to_s: String is noinit
+ 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`.
#
# 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
# Is a constructor required?
var need_init: Bool