end
end
-redef class VarVariable
- redef readable var _is_typed: Bool = false
-end
###############################################################################
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
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
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
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
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.")
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
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
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)
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
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.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
# 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
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
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}.")
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
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
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}.")
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
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
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
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
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
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
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