+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2008-2009 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 and variable visibility in property bodies, statements and expressions
+package control_flow
+
+import syntax_base
+
+# Associate symbols to variable and variables to type
+# Can be nested
+abstract class VariableContext
+ # Look for the variable from its name
+ # Return null if nothing found
+ meth [](s: Symbol): Variable
+ do
+ if _dico.has_key(s) then
+ return _dico[s]
+ else
+ return null
+ end
+ end
+
+ # Register a new variable with its name
+ 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
+ # May be different from the declaration static type
+ meth stype(v: Variable): MMType
+ do
+ return v.stype
+ end
+
+ # 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
+ return new SubVariableContext.with_prev(self, node)
+ end
+
+ # Build a nested VariableContext with new variable information
+ meth sub_with(node: PNode, v: Variable, t: MMType): SubVariableContext
+ do
+ return new CastVariableContext.with_prev(self, node, v, t)
+ end
+
+ # The visitor of the context (used to display error)
+ attr _visitor: AbsSyntaxVisitor
+
+ # The syntax node that introduced the context
+ readable attr _node: PNode
+
+ init(visitor: AbsSyntaxVisitor, node: PNode)
+ do
+ _visitor = visitor
+ _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
+special VariableContext
+ init(visitor: AbsSyntaxVisitor, node: PNode)
+ do
+ super(visitor, node)
+ _all_variables = new HashSet[Variable]
+ end
+end
+
+class SubVariableContext
+special VariableContext
+ readable attr _prev: VariableContext
+
+ redef meth [](s)
+ do
+ if _dico.has_key(s) then
+ return _dico[s]
+ else
+ return prev[s]
+ end
+ end
+
+ redef meth stype(v)
+ do
+ return prev.stype(v)
+ end
+
+ init with_prev(p: VariableContext, node: PNode)
+ 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
+
+class CastVariableContext
+special SubVariableContext
+ attr _variable: Variable
+ attr _var_type: MMType
+
+ redef meth stype(v)
+ do
+ if _variable == v then
+ return _var_type
+ end
+ return prev.stype(v)
+ end
+
+ init with_prev(p: VariableContext, node: PNode, v: Variable, t: MMType)
+ do
+ super(p, node)
+ _variable = v
+ _var_type =t
+ 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