# 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
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
# 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 vars.has_key(variable) and vars[variable] == mtype then return
self.vars[variable] = mtype
+ v.dirty = 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)