syntax: do not nest variable context in ABlockExpr
[nit.git] / src / syntax / typing.nit
index f004491..adc5736 100644 (file)
@@ -293,7 +293,6 @@ redef class AClosureDecl
                        v.check_conform_expr(x, t)
                end
 
-               old_var_ctx.merge(v.variable_ctx)
                v.variable_ctx = old_var_ctx
                v.base_variable_ctx = old_base_var_ctx
                v.escapable_ctx.pop
@@ -381,19 +380,15 @@ end
 redef class ABlockExpr
        redef fun accept_typing(v)
        do
-               var old_var_ctx = v.variable_ctx
-               v.variable_ctx = v.variable_ctx.sub(self)
-
                for e in n_expr do
-                       if v.variable_ctx.unreash and not v.variable_ctx.already_unreash then
+                       if not v.variable_ctx.unreash then
+                               v.enter_visit(e)
+                       else if not v.variable_ctx.already_unreash then
                                v.variable_ctx.already_unreash = true
-                               v.warning(e, "Warning: unreachable statement.")
+                               v.error(e, "Error: unreachable statement.")
                        end
-                       v.enter_visit(e)
                end
 
-               old_var_ctx.merge(v.variable_ctx)
-               v.variable_ctx = old_var_ctx
                _is_typed = true
        end
 end
@@ -442,10 +437,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.")
@@ -476,9 +474,21 @@ redef class ADoExpr
                var escapable = new BreakOnlyEscapableBlock(self)
                _escapable = escapable
                v.escapable_ctx.push(escapable, n_label)
+               var old_var_ctx = v.variable_ctx
 
+               v.variable_ctx = old_var_ctx.sub(self)
                super
 
+               # Add the end of the block as an exit context
+               if not v.variable_ctx.unreash then
+                       escapable.break_variable_contexts.add(v.variable_ctx)
+               end
+
+               # Merge all exit contexts
+               if not escapable.break_variable_contexts.is_empty then
+                       v.variable_ctx = old_var_ctx.merge(self, escapable.break_variable_contexts, v.base_variable_ctx)
+               end
+
                v.escapable_ctx.pop
                _is_typed = true
        end
@@ -514,8 +524,7 @@ redef class AIfExpr
                end
 
                # Merge 'then' and 'else' contexts
-               old_var_ctx.merge2(then_var_ctx, v.variable_ctx, v.base_variable_ctx)
-               v.variable_ctx = old_var_ctx
+               v.variable_ctx = old_var_ctx.merge_reash(self, then_var_ctx, v.variable_ctx, v.base_variable_ctx)
                _is_typed = true
        end
 end
@@ -532,7 +541,6 @@ redef class AWhileExpr
                var old_var_ctx = v.variable_ctx
                var old_base_var_ctx = v.base_variable_ctx
                v.base_variable_ctx = v.variable_ctx
-               v.variable_ctx = v.variable_ctx.sub(self)
 
                # Process condition
                v.enter_visit(n_expr)
@@ -551,7 +559,12 @@ redef class AWhileExpr
                        v.enter_visit(n_block)
                end
 
+               # Compute outside context (assert !cond + all breaks)
                v.variable_ctx = old_var_ctx
+               v.use_if_false_variable_ctx(n_expr)
+               escapable.break_variable_contexts.add(v.variable_ctx)
+               v.variable_ctx = old_var_ctx.merge(self, escapable.break_variable_contexts, v.base_variable_ctx)
+
                v.base_variable_ctx = old_base_var_ctx
                v.escapable_ctx.pop
                _is_typed = true
@@ -570,7 +583,6 @@ redef class ALoopExpr
                var old_var_ctx = v.variable_ctx
                var old_base_var_ctx = v.base_variable_ctx
                v.base_variable_ctx = v.variable_ctx
-               v.variable_ctx = v.variable_ctx.sub(self)
 
                # Process inside
                if n_block != null then
@@ -578,7 +590,14 @@ redef class ALoopExpr
                        v.enter_visit(n_block)
                end
 
-               v.variable_ctx = old_var_ctx
+               # Compute outside context (assert all breaks)
+               if escapable.break_variable_contexts.is_empty then
+                       old_var_ctx.unreash = true
+                       v.variable_ctx = old_var_ctx
+               else
+                       v.variable_ctx = old_var_ctx.merge(self, escapable.break_variable_contexts, v.base_variable_ctx)
+               end
+
                v.base_variable_ctx = old_base_var_ctx
                v.escapable_ctx.pop
                _is_typed = true
@@ -609,7 +628,12 @@ redef class AForExpr
                # Process collection
                v.enter_visit(n_expr)
 
-               if not v.check_conform_expr(n_expr, v.type_collection) then return
+               if not v.check_conform_expr(n_expr, v.type_collection) then
+                       v.variable_ctx = old_var_ctx
+                       v.base_variable_ctx = old_base_var_ctx
+                       v.escapable_ctx.pop
+                       return
+               end
                var expr_type = n_expr.stype
 
                # Get iterator
@@ -679,7 +703,7 @@ redef class AVarAssignExpr
                if btype != null and not v.check_conform_expr(n_value, btype) then return
 
                # Always cast
-               v.variable_ctx.stype(variable) = n_value.stype
+               v.variable_ctx = v.variable_ctx.sub_with(self, variable, n_value.stype)
 
                _is_typed = true
        end
@@ -731,7 +755,7 @@ redef class AVarReassignExpr
                if btype != null and not v.check_conform(n_value, t2, btype) then return
 
                # Always cast
-               v.variable_ctx.stype(variable) = t2
+               v.variable_ctx = v.variable_ctx.sub_with(self, variable, t2)
 
                _is_typed = true
        end
@@ -795,8 +819,7 @@ redef class AIfexprExpr
                v.enter_visit(n_else)
 
                # Merge 'then' and 'else' contexts
-               old_var_ctx.merge2(then_var_ctx, v.variable_ctx, v.base_variable_ctx)
-               v.variable_ctx = old_var_ctx
+               v.variable_ctx = old_var_ctx.merge_reash(self, then_var_ctx, v.variable_ctx, v.base_variable_ctx)
 
                var stype = v.check_conform_multiexpr(null, [n_then, n_else])
                if stype == null then return
@@ -1243,8 +1266,13 @@ redef class AAbsSendExpr
        private fun get_property(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, name: Symbol): nullable MMMethod
        do
                if type_recv isa MMTypeNone then
-                       v.error(self, "Error: Method '{name}' call on 'null'.")
-                       return null
+                       if name == (once "==".to_symbol) or name == (once "!=".to_symbol) then
+                               # Special case on != and == that are allowed for 'null'
+                               type_recv = v.type_object.as_nullable
+                       else
+                               v.error(self, "Error: Method '{name}' call on 'null'.")
+                               return null
+                       end
                end
                var lc = type_recv.local_class
                var prop: nullable MMMethod = null
@@ -1461,7 +1489,7 @@ redef class AEqExpr
        private fun try_to_isa(v: TypingVisitor, n: AExpr)
        do
                var variable = n.its_variable
-               if variable != null then
+               if variable != null and n.stype isa MMNullableType then
                        _if_false_variable_ctx = v.variable_ctx.sub_with(self, variable, n.stype.as_notnull)
                        _if_true_variable_ctx = v.variable_ctx.sub_with(self, variable, v.type_none)
                end
@@ -1492,7 +1520,7 @@ redef class ANeExpr
        private fun try_to_isa(v: TypingVisitor, n: AExpr)
        do
                var variable = n.its_variable
-               if variable != null then
+               if variable != null and n.stype isa MMNullableType then
                        _if_true_variable_ctx = v.variable_ctx.sub_with(self, variable, n.stype.as_notnull)
                        _if_false_variable_ctx = v.variable_ctx.sub_with(self, variable, v.type_none)
                end