syntax: untyped local variables are 'nullable Object'
[nit.git] / src / syntax / typing.nit
index 0f19f7f..97eb5f2 100644 (file)
@@ -271,7 +271,10 @@ redef class AClosureDecl
                v.base_variable_ctx = v.variable_ctx
                v.variable_ctx = v.variable_ctx.sub(self)
 
-               var escapable = new EscapableClosure(self, variable.closure, null)
+               var blist: nullable Array[AExpr] = null
+               var t = v.local_property.signature.return_type
+               if t != null then blist = new Array[AExpr]
+               var escapable = new EscapableClosure(self, variable.closure, blist)
                _escapable = escapable
                v.escapable_ctx.push(escapable, null)
 
@@ -281,11 +284,14 @@ redef class AClosureDecl
                        if v.variable_ctx.unreash == false then
                                if variable.closure.signature.return_type != null then
                                        v.error(self, "Control error: Reached end of block (a 'continue' with a value was expected).")
-                               else if variable.closure.is_break then
-                                       v.error(self, "Control error: Reached end of break block (an 'abort' was expected).")
+                               else if variable.closure.is_break and escapable.break_list != null then
+                                       v.error(self, "Control error: Reached end of break block (a 'break' with a value was expected).")
                                end
                        end
                end
+               if blist != null then for x in blist do
+                       v.check_conform_expr(x, t)
+               end
 
                old_var_ctx.merge(v.variable_ctx)
                v.variable_ctx = old_var_ctx
@@ -350,20 +356,23 @@ redef class AVardeclExpr
 
        redef fun after_typing(v)
        do
-               var va = new VarVariable(n_id.to_symbol, self)
+               var va = new VarVariable(n_id.to_symbol, n_id)
                _variable = va
                v.variable_ctx.add(va)
-               if n_expr != null then v.variable_ctx.mark_is_set(va)
+               var ne = n_expr
+               if ne != null then v.variable_ctx.mark_is_set(va)
 
                if n_type != null then
                        if not n_type.is_typed then return
                        va.stype = n_type.stype
-                       if n_expr != null then
-                               v.check_conform_expr(n_expr.as(not null), va.stype)
+                       if ne != null then
+                               v.check_conform_expr(ne, va.stype)
                        end
+               else if ne != null then
+                       if not v.check_expr(ne) then return
+                       va.stype = ne.stype
                else
-                       if not v.check_expr(n_expr.as(not null)) then return
-                       va.stype = n_expr.stype
+                       va.stype = v.type_object.as_nullable
                end
                _is_typed = true
        end
@@ -529,6 +538,10 @@ redef class AWhileExpr
                v.enter_visit(n_expr)
                v.check_conform_expr(n_expr, v.type_bool)
 
+               if n_expr isa ATrueExpr then
+                       v.warning(self, "Warning: use 'loop' instead of 'while true do'.")
+               end
+
                # Prepare inside context (assert cond)
                v.use_if_true_variable_ctx(n_expr)
 
@@ -545,6 +558,33 @@ redef class AWhileExpr
        end
 end
 
+redef class ALoopExpr
+       # The corresponding escapable block
+       readable var _escapable: nullable EscapableBlock
+
+       redef fun accept_typing(v)
+       do
+               var escapable = new EscapableBlock(self)
+               _escapable = escapable
+               v.escapable_ctx.push(escapable, n_label)
+               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
+                       v.variable_ctx = v.variable_ctx.sub(n_block.as(not null))
+                       v.enter_visit(n_block)
+               end
+
+               v.variable_ctx = old_var_ctx
+               v.base_variable_ctx = old_base_var_ctx
+               v.escapable_ctx.pop
+               _is_typed = true
+       end
+end
+
 redef class AForExpr
        var _variable: nullable AutoVariable
        redef fun variable do return _variable.as(not null)
@@ -562,7 +602,7 @@ redef class AForExpr
                var old_base_var_ctx = v.base_variable_ctx
                v.base_variable_ctx = v.variable_ctx
                v.variable_ctx = v.variable_ctx.sub(self)
-               var va = new AutoVariable(n_id.to_symbol, self)
+               var va = new AutoVariable(n_id.to_symbol, n_id)
                _variable = va
                v.variable_ctx.add(va)
 
@@ -592,9 +632,21 @@ redef class AForExpr
 end
 
 redef class AAssertExpr
-       redef fun after_typing(v)
+       redef fun accept_typing(v)
        do
+               # Process condition
+               v.enter_visit(n_expr)
                v.check_conform_expr(n_expr, v.type_bool)
+
+               # Process optional 'else' part
+               if n_else != null then
+                       var old_var_ctx = v.variable_ctx
+                       v.use_if_false_variable_ctx(n_expr)
+                       v.enter_visit(n_else)
+                       v.variable_ctx = old_var_ctx
+               end
+
+               # Prepare outside
                v.use_if_true_variable_ctx(n_expr)
                _is_typed = true
        end
@@ -620,11 +672,11 @@ redef class AVarAssignExpr
        redef fun after_typing(v)
        do
                v.variable_ctx.mark_is_set(variable)
-               var t = v.variable_ctx.stype(variable)
 
                # Check the base type
                var btype = v.base_variable_ctx.stype(variable)
-               if not v.check_conform_expr(n_value, btype) then return
+               if not v.check_expr(n_value) then return
+               if btype != null and not v.check_conform_expr(n_value, btype) then return
 
                # Always cast
                v.variable_ctx.stype(variable) = n_value.stype
@@ -671,7 +723,8 @@ redef class AVarReassignExpr
 
                # Check the base type
                var btype = v.base_variable_ctx.stype(variable)
-               if not v.check_conform(n_value, t2, btype) then return
+               if not v.check_expr(n_value) then return
+               if btype != null and not v.check_conform(n_value, t2, btype) then return
 
                # Always cast
                v.variable_ctx.stype(variable) = t2
@@ -723,15 +776,24 @@ redef class AIfexprExpr
                v.use_if_true_variable_ctx(n_expr)
 
                # Process 'then'
+               v.variable_ctx = v.variable_ctx.sub(n_then)
                v.enter_visit(n_then)
 
+               # Remember what appened in the 'then'
+               var then_var_ctx = v.variable_ctx
+
                # Prepare 'else' context
                v.variable_ctx = old_var_ctx
                v.use_if_false_variable_ctx(n_expr)
 
                # Process 'else'
+               v.variable_ctx = v.variable_ctx.sub(n_else)
                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
+
                var stype = v.check_conform_multiexpr(null, [n_then, n_else])
                if stype == null then return
 
@@ -1124,12 +1186,23 @@ redef class AAbsAbsSendExpr
 
                                # Process each closure definition
                                for i in [0..arity[ do
-                                       var csi = cs[i]
                                        var cdi = cd[i]
-                                       var esc = new EscapableClosure(cdi, csi, break_list)
-                                       v.escapable_ctx.push(esc, n_label)
-                                       cdi.accept_typing2(v, esc)
-                                       v.escapable_ctx.pop
+                                       var cni = cdi.n_id.to_symbol
+                                       var csi = psig.closure_named(cni)
+                                       if csi != null then
+                                               var esc = new EscapableClosure(cdi, csi, break_list)
+                                               v.escapable_ctx.push(esc, n_label)
+                                               cdi.accept_typing2(v, esc)
+                                               v.escapable_ctx.pop
+                                       else if cs.length == 1 then
+                                               v.error(cdi.n_id, "Error: no closure named '!{cni}' in {name}; only closure is !{cs.first.name}.")
+                                       else
+                                               var a = new Array[String]
+                                               for c in cs do
+                                                       a.add("!{c.name}")
+                                               end
+                                               v.error(cdi.n_id, "Error: no closure named '!{cni}' in {name}; only closures are {a.join(",")}.")
+                                       end
                                end
 
                                # Check break type conformity
@@ -1567,6 +1640,16 @@ redef class AClosureCallExpr
        end
 end
 
+redef class AClosureId
+       fun to_symbol: Symbol is abstract
+end
+redef class ASimpleClosureId
+       redef fun to_symbol: Symbol do return n_id.to_symbol
+end
+redef class ABreakClosureId
+       redef fun to_symbol: Symbol do return n_kwbreak.to_symbol
+end
+
 redef class AClosureDef
        var _closure: nullable MMClosure
        redef fun closure do return _closure.as(not null)
@@ -1599,7 +1682,7 @@ redef class AClosureDef
                v.variable_ctx = v.variable_ctx.sub(self)
                variables = new Array[AutoVariable]
                for i in [0..n_ids.length[ do
-                       var va = new AutoVariable(n_ids[i].to_symbol, self)
+                       var va = new AutoVariable(n_ids[i].to_symbol, n_ids[i])
                        variables.add(va)
                        va.stype = sig[i]
                        v.variable_ctx.add(va)
@@ -1611,8 +1694,8 @@ redef class AClosureDef
                if v.variable_ctx.unreash == false then
                        if closure.signature.return_type != null then
                                v.error(self, "Control error: Reached end of block (a 'continue' with a value was expected).")
-                       else if closure.is_break then
-                               v.error(self, "Control error: Reached end of break block (a 'break' was expected).")
+                       else if closure.is_break and esc.break_list != null then
+                               v.error(self, "Control error: Reached end of break block (a 'break' with a value was expected).")
                        end
                end
                v.variable_ctx = old_var_ctx