syntax: while/loop exit type evolution
[nit.git] / src / syntax / control_flow.nit
index 44d1270..fc86679 100644 (file)
@@ -36,6 +36,10 @@ abstract class VariableContext
        # Register a new variable with its name
        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
@@ -45,20 +49,10 @@ abstract class VariableContext
                _set_variables.add(v)
        end
 
-       fun 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
 
@@ -90,13 +84,13 @@ abstract class VariableContext
        var _stypes: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
 
        # Build a new VariableContext
-       fun 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
-       fun sub_with(node: PNode, v: Variable, t: MMType): SubVariableContext
+       fun sub_with(node: ANode, v: Variable, t: MMType): SubVariableContext
        do
                var ctx = sub(node)
                ctx.stype(v) = t
@@ -107,9 +101,9 @@ abstract class VariableContext
        var _visitor: AbsSyntaxVisitor
 
        # The syntax node that introduced the context
-       readable var _node: PNode
+       readable var _node: ANode
 
-       init(visitor: AbsSyntaxVisitor, node: PNode)
+       init(visitor: AbsSyntaxVisitor, node: ANode)
        do
                _visitor = visitor
                _node = node
@@ -155,8 +149,10 @@ abstract class VariableContext
        do
                if ctx1.unreash then
                        merge(ctx2)
+                       return
                else if ctx2.unreash then
                        merge(ctx1)
+                       return
                end
                for v in _all_variables do
                        if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
@@ -168,22 +164,114 @@ abstract class VariableContext
                        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 if s1 == null or s2 == null then
+                               stype(v) = null
                        else
+                               var sm = merge_types(s1, s2)
+                               if sm == null then
+                                       stype(v) = basectx.stype(v)
+                               else
+                                       stype(v) = sm
+                               end
+                       end
+               end
+       end
+
+       # Combine informations of ctx to the current flow context informations as an alternative
+       # Require that all contexts are reachable at the end
+       fun combine_merge(ctxs: Array[VariableContext], basectx: VariableContext)
+       do
+               for v in _all_variables do
+                       do
+                               if not is_set(v) then
+                                       for ctx in ctxs do
+                                               if not ctx.is_set(v) then break label set
+                                       end
+                                       mark_is_set(v)
+                               end
+                       end label set
+
+                       var candidate: nullable MMType = null
+                       var is_nullable = false
+                       var same_candidate: nullable MMType = ctxs.first.stype(v)
+                       for ctx in ctxs do
+                               var t = ctx.stype(v)
+                               if t == null then
+                                       stype(v) = null
+                                       continue label each_variable
+                               end
+                               if t != same_candidate then
+                                       same_candidate = null
+                               end
+                               if t isa MMTypeNone then
+                                       is_nullable = true
+                                       continue
+                               end
+                               if t isa MMNullableType then
+                                       is_nullable = true
+                                       t = t.as_notnull
+                               end
+                               if candidate == null or candidate < t then
+                                       candidate = t
+                               end
+                       end
+                       if same_candidate != null then
+                               stype(v) = same_candidate
+                       end
+                       if is_nullable then
+                               if candidate == null then
+                                       candidate = _visitor.type_none
+                               else
+                                       candidate = candidate.as_nullable
+                               end
+                       end
+                       if candidate == null then
                                stype(v) = basectx.stype(v)
+                       else
+                               for ctx in ctxs do
+                                       var t = ctx.stype(v)
+                                       if not t < candidate then
+                                               stype(v) = basectx.stype(v)
+                                               continue label each_variable
+                                       end
+                               end
                        end
+                       stype(v) = candidate
+               end label each_variable
+       end
+
+       # Combine and get the most specific comon supertype
+       # return null if no comon supertype is found
+       private fun merge_types(t1, t2: MMType): nullable MMType
+       do
+               if t1 == t2 then return t1
+               if t1 isa MMTypeNone then return t2.as_nullable
+               if t2 isa MMTypeNone then return t1.as_nullable
+               var is_nullable = false
+               if t1.is_nullable then
+                       is_nullable = true
+                       t1 = t1.as_notnull
+               end
+               if t2.is_nullable then
+                       is_nullable = true
+                       t2 = t2.as_notnull
+               end
+               var t: MMType
+               if t1 < t2 then
+                       t = t2
+               else if t2 < t1 then
+                       t = t1
+               else
+                       return null
                end
+               if is_nullable then t = t.as_nullable
+               return t
        end
 
        redef fun to_s
        do
                var s = new Buffer
-               s.append(node.locate)
+               s.append(node.location.to_s)
                for v in _all_variables do
                        var t = stype(v)
                        if t == null then continue
@@ -195,7 +283,7 @@ end
 
 class RootVariableContext
 special VariableContext
-       init(visitor: AbsSyntaxVisitor, node: PNode)
+       init(visitor: AbsSyntaxVisitor, node: ANode)
        do
                super(visitor, node)
                _all_variables = new HashSet[Variable]
@@ -224,7 +312,7 @@ special VariableContext
                end
        end
 
-       init with_prev(p: VariableContext, node: PNode)
+       init with_prev(p: VariableContext, node: ANode)
        do
                init(p._visitor, node)
                _prev = p