end
end
+ # Number of nested once
+ readable writable attr _once_count: Int = 0
+
init(tc, module) do super
private meth get_default_constructor_for(n: PNode, c: MMLocalClass, prop: MMSrcMethod): MMMethod
meth add(v: Variable)
do
_dico[v.name] = v
+ _all_variables.add(v)
+ end
+
+ meth mark_is_set(v: Variable)
+ do
+ _set_variables.add(v)
+ end
+
+ meth check_is_set(n: PNode, v: Variable)
+ do
+ if v.must_be_set and not is_set(v) then
+ _visitor.error(n, "Error: variable '{v}' is possibly unset.")
+ var x = self
+ while true do
+ print " {x.node.locate}: {x._set_variables.join(", ")} ; {x._dico.join(", ")}"
+ var x0 = x
+ if x0 isa SubVariableContext then
+ x = x0.prev
+ else
+ break
+ end
+ end
+ end
end
# The effective static type of a given variable
# Variables by name (in the current context only)
attr _dico: Map[Symbol, Variable]
+ # All variables in all contextes
+ attr _all_variables: Set[Variable]
+
# Build a new VariableContext
meth sub(node: PNode): SubVariableContext
do
_node = node
_dico = new HashMap[Symbol, Variable]
end
+
+ # Is a control flow break met? (return, break, continue)
+ readable writable attr _unreash: Bool = false
+
+ # Is a control flow already broken?
+ # Used to avoid repeating the same error message
+ readable writable attr _already_unreash: Bool = false
+
+ # Set of variable that are set (assigned)
+ readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
+
+ # Is a variable set?
+ meth is_set(v: Variable): Bool
+ do
+ return _set_variables.has(v)
+ end
+
+ # Merge back one flow context information
+ meth merge(ctx: VariableContext)
+ do
+ if ctx.unreash then
+ unreash = true
+ if ctx.already_unreash then already_unreash = true
+ return
+ end
+ for v in _all_variables do
+ if not is_set(v) and ctx.is_set(v) then
+ mark_is_set(v)
+ end
+ end
+ end
+
+ # Merge back two alternative flow context informations
+ meth merge2(ctx1, ctx2: VariableContext)
+ do
+ if ctx1.unreash then
+ merge(ctx2)
+ else if ctx2.unreash then
+ merge(ctx1)
+ end
+ for v in _all_variables do
+ if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
+ mark_is_set(v)
+ end
+ end
+ end
end
class RootVariableContext
init(visitor: AbsSyntaxVisitor, node: PNode)
do
super(visitor, node)
+ _all_variables = new HashSet[Variable]
end
end
do
init(p._visitor, node)
_prev = p
+ _all_variables = p._all_variables
+ end
+
+ redef meth is_set(v)
+ do
+ return _set_variables.has(v) or _prev.is_set(v)
end
end
end
end
+redef class Variable
+ # Is the variable must be set before being used ?
+ meth must_be_set: Bool do return false
+end
+
+redef class VarVariable
+ redef meth must_be_set do return true
+end
+
+
###############################################################################
redef class PNode
end
end
+redef class AConcreteMethPropdef
+ redef meth accept_typing(v)
+ do
+ super
+ if v.variable_ctx.unreash == false and method.signature.return_type != null then
+ v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
+ end
+ end
+end
+
redef class AConcreteInitPropdef
readable attr _super_init_calls: Array[MMMethod] = new Array[MMMethod]
readable attr _explicit_super_init_calls: Array[MMMethod] = new Array[MMMethod]
super
+ if n_expr != null then
+ if v.variable_ctx.unreash == false then
+ if variable.closure.signature.return_type != null then
+ v.error(self, "Control error: Reached end of block (a 'continue' with a value was expected).")
+ else if variable.closure.is_break then
+ v.error(self, "Control error: Reached end of break block (an 'abort' was expected).")
+ end
+ end
+ end
+
+ old_var_ctx.merge(v.variable_ctx)
v.variable_ctx = old_var_ctx
v.escapable_ctx.pop
end
var va = new VarVariable(n_id.to_symbol, self)
variable = va
v.variable_ctx.add(va)
+ if n_expr != null then v.variable_ctx.mark_is_set(va)
if n_type != null then
va.stype = n_type.stype
var old_var_ctx = v.variable_ctx
v.variable_ctx = v.variable_ctx.sub(self)
- super
+ for e in n_expr do
+ if v.variable_ctx.unreash and not v.variable_ctx.already_unreash then
+ v.variable_ctx.already_unreash = true
+ v.warning(e, "Warning: unreachable statement.")
+ end
+ v.visit(e)
+ end
+ old_var_ctx.merge(v.variable_ctx)
v.variable_ctx = old_var_ctx
_is_typed = true
end
redef class AReturnExpr
redef meth after_typing(v)
do
+ v.variable_ctx.unreash = true
var t = v.local_property.signature.return_type
if n_expr == null and t != null then
v.error(self, "Error: Return without value in a function.")
redef class AContinueExpr
redef meth after_typing(v)
do
+ v.variable_ctx.unreash = true
var esc = compute_escapable_block(v.escapable_ctx)
if esc == null then return
redef class ABreakExpr
redef meth after_typing(v)
do
+ v.variable_ctx.unreash = true
var esc = compute_escapable_block(v.escapable_ctx)
if esc == null then return
end
end
+redef class AAbortExpr
+ redef meth after_typing(v)
+ do
+ v.variable_ctx.unreash = true
+ end
+end
+
redef class AIfExpr
redef meth accept_typing(v)
do
v.check_conform_expr(n_expr, v.type_bool)
v.use_if_true_variable_ctx(n_expr)
+ v.variable_ctx = v.variable_ctx.sub(n_then)
v.visit(n_then)
- # Restore variable ctx
- v.variable_ctx = old_var_ctx
- if n_else != null then
+ if n_else == null then
+ # Restore variable ctx since the 'then' block is optional
+ v.variable_ctx = old_var_ctx
+ else
+ # Remember what appened in the 'then'
+ var then_var_ctx = v.variable_ctx
+ # Reset to process the 'else'
+ v.variable_ctx = old_var_ctx.sub(n_else)
v.visit(n_else)
+ # Merge then and else in the old control_flow
+ old_var_ctx.merge2(then_var_ctx, v.variable_ctx)
v.variable_ctx = old_var_ctx
end
_is_typed = true
redef meth after_typing(v)
do
+ v.variable_ctx.check_is_set(self, variable)
_stype = v.variable_ctx.stype(variable)
_is_typed = _stype != null
end
redef class AVarAssignExpr
redef meth after_typing(v)
do
+ v.variable_ctx.mark_is_set(variable)
var t = v.variable_ctx.stype(variable)
v.check_conform_expr(n_value, t)
_is_typed = true
redef class AVarReassignExpr
redef meth after_typing(v)
do
+ v.variable_ctx.check_is_set(self, variable)
+ v.variable_ctx.mark_is_set(variable)
var t = v.variable_ctx.stype(variable)
do_lvalue_typing(v, t)
_is_typed = true
redef meth after_typing(v)
do
var va = variable
+ if va.closure.is_break then v.variable_ctx.unreash = true
var sig = va.closure.signature
var args = process_signature(v, sig, n_id.to_symbol, n_args.to_a)
if not n_closure_defs.is_empty then
_accept_typing2 = true
accept_typing(v)
+
+ if v.variable_ctx.unreash == false then
+ if closure.signature.return_type != null then
+ v.error(self, "Control error: Reached end of block (a 'continue' with a value was expected).")
+ else if closure.is_break then
+ v.error(self, "Control error: Reached end of break block (a 'break' was expected).")
+ end
+ end
v.variable_ctx = old_var_ctx
end
end
_stype = n_expr.stype
end
end
+
+redef class AOnceExpr
+ redef meth accept_typing(v)
+ do
+ if v.once_count > 0 then
+ v.warning(self, "Useless once in a once expression.")
+ end
+ v.once_count = v.once_count + 1
+
+ super
+
+ v.once_count = v.once_count - 1
+ end
+end
+