VariableContext is used instead of ControlFlowContext.
Signed-off-by: Jean Privat <jean@pryen.org>
end
end
-redef class AControlableBlock
+class AControlableBlock
+special PExpr
meth compile_inside_block(v: CompilerVisitor) is abstract
redef meth compile_stmt(v)
do
end
redef class AWhileExpr
+special AControlableBlock
redef meth compile_inside_block(v)
do
v.add_instr("while (true) \{ /*while*/")
end
redef class AForExpr
+special AControlableBlock
redef meth compile_inside_block(v)
do
var e = v.compile_expr(n_expr)
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Analysis control flow in property bodies, statements and expressions
-package control_flow
-
-import syntax_base
-
-redef class MMSrcModule
- # Walk trough the module and type statments and expressions
- # Require than supermodules are processed
- meth do_control_flow(tc: ToolContext)
- do
- var tv = new ControlFlowVisitor(tc, self)
- tv.visit(node)
- 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
-
-
-
-# Control flow visitor
-# * Check reachability in methods
-# * Associate breaks and continues
-# * Check some other warning
-private class ControlFlowVisitor
-special AbsSyntaxVisitor
- redef meth visit(n)
- do
- if n != null then n.accept_control_flow(self)
- end
-
- # Number of nested once
- readable writable attr _once_count: Int = 0
-
- # Current knowledge about variables types
- readable writable attr _control_flow_ctx: ControlFlowContext
-
- meth check_is_set(n: PNode, v: Variable)
- do
- if v.must_be_set and not control_flow_ctx.is_set(v) then
- error(n, "Error: variable '{v}' is possibly unset.")
- var cfc = control_flow_ctx
- while cfc != null do
- print("cfc: " + cfc.set_variables.join(" "))
- cfc = cfc.prev
- end
- end
- end
-
- meth mark_is_set(v: Variable)
- do
- control_flow_ctx.set_variables.add(v)
- end
-
- init(tc, m) do super
-end
-
-private class ControlFlowContext
- # Previous control flow context if any
- readable attr _prev: ControlFlowContext
-
- # 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) or (_prev != null and _prev.is_set(v))
- end
-
- meth sub: ControlFlowContext
- do
- return new ControlFlowContext.with_prev(self)
- end
-
- init
- do
- end
-
- init with_prev(p: ControlFlowContext)
- do
- _prev = p
- _unreash = p.unreash
- _already_unreash = p.already_unreash
- end
-end
-
-###############################################################################
-
-redef class PNode
- private meth accept_control_flow(v: ControlFlowVisitor)
- do
- accept_abs_syntax_visitor(v)
- end
-end
-
-redef class AMethPropdef
- redef meth accept_control_flow(v)
- do
- v.control_flow_ctx = new ControlFlowContext
- super
- end
-end
-
-redef class AConcreteMethPropdef
- redef meth accept_control_flow(v)
- do
- super
- if v.control_flow_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 AVardeclExpr
- redef meth accept_control_flow(v)
- do
- super
- if n_expr != null then v.mark_is_set(variable)
- end
-end
-
-redef class ABlockExpr
- redef meth accept_control_flow(v)
- do
- for e in n_expr do
- if v.control_flow_ctx.unreash and not v.control_flow_ctx.already_unreash then
- v.control_flow_ctx.already_unreash = true
- v.warning(e, "Warning: unreachable statement.")
- end
- v.visit(e)
- end
- end
-end
-
-redef class AReturnExpr
- redef meth accept_control_flow(v)
- do
- super
- v.control_flow_ctx.unreash = true
- end
-end
-
-redef class ABreakExpr
- redef meth accept_control_flow(v)
- do
- super
- v.control_flow_ctx.unreash = true
- end
-end
-redef class AContinueExpr
- redef meth accept_control_flow(v)
- do
- super
- v.control_flow_ctx.unreash = true
- end
-end
-
-redef class AAbortExpr
- redef meth accept_control_flow(v)
- do
- super
- v.control_flow_ctx.unreash = true
- end
-end
-
-redef class AClosureCallExpr
- redef meth accept_control_flow(v)
- do
- super
- if variable.closure.is_break then v.control_flow_ctx.unreash = true
- end
-end
-
-redef class AIfExpr
- redef meth accept_control_flow(v)
- do
- v.visit(n_expr)
-
- var old_control_flow_ctx = v.control_flow_ctx
- v.control_flow_ctx = v.control_flow_ctx.sub
-
- v.visit(n_then)
-
- if n_else == null then
- # Restore control flow ctx since the 'then" block is optional
- v.control_flow_ctx = old_control_flow_ctx
- else
- # Remember what appens in the 'then'
- var then_control_flow_ctx = v.control_flow_ctx
- # Reset to execute the 'else'
- v.control_flow_ctx = old_control_flow_ctx.sub
-
- v.visit(n_else)
-
- # Merge then and else in the old control_flow
- old_control_flow_ctx.unreash = v.control_flow_ctx.unreash and then_control_flow_ctx.unreash
-
- if v.control_flow_ctx.unreash then v.control_flow_ctx = then_control_flow_ctx
- if then_control_flow_ctx.unreash then then_control_flow_ctx = v.control_flow_ctx
- for variable in v.control_flow_ctx.set_variables do
- if then_control_flow_ctx.is_set(variable) then
- old_control_flow_ctx.set_variables.add(variable)
- end
- end
- v.control_flow_ctx = old_control_flow_ctx
- end
- end
-end
-
-class AControlableBlock
-special PExpr
- redef meth accept_control_flow(v)
- do
- # Store old control flow values
- var old_control_flow_ctx = v.control_flow_ctx
- v.control_flow_ctx = v.control_flow_ctx.sub
-
- super
-
- # Check control flow if any
- check_control_flow(v)
-
- # Restore control flow value since all controlable blocks are optionnal
- v.control_flow_ctx = old_control_flow_ctx
- end
-
- private meth check_control_flow(v: ControlFlowVisitor) do end
-end
-
-redef class AWhileExpr
-special AControlableBlock
-end
-
-redef class AForExpr
-special AControlableBlock
-end
-
-redef class AVarExpr
- redef meth accept_control_flow(v)
- do
- super
- v.check_is_set(self, variable)
- end
-end
-
-redef class AVarAssignExpr
- redef meth accept_control_flow(v)
- do
- super
- v.mark_is_set(variable)
- end
-end
-
-redef class AVarReassignExpr
- redef meth accept_control_flow(v)
- do
- super
- v.check_is_set(self, variable)
- v.mark_is_set(variable)
- end
-end
-
-redef class AClosureDecl
- redef meth accept_control_flow(v)
- do
- if n_expr != null then
- var old_control_flow_ctx = v.control_flow_ctx
- v.control_flow_ctx = v.control_flow_ctx.sub
-
- super
-
- if v.control_flow_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
-
- v.control_flow_ctx = old_control_flow_ctx
- end
- end
-end
-
-redef class AClosureDef
-special AControlableBlock
- redef meth accept_control_flow(v)
- do
- for va in variables do v.mark_is_set(va)
- super
- end
-
- redef meth check_control_flow(v)
- do
- if v.control_flow_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
- end
-end
-
-redef class AOnceExpr
- redef meth accept_control_flow(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
-
import mmloader
import mmbuilder
-import control_flow
import typing
# Loader of nit source files
private meth process_supermodules(tc: ToolContext)
do
node.import_super_modules(tc, self)
-
end
# Syntax analysis and MM construction for the module
do_typing(tc)
if tc.error_count > 0 then exit(1)
-
- do_control_flow(tc)
- if tc.error_count > 0 then exit(1)
-
end
-
end
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
+