# The static type of the receiver
# Mainly used for type tests and type resolutions
- var anchor: nullable MClassType
+ var anchor: nullable MClassType = null
# The analyzed mclassdef
- var mclassdef: nullable MClassDef
+ var mclassdef: nullable MClassDef = null
# The analyzed property
var mpropdef: nullable MPropDef
# * method called on the implicit self must be top-level
var is_toplevel_context = false
- init(modelbuilder: ModelBuilder, mmodule: MModule, mpropdef: nullable MPropDef)
+ init
do
- self.modelbuilder = modelbuilder
- self.mmodule = mmodule
+ var mpropdef = self.mpropdef
if mpropdef != null then
self.mpropdef = mpropdef
selfvariable.declared_type = mclass.mclass_type
var mprop = mpropdef.mproperty
- if mprop isa MMethod and mprop.is_toplevel then
+ if mprop isa MMethod and (mprop.is_toplevel or mprop.is_new) then
is_toplevel_context = true
end
end
#node.debug("Unsafe typing: expected {sup}, got {sub}")
return sup
end
- self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}")
+ if sub.need_anchor then
+ var u = anchor_to(sub)
+ self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}")
+ else
+ self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}")
+ end
return null
end
end
- private fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
+ fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
do
var sub = visit_expr(nexpr)
if sub == null then return null # Forward error
return sup
end
+ # Can `mtype` be null (up to the current knowledge)?
+ fun can_be_null(mtype: MType): Bool
+ do
+ if mtype isa MNullableType or mtype isa MNullType then return true
+ if mtype isa MFormalType then
+ var x = anchor_to(mtype)
+ if x isa MNullableType or x isa MNullType then return true
+ end
+ return false
+ end
+
+ # Check that `mtype` can be null (up to the current knowledge).
+ #
+ # If not then display a `useless-null-test` warning on node and return false.
+ # Else return true.
+ fun check_can_be_null(anode: ANode, mtype: MType): Bool
+ do
+ if can_be_null(mtype) then return true
+
+ if mtype isa MFormalType then
+ var res = anchor_to(mtype)
+ modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.")
+ else
+ modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
+ end
+ return false
+ end
+
# Special verification on != and == for null
# Return true
fun null_test(anode: ABinopExpr)
if not mtype2 isa MNullType then return
+ if mtype isa MNullType then return
+
# Check of useless null
- if not mtype isa MNullableType then
- if not anchor_to(mtype) isa MNullableType then
- modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
- end
- return
- end
+ if not check_can_be_null(anode.n_expr, mtype) then return
+
+ mtype = mtype.as_notnull
# Check for type adaptation
var variable = anode.n_expr.its_variable
if variable == null then return
+ # One is null (mtype2 see above) the other is not null
if anode isa AEqExpr then
anode.after_flow_context.when_true.set_var(variable, mtype2)
- anode.after_flow_context.when_false.set_var(variable, mtype.mtype)
+ anode.after_flow_context.when_false.set_var(variable, mtype)
else if anode isa ANeExpr then
anode.after_flow_context.when_false.set_var(variable, mtype2)
- anode.after_flow_context.when_true.set_var(variable, mtype.mtype)
+ anode.after_flow_context.when_true.set_var(variable, mtype)
else
abort
end
fun get_mclass(node: ANode, name: String): nullable MClass
do
- var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name)
- if mclass == null then
- self.modelbuilder.error(node, "Type Error: missing primitive class `{name}'.")
- end
+ var mclass = modelbuilder.get_mclass_by_name(node, mmodule, name)
return mclass
end
#debug("recv: {recvtype} (aka {unsafe_type})")
if recvtype isa MNullType then
- self.error(node, "Error: Method '{name}' call on 'null'.")
- return null
+ # `null` only accepts some methods of object.
+ if name == "==" or name == "!=" or name == "is_same_instance" then
+ var objclass = get_mclass(node, "Object")
+ if objclass == null then return null # Forward error
+ unsafe_type = objclass.mclass_type
+ else
+ self.error(node, "Error: Method '{name}' call on 'null'.")
+ return null
+ end
end
var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
+ if name == "new" and mproperty == null then
+ name = "init"
+ mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
+ end
+
if mproperty == null then
#self.modelbuilder.error(node, "Type error: property {name} not found in {unsafe_type} (ie {recvtype})")
if recv_is_self then
end
- var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+ var msignature = mpropdef.new_msignature or else mpropdef.msignature
+ if msignature == null then return null # skip error
msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature)
var erasure_cast = false
var rettype = mpropdef.msignature.return_mtype
if not recv_is_self and rettype != null then
- rettype = rettype.as_notnullable
+ rettype = rettype.undecorate
if rettype isa MParameterType then
var erased_rettype = msignature.return_mtype
assert erased_rettype != null
var found = true
for t2 in col do
if t2 == null then continue # return null
- if t2 isa MNullableType or t2 isa MNullType then
+ if can_be_null(t2) and not can_be_null(t1) then
t1 = t1.as_nullable
end
if not is_subtype(t2, t1) then found = false
var nblock = self.n_block
if nblock == null then return
- var mpropdef = self.mpropdef.as(not null)
+ var mpropdef = self.mpropdef
+ if mpropdef == null then return # skip error
+
var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
self.selfvariable = v.selfvariable
var mmethoddef = self.mpropdef.as(not null)
- for i in [0..mmethoddef.msignature.arity[ do
- var mtype = mmethoddef.msignature.mparameters[i].mtype
- if mmethoddef.msignature.vararg_rank == i then
+ var msignature = mmethoddef.msignature
+ if msignature == null then return # skip error
+ for i in [0..msignature.arity[ do
+ var mtype = msignature.mparameters[i].mtype
+ if msignature.vararg_rank == i then
var arrayclass = v.get_mclass(self.n_signature.n_params[i], "Array")
if arrayclass == null then return # Skip error
mtype = arrayclass.get_mtype([mtype])
end
v.visit_stmt(nblock)
- if not nblock.after_flow_context.is_unreachable and mmethoddef.msignature.return_mtype != null then
+ if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then
# We reach the end of the function without having a return, it is bad
v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
end
redef class AAttrPropdef
redef fun do_typing(modelbuilder: ModelBuilder)
do
- var mpropdef = self.mpropdef.as(not null)
+ if not has_value then return
+
+ var mpropdef = self.mpropdef
+ if mpropdef == null then return # skip error
+
var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
self.selfvariable = v.selfvariable
var mtype = self.mpropdef.static_mtype
v.visit_expr_subtype(nexpr, mtype)
end
+ var nblock = self.n_block
+ if nblock != null then
+ v.visit_stmt(nblock)
+ if not nblock.after_flow_context.is_unreachable then
+ # We reach the end of the init without having a return, it is bad
+ v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).")
+ end
+ end
end
end
do
v.error(self, "no implemented accept_typing for {self.class_name}")
end
+
+ # Is non-null if `self` is a leaf of a comprehension array construction.
+ # In this case, the enclosing literal array node is designated.
+ # The result of the evaluation of `self` must be
+ # stored inside the designated array (there is an implicit `push`)
+ var comprehension: nullable AArrayExpr = null
end
redef class ABlockExpr
var nexpr = self.n_expr
if nexpr != null then
if mtype != null then
- v.visit_expr_subtype(nexpr, mtype)
+ var etype = v.visit_expr_subtype(nexpr, mtype)
+ if etype == mtype then
+ assert ntype != null
+ v.modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition for variable `{variable.name}`")
+ end
else
mtype = v.visit_expr(nexpr)
if mtype == null then return # Skip error
var decltype = mtype
if mtype == null or mtype isa MNullType then
- decltype = v.get_mclass(self, "Object").mclass_type.as_nullable
+ var objclass = v.get_mclass(self, "Object")
+ if objclass == null then return # skip error
+ decltype = objclass.mclass_type.as_nullable
if mtype == null then mtype = decltype
end
self.read_type = readtype
- if readtype isa MNullType then
- v.error(self, "Error: Method '{reassign_name}' call on 'null'.")
- return null
- end
-
var callsite = v.get_method(self, readtype, reassign_name, false)
if callsite == null then return null # Skip error
self.reassign_callsite = callsite
redef fun accept_typing(v)
do
var nexpr = self.n_expr
- var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype
+ var ret_type
+ var mpropdef = v.mpropdef
+ if mpropdef isa MMethodDef then
+ ret_type = mpropdef.msignature.return_mtype
+ else if mpropdef isa MAttributeDef then
+ ret_type = mpropdef.static_mtype
+ else
+ abort
+ end
if nexpr != null then
if ret_type != null then
v.visit_expr_subtype(nexpr, ret_type)
v.visit_stmt(n_then)
v.visit_stmt(n_else)
+
self.is_typed = true
+
+ if n_then != null and n_else == null then
+ self.mtype = n_then.mtype
+ end
end
end
is_col = true
end
- if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type, objcla.mclass_type.as_nullable])) then
+ if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then
# Map Iterator
var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla)
var variables = self.variables
# anchor formal and virtual types
if mtype.need_anchor then mtype = v.anchor_to(mtype)
- mtype = mtype.as_notnullable
+ mtype = mtype.undecorate
self.coltype = mtype.as(MClassType)
# get methods is_ok, next, item
self.do_type_iterator(v, mtype)
v.visit_stmt(n_block)
+ self.mtype = n_block.mtype
self.is_typed = true
end
end
return # Skip error
end
- t1 = t1.as_notnullable
+ if t1 isa MNullType then
+ v.error(n_expr, "Type error: or else on null")
+ else if v.check_can_be_null(n_expr, t1) then
+ t1 = t1.as_notnull
+ end
var t = v.merge_types(self, [t1, t2])
if t == null then
- t = v.mmodule.object_type
- if t2 isa MNullableType then
+ var c = v.get_mclass(self, "Object")
+ if c == null then return # forward error
+ t = c.mclass_type
+ if v.can_be_null(t2) then
t = t.as_nullable
end
#v.error(self, "Type Error: ambiguous type {t1} vs {t2}")
var mclass = v.get_mclass(self, "String")
if mclass == null then return # Forward error
self.mtype = mclass.mclass_type
+ var objclass = v.get_mclass(self, "Object")
+ if objclass == null then return # Forward error
+ var objtype = objclass.mclass_type
for nexpr in self.n_exprs do
- v.visit_expr_subtype(nexpr, v.mmodule.object_type)
+ v.visit_expr_subtype(nexpr, objtype)
end
end
end
redef class AArrayExpr
+ # The `with_capacity` method on Array
var with_capacity_callsite: nullable CallSite
+
+ # The `push` method on arrays
var push_callsite: nullable CallSite
+ # The element of each type
+ var element_mtype: nullable MType
+
+ # Set that `self` is a part of comprehension array `na`
+ # If `self` is a `for`, or a `if`, then `set_comprehension` is recursively applied.
+ private fun set_comprehension(n: nullable AExpr)
+ do
+ if n == null then
+ return
+ else if n isa AForExpr then
+ set_comprehension(n.n_block)
+ else if n isa AIfExpr then
+ set_comprehension(n.n_then)
+ set_comprehension(n.n_else)
+ else
+ # is a leave
+ n.comprehension = self
+ end
+ end
redef fun accept_typing(v)
do
var mtype: nullable MType = null
end
var mtypes = new Array[nullable MType]
var useless = false
- for e in self.n_exprs.n_exprs do
+ for e in self.n_exprs do
var t = v.visit_expr(e)
if t == null then
return # Skip error
end
+ set_comprehension(e)
if mtype != null then
if v.check_subtype(e, t, mtype) == null then return # Skip error
if t == mtype then useless = true
if mtype == null then
mtype = v.merge_types(self, mtypes)
end
- if mtype == null then
+ if mtype == null or mtype isa MNullType then
v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}")
return
end
assert ntype != null
v.modelbuilder.warning(ntype, "useless-type", "Warning: useless type declaration `{mtype}` in literal Array since it can be inferred from the elements type.")
end
+
+ self.element_mtype = mtype
+
var mclass = v.get_mclass(self, "Array")
if mclass == null then return # Forward error
var array_mtype = mclass.get_mtype([mtype])
v.error(self, "Type error: as(not null) on null")
return
end
- if mtype isa MNullableType then
- self.mtype = mtype.mtype
- return
- end
- self.mtype = mtype
- if mtype isa MClassType then
- v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}`.")
- return
- end
- assert mtype.need_anchor
- var u = v.anchor_to(mtype)
- if not u isa MNullableType then
- v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}: {u}`.")
- return
+ if v.check_can_be_null(n_expr, mtype) then
+ mtype = mtype.as_notnull
end
+
+ self.mtype = mtype
end
end
var name = self.property_name
if recvtype == null then return # Forward error
- if recvtype isa MNullType then
- v.error(self, "Error: Method '{name}' call on 'null'.")
- return
- end
var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr)
if callsite == null then return
var name = self.property_name
if recvtype == null then return # Forward error
- if recvtype isa MNullType then
- v.error(self, "Error: Method '{name}' call on 'null'.")
- return
- end
var for_self = self.n_expr isa ASelfExpr
var callsite = v.get_method(self, recvtype, name, for_self)
# The constructor invoked by the new.
var callsite: nullable CallSite
+ # The designated type
+ var recvtype: nullable MClassType
+
redef fun accept_typing(v)
do
var recvtype = v.resolve_mtype(self.n_type)
if recvtype == null then return
- self.mtype = recvtype
if not recvtype isa MClassType then
if recvtype isa MNullableType then
v.error(self, "Type error: cannot instantiate the formal type {recvtype}.")
return
end
- else
- if recvtype.mclass.kind == abstract_kind then
- v.error(self, "Cannot instantiate abstract class {recvtype}.")
- return
- else if recvtype.mclass.kind == interface_kind then
- v.error(self, "Cannot instantiate interface {recvtype}.")
- return
- end
end
+ self.recvtype = recvtype
+
var name: String
var nid = self.n_id
if nid != null then
name = nid.text
else
- name = "init"
+ name = "new"
end
var callsite = v.get_method(self, recvtype, name, false)
if callsite == null then return
+ if not callsite.mproperty.is_new then
+ var kind = recvtype.mclass.kind
+ if kind != concrete_kind then
+ v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+ return
+ end
+ self.mtype = recvtype
+ else
+ self.mtype = callsite.msignature.return_mtype
+ assert self.mtype != null
+ end
+
self.callsite = callsite
if not callsite.mproperty.is_init_for(recvtype.mclass) then
var mpropdefs = mproperty.lookup_definitions(v.mmodule, unsafe_type)
assert mpropdefs.length == 1
var mpropdef = mpropdefs.first
- var attr_type = mpropdef.static_mtype.as(not null)
+ var attr_type = mpropdef.static_mtype
+ if attr_type == null then return # skip error
attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr)
self.attr_type = attr_type
end