From: Jean Privat Date: Fri, 19 Jun 2009 05:58:04 +0000 (-0400) Subject: syntax: merge control_flow.nit in typing.nit X-Git-Tag: v0.3~247 X-Git-Url: http://nitlanguage.org syntax: merge control_flow.nit in typing.nit VariableContext is used instead of ControlFlowContext. Signed-off-by: Jean Privat --- diff --git a/src/compiling/compiling_methods.nit b/src/compiling/compiling_methods.nit index f99df6d..b381b22 100644 --- a/src/compiling/compiling_methods.nit +++ b/src/compiling/compiling_methods.nit @@ -1059,7 +1059,8 @@ redef class AIfexprExpr end end -redef class AControlableBlock +class AControlableBlock +special PExpr meth compile_inside_block(v: CompilerVisitor) is abstract redef meth compile_stmt(v) do @@ -1078,6 +1079,7 @@ redef class AControlableBlock end redef class AWhileExpr +special AControlableBlock redef meth compile_inside_block(v) do v.add_instr("while (true) \{ /*while*/") @@ -1094,6 +1096,7 @@ redef class AWhileExpr end redef class AForExpr +special AControlableBlock redef meth compile_inside_block(v) do var e = v.compile_expr(n_expr) diff --git a/src/syntax/control_flow.nit b/src/syntax/control_flow.nit deleted file mode 100644 index d13568e..0000000 --- a/src/syntax/control_flow.nit +++ /dev/null @@ -1,348 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2008 Jean Privat -# -# 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 - diff --git a/src/syntax/syntax.nit b/src/syntax/syntax.nit index 5dbe44e..8ce3d94 100644 --- a/src/syntax/syntax.nit +++ b/src/syntax/syntax.nit @@ -20,7 +20,6 @@ package syntax import mmloader import mmbuilder -import control_flow import typing # Loader of nit source files @@ -61,7 +60,6 @@ redef class MMSrcModule private meth process_supermodules(tc: ToolContext) do node.import_super_modules(tc, self) - end # Syntax analysis and MM construction for the module @@ -73,11 +71,6 @@ redef class MMSrcModule 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 diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index 0b7f6b3..ee2eb0a 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -69,6 +69,9 @@ special AbsSyntaxVisitor 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 @@ -139,6 +142,29 @@ abstract class VariableContext 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 @@ -151,6 +177,9 @@ abstract class VariableContext # 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 @@ -175,6 +204,52 @@ abstract class VariableContext _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 @@ -182,6 +257,7 @@ special VariableContext init(visitor: AbsSyntaxVisitor, node: PNode) do super(visitor, node) + _all_variables = new HashSet[Variable] end end @@ -207,6 +283,12 @@ special VariableContext 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 @@ -231,6 +313,16 @@ special SubVariableContext 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 @@ -271,6 +363,16 @@ redef class AMethPropdef 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] @@ -346,6 +448,17 @@ redef class AClosureDecl 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 @@ -395,6 +508,7 @@ redef class AVardeclExpr 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 @@ -415,8 +529,15 @@ redef class ABlockExpr 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 @@ -425,6 +546,7 @@ 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.") @@ -440,6 +562,7 @@ end 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 @@ -463,6 +586,7 @@ end 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 @@ -479,6 +603,13 @@ redef class ABreakExpr 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 @@ -487,13 +618,21 @@ redef class AIfExpr 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 @@ -591,6 +730,7 @@ redef class AVarExpr 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 @@ -599,6 +739,7 @@ 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 @@ -633,6 +774,8 @@ end 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 @@ -1427,6 +1570,7 @@ special AAbsAbsSendExpr 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 @@ -1479,6 +1623,14 @@ redef class AClosureDef _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 @@ -1531,3 +1683,18 @@ redef class AProxyExpr _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 +