if not mtype2 isa MNullType then return
- if mtype isa MNullType then return
-
# Check of useless null
if not check_can_be_null(anode.n_expr, mtype) then return
- mtype = mtype.as_notnull
+ if mtype isa MNullType then
+ # Because of type adaptation, we cannot just stop here
+ # so return use `null` as a bottom type that will be merged easily (cf) `merge_types`
+ mtype = null
+ else
+ mtype = mtype.as_notnull
+ end
# Check for type adaptation
var variable = anode.n_expr.its_variable
# 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)
+ anode.after_flow_context.when_true.set_var(self, variable, mtype2)
+ anode.after_flow_context.when_false.set_var(self, 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)
+ anode.after_flow_context.when_false.set_var(self, variable, mtype2)
+ anode.after_flow_context.when_true.set_var(self, variable, mtype)
else
abort
end
fun get_variable(node: AExpr, variable: Variable): nullable MType
do
+ if not variable.is_adapted then return variable.declared_type
+
var flow = node.after_flow_context
if flow == null then
self.error(node, "No context!")
end
end
+ # Some variables where type-adapted during the visit
+ var dirty = false
+
+ # Some loops had been visited during the visit
+ var has_loop = false
+
fun set_variable(node: AExpr, variable: Variable, mtype: nullable MType)
do
var flow = node.after_flow_context
assert flow != null
- flow.set_var(variable, mtype)
+ flow.set_var(self, variable, mtype)
end
fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
redef class Variable
# The declared type of the variable
var declared_type: nullable MType
+
+ # Was the variable type-adapted?
+ # This is used to speedup type retrieval while it remains `false`
+ private var is_adapted = false
end
redef class FlowContext
# Adapt the variable to a static type
# Warning1: do not modify vars directly.
# Warning2: sub-flow may have cached a unadapted variable
- private fun set_var(variable: Variable, mtype: nullable MType)
+ private fun set_var(v: TypeVisitor, variable: Variable, mtype: nullable MType)
do
+ if variable.declared_type == mtype and not variable.is_adapted then return
+ if vars.has_key(variable) and vars[variable] == mtype then return
self.vars[variable] = mtype
+ v.dirty = true
+ variable.is_adapted = true
+ #node.debug "set {variable} to {mtype or else "X"}"
end
# Look in the flow and previous flow and collect all first reachable type adaptation of a local variable
assert variable != null
variable.declared_type = mtype
end
- v.visit_stmt(nblock)
+
+ loop
+ v.dirty = false
+ v.visit_stmt(nblock)
+ if not v.has_loop or not v.dirty then break
+ end
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
redef class AWhileExpr
redef fun accept_typing(v)
do
+ v.has_loop = true
v.visit_expr_bool(n_expr)
-
v.visit_stmt(n_block)
self.is_typed = true
end
redef class ALoopExpr
redef fun accept_typing(v)
do
+ v.has_loop = true
v.visit_stmt(n_block)
self.is_typed = true
end
redef fun accept_typing(v)
do
+ v.has_loop = true
var mtype = v.visit_expr(n_expr)
if mtype == null then return
self.do_type_iterator(v, mtype)
v.visit_stmt(n_block)
+
self.mtype = n_block.mtype
self.is_typed = true
end
end
end
if mtype == null then
+ # Ensure monotony for type adaptation on loops
+ if self.element_mtype != null then mtypes.add self.element_mtype
mtype = v.merge_types(self, mtypes)
end
if mtype == null or mtype isa MNullType then
#var from = if orig != null then orig.to_s else "invalid"
#var to = if mtype != null then mtype.to_s else "invalid"
#debug("adapt {variable}: {from} -> {to}")
- self.after_flow_context.when_true.set_var(variable, mtype)
+ self.after_flow_context.when_true.set_var(v, variable, mtype)
end
self.mtype = v.type_bool(self)
end
self.recvtype = recvtype
+ var kind = recvtype.mclass.kind
var name: String
var nid = self.n_id
else
name = "new"
end
+ if name == "intern" then
+ if kind != concrete_kind then
+ v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+ return
+ end
+ if n_args.n_exprs.not_empty then
+ v.error(n_args, "Type Error: the intern constructor expects no arguments.")
+ return
+ end
+ # Our job is done
+ self.mtype = recvtype
+ return
+ 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