X-Git-Url: http://nitlanguage.org diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index 8bbcda8..adc5736 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -136,9 +136,6 @@ special AbsSyntaxVisitor end end -redef class VarVariable - redef readable var _is_typed: Bool = false -end ############################################################################### @@ -287,8 +284,8 @@ 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 @@ -296,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 @@ -373,9 +369,10 @@ redef class AVardeclExpr end else if ne != null then if not v.check_expr(ne) then return - va.stype = n_expr.stype + va.stype = ne.stype + else + va.stype = v.type_object.as_nullable end - va._is_typed = true _is_typed = true end end @@ -383,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 @@ -444,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.") @@ -478,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 @@ -516,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 @@ -534,12 +541,15 @@ 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) 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) @@ -549,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 @@ -568,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 @@ -576,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 @@ -607,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 @@ -677,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 @@ -693,6 +719,10 @@ redef class AReassignFormExpr return null end var name = n_assign_op.method_name + if type_lvalue isa MMTypeNone then + v.error(self, "Error: Method '{name}' call on 'null'.") + return null + end var lc = type_lvalue.local_class if not lc.has_global_property_by_name(name) then v.error(self, "Error: Method '{name}' doesn't exists in {type_lvalue}.") @@ -725,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 @@ -774,15 +804,23 @@ 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 + 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 @@ -1031,6 +1069,10 @@ redef class AAttrFormExpr if not v.check_expr(n_expr) then return var type_recv = n_expr.stype var name = n_id.to_symbol + if type_recv isa MMTypeNone then + v.error(self, "Error: Attribute '{name}' access on 'null'.") + return + end var lc = type_recv.local_class if not lc.has_global_property_by_name(name) then v.error(self, "Error: Attribute {name} doesn't exists in {type_recv}.") @@ -1223,16 +1265,25 @@ 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 + 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 if lc.has_global_property_by_name(name) then prop = lc.select_method(name) if prop == null and v.local_property.global.is_init then - var props = type_recv.local_class.super_methods_named(name) + var props = lc.super_methods_named(name) if props.length > 1 then v.error(self, "Error: Ambigous method name '{name}' for {props.join(", ")}. Use explicit designation.") return null else if props.length == 1 then - var p = type_recv.local_class[props.first.global] + var p = lc[props.first.global] assert p isa MMMethod prop = p end @@ -1425,7 +1476,11 @@ redef class AEqExpr end if n_expr.stype isa MMTypeNone then - try_to_isa(v, n_expr2) + if n_expr2.stype isa MMTypeNone then + v.warning(self, "Warning: comparaison between 2 null values.") + else + try_to_isa(v, n_expr2) + end else if n_expr2.stype isa MMTypeNone then try_to_isa(v, n_expr) end @@ -1434,8 +1489,9 @@ 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 end end @@ -1451,7 +1507,11 @@ redef class ANeExpr end if n_expr.stype isa MMTypeNone then - try_to_isa(v, n_expr2) + if n_expr2.stype isa MMTypeNone then + v.warning(self, "Warning: comparaison between 2 null values.") + else + try_to_isa(v, n_expr2) + end else if n_expr2.stype isa MMTypeNone then try_to_isa(v, n_expr) end @@ -1460,8 +1520,9 @@ 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 end end @@ -1683,8 +1744,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 @@ -1704,6 +1765,9 @@ special AExpr v.warning(self, "Warning: Expression is already a {ttype}.") else if etype < ttype then v.warning(self, "Warning: Expression is already a {ttype} since it is a {etype}.") + else if etype isa MMTypeNone then + # ttype is not nullable because of prevous test + v.warning(self, "Warning: Expression is null therefore cannot be a {ttype}.") else if etype.is_nullable and etype.as_notnull == ttype then if ttype isa MMTypeFormal and ttype.bound.is_nullable then # No warning in this case since with