syntax: error on local variable masking
[nit.git] / src / syntax / control_flow.nit
index ceadd4e..4b8d0fc 100644 (file)
@@ -24,7 +24,7 @@ import syntax_base
 abstract class VariableContext
        # Look for the variable from its name
        # Return null if nothing found
-       meth [](s: Symbol): Variable
+       fun [](s: Symbol): nullable Variable
        do
                if _dico.has_key(s) then
                        return _dico[s]
@@ -34,66 +34,76 @@ abstract class VariableContext
        end
 
        # Register a new variable with its name
-       meth add(v: Variable)
+       fun add(v: Variable)
        do
+               var old_var = self[v.name]
+               if old_var != null then
+                       _visitor.error(v.decl, "Error: '{v}' already defined at {old_var.decl.location.relative_to(v.decl.location)}.")
+               end
                _dico[v.name] = v
                _all_variables.add(v)
        end
 
-       meth mark_is_set(v: Variable)
+       fun mark_is_set(v: Variable)
        do
                _set_variables.add(v)
        end
 
-       meth check_is_set(n: PNode, v: Variable)
+       fun check_is_set(n: ANode, 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
+       fun stype(v: Variable): nullable MMType
+       do
+               if _stypes.has_key(v) then
+                       return _stypes[v]
+               else
+                       return v.stype
+               end
+       end
+
+       # Set effective static type of a given variable
+       # May be different from the declaration static type
+       fun stype=(v: Variable, t: nullable MMType)
        do
-               return v.stype
+               _stypes[v] = t
        end
 
        # Variables by name (in the current context only)
-       attr _dico: Map[Symbol, Variable]
+       var _dico: Map[Symbol, Variable]
 
        # All variables in all contextes
-       attr _all_variables: Set[Variable]
+       var _all_variables: Set[Variable]
+
+       # Updated static type of variables
+       var _stypes: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
 
        # Build a new VariableContext
-       meth sub(node: PNode): SubVariableContext
+       fun sub(node: ANode): 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
+       fun sub_with(node: ANode, v: Variable, t: MMType): SubVariableContext
        do
-               return new CastVariableContext.with_prev(self, node, v, t)
+               var ctx = sub(node)
+               ctx.stype(v) = t
+               return ctx
        end
 
        # The visitor of the context (used to display error)
-       attr _visitor: AbsSyntaxVisitor
+       var _visitor: AbsSyntaxVisitor
 
        # The syntax node that introduced the context
-       readable attr _node: PNode
+       readable var _node: ANode
 
-       init(visitor: AbsSyntaxVisitor, node: PNode)
+       init(visitor: AbsSyntaxVisitor, node: ANode)
        do
                _visitor = visitor
                _node = node
@@ -101,23 +111,23 @@ abstract class VariableContext
        end
 
        # Is a control flow break met? (return, break, continue)
-       readable writable attr _unreash: Bool = false
+       readable writable var _unreash: Bool = false
 
        # Is a control flow already broken?
        # Used to avoid repeating the same error message
-       readable writable attr _already_unreash: Bool = false
+       readable writable var _already_unreash: Bool = false
 
        # Set of variable that are set (assigned)
-       readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
+       readable var _set_variables: HashSet[Variable] = new HashSet[Variable]
 
        # Is a variable set?
-       meth is_set(v: Variable): Bool
+       fun is_set(v: Variable): Bool
        do
                return _set_variables.has(v)
        end
 
        # Merge back one flow context information
-       meth merge(ctx: VariableContext)
+       fun merge(ctx: VariableContext)
        do
                if ctx.unreash then
                        unreash = true
@@ -128,11 +138,14 @@ abstract class VariableContext
                        if not is_set(v) and ctx.is_set(v) then
                                mark_is_set(v)
                        end
+                       var s = stype(v)
+                       var s1 = ctx.stype(v)
+                       if s1 != s then stype(v) = s1
                end
        end
 
        # Merge back two alternative flow context informations
-       meth merge2(ctx1, ctx2: VariableContext)
+       fun merge2(ctx1, ctx2, basectx: VariableContext)
        do
                if ctx1.unreash then
                        merge(ctx2)
@@ -143,13 +156,40 @@ abstract class VariableContext
                        if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
                                mark_is_set(v)
                        end
+
+                       var s = stype(v)
+                       var s1 = ctx1.stype(v)
+                       var s2 = ctx2.stype(v)
+                       if s1 == s and s2 == s then
+                               # NOP
+                       else if s1 == s2 then
+                               stype(v) = s1
+                       else if s2 == null or s1 < s2 then
+                               stype(v) = s2
+                       else if s1 == null or s2 < s1 then
+                               stype(v) = s1
+                       else
+                               stype(v) = basectx.stype(v)
+                       end
                end
        end
+
+       redef fun to_s
+       do
+               var s = new Buffer
+               s.append(node.location.to_s)
+               for v in _all_variables do
+                       var t = stype(v)
+                       if t == null then continue
+                       s.append(" {v}:{t}")
+               end
+               return s.to_s
+       end
 end
 
 class RootVariableContext
 special VariableContext
-       init(visitor: AbsSyntaxVisitor, node: PNode)
+       init(visitor: AbsSyntaxVisitor, node: ANode)
        do
                super(visitor, node)
                _all_variables = new HashSet[Variable]
@@ -158,9 +198,9 @@ end
 
 class SubVariableContext
 special VariableContext
-       readable attr _prev: VariableContext
+       readable var _prev: VariableContext
 
-       redef meth [](s)
+       redef fun [](s)
        do
                if _dico.has_key(s) then
                        return _dico[s]
@@ -169,50 +209,33 @@ special VariableContext
                end
        end
 
-       redef meth stype(v)
+       redef fun stype(v)
        do
-               return prev.stype(v)
+               if _stypes.has_key(v) then
+                       return _stypes[v]
+               else
+                       return prev.stype(v)
+               end
        end
 
-       init with_prev(p: VariableContext, node: PNode)
+       init with_prev(p: VariableContext, node: ANode)
        do
                init(p._visitor, node)
                _prev = p
                _all_variables = p._all_variables
        end
 
-       redef meth is_set(v)
+       redef fun 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
+       fun must_be_set: Bool do return false
 end
 
 redef class VarVariable
-       redef meth must_be_set do return true
+       redef fun must_be_set do return true
 end