syntax: while/loop exit type evolution
authorJean Privat <jean@pryen.org>
Tue, 12 Jan 2010 20:31:56 +0000 (15:31 -0500)
committerJean Privat <jean@pryen.org>
Thu, 14 Jan 2010 16:43:42 +0000 (11:43 -0500)
Implementation requires that variable contexts on breaks are collected
and merged.

Signed-off-by: Jean Privat <jean@pryen.org>

src/syntax/control_flow.nit
src/syntax/escape.nit
src/syntax/typing.nit
tests/base_var_type_evolution_null4.nit [new file with mode: 0644]
tests/sav/base_isa_cast2_alt8.sav
tests/sav/base_var_type_evolution_null4.sav [new file with mode: 0644]
tests/sav/base_var_type_evolution_null4_alt1.sav [new file with mode: 0644]
tests/sav/base_var_type_evolution_null4_alt2.sav [new file with mode: 0644]
tests/sav/base_var_type_evolution_null4_alt3.sav [new file with mode: 0644]

index b0b1d9e..fc86679 100644 (file)
@@ -177,6 +177,69 @@ abstract class VariableContext
                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
index eb4deb0..8703d26 100644 (file)
@@ -18,6 +18,7 @@
 package escape
 
 import syntax_base
+import control_flow
 
 # Stack escapable blocks
 class EscapableContext
@@ -113,6 +114,9 @@ class EscapableBlock
        # The static type required by the continue statement (if any)
        fun continue_stype: nullable MMType do return null
 
+       # Alternatives variable contexts for breaks
+       readable var _break_variable_contexts: Array[VariableContext] = new Array[VariableContext]
+
        init(node: ANode)
        do
                _node = node
index 7f625e8..84a8483 100644 (file)
@@ -442,10 +442,13 @@ end
 redef class ABreakExpr
        redef fun after_typing(v)
        do
+               var unreash = v.variable_ctx.unreash
                v.variable_ctx.unreash = true
                var esc = compute_escapable_block(v.escapable_ctx)
                if esc == null then return
 
+               if not unreash then esc.break_variable_contexts.add(v.variable_ctx)
+
                var bl = esc.break_list
                if n_expr == null and bl != null then
                        v.error(self, "Error: break with a value required in this block.")
@@ -552,6 +555,12 @@ redef class AWhileExpr
                end
 
                v.variable_ctx = old_var_ctx
+
+               # Compute outside context (assert !cond + all breaks)
+               v.use_if_false_variable_ctx(n_expr)
+               escapable.break_variable_contexts.add(v.variable_ctx)
+               old_var_ctx.combine_merge(escapable.break_variable_contexts, v.base_variable_ctx)
+
                v.base_variable_ctx = old_base_var_ctx
                v.escapable_ctx.pop
                _is_typed = true
@@ -578,6 +587,11 @@ redef class ALoopExpr
                        v.enter_visit(n_block)
                end
 
+               # Compute outside context (assert all breaks)
+               if not escapable.break_variable_contexts.is_empty then
+                       old_var_ctx.combine_merge(escapable.break_variable_contexts, v.base_variable_ctx)
+               end
+
                v.variable_ctx = old_var_ctx
                v.base_variable_ctx = old_base_var_ctx
                v.escapable_ctx.pop
diff --git a/tests/base_var_type_evolution_null4.nit b/tests/base_var_type_evolution_null4.nit
new file mode 100644 (file)
index 0000000..18a88d0
--- /dev/null
@@ -0,0 +1,52 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2010 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.
+
+import kernel
+
+class A
+end
+
+fun rand: Bool = true
+
+fun eat_na(a: nullable A) do if a == null then 0.output else 1.output
+fun eat_a(a: A) do 2.output
+
+fun get_a: nullable A = new A
+fun get_na: nullable A = null
+
+var a = get_na
+while a == null do
+       eat_na(a)
+       #alt1#eat_a(a)
+       #alt2#if rand then break
+       a = new A
+end
+eat_na(a)
+eat_a(a)
+
+'\n'.output
+
+a = get_a
+loop
+       eat_na(a)
+       if rand and a != null then
+               eat_a(a)
+               break
+       end
+       #alt3#break
+end
+eat_na(a)
+eat_a(a)
index c2d6231..06e567b 100644 (file)
@@ -1 +1,7 @@
-alt/base_isa_cast2_alt8.nit:62,1--7: Error: Method 'foo' doesn't exists in A.
+1
+2
+3
+4
+5
+6
+7
diff --git a/tests/sav/base_var_type_evolution_null4.sav b/tests/sav/base_var_type_evolution_null4.sav
new file mode 100644 (file)
index 0000000..81eddda
--- /dev/null
@@ -0,0 +1,8 @@
+0
+1
+2
+
+1
+2
+1
+2
diff --git a/tests/sav/base_var_type_evolution_null4_alt1.sav b/tests/sav/base_var_type_evolution_null4_alt1.sav
new file mode 100644 (file)
index 0000000..54705a8
--- /dev/null
@@ -0,0 +1 @@
+alt/base_var_type_evolution_null4_alt1.nit:33,8: Type error: expected A, got null
diff --git a/tests/sav/base_var_type_evolution_null4_alt2.sav b/tests/sav/base_var_type_evolution_null4_alt2.sav
new file mode 100644 (file)
index 0000000..88ccea7
--- /dev/null
@@ -0,0 +1,8 @@
+0
+0
+2
+
+1
+2
+1
+2
diff --git a/tests/sav/base_var_type_evolution_null4_alt3.sav b/tests/sav/base_var_type_evolution_null4_alt3.sav
new file mode 100644 (file)
index 0000000..da50622
--- /dev/null
@@ -0,0 +1 @@
+alt/base_var_type_evolution_null4_alt3.nit:52,7: Type error: expected A, got nullable A