X-Git-Url: http://nitlanguage.org diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index fad4baa..34dc5f1 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -18,8 +18,8 @@ package typing import syntax_base -import escape -import control_flow +import flow +import scope redef class MMSrcModule # Walk trough the module and type statments and expressions @@ -27,7 +27,7 @@ redef class MMSrcModule fun do_typing(tc: ToolContext) do var tv = new TypingVisitor(tc, self) - tv.visit(node) + tv.enter_visit(node) end end @@ -43,23 +43,45 @@ special AbsSyntaxVisitor if n != null then n.accept_typing(self) end - # Current knowledge about variables names and types - fun variable_ctx: VariableContext do return _variable_ctx.as(not null) - writable var _variable_ctx: nullable VariableContext + # Current knowledge about scoped things (variable, labels, etc.) + readable var _scope_ctx: ScopeContext = new ScopeContext(self) - # Non-bypassable knowledge about variables names and types - fun base_variable_ctx: VariableContext do return _base_variable_ctx.as(not null) - writable var _base_variable_ctx: nullable VariableContext + # Current knowledge about control flow + fun flow_ctx: FlowContext do return _flow_ctx.as(not null) + writable var _flow_ctx: nullable FlowContext + + # Mark a local variable as set + fun mark_is_set(va: Variable) + do + if flow_ctx.is_set(va) then return + flow_ctx = flow_ctx.sub_setvariable(va) + end - # Current knowledge about escapable blocks - readable writable var _escapable_ctx: EscapableContext = new EscapableContext(self) + # Mark the flow context as unreashable + fun mark_unreash(n: ANode) + do + flow_ctx = flow_ctx.sub_unreash(n) + end + + # Enter in an expression as inside a new local variable scope + fun enter_visit_block(node: nullable AExpr) + do + if node == null then return + scope_ctx.push(node) + enter_visit(node) + scope_ctx.pop + end + + # Non-bypassable knowledge about variables names and types + fun base_flow_ctx: FlowContext do return _base_flow_ctx.as(not null) + writable var _base_flow_ctx: nullable FlowContext # The current reciever fun self_var: ParamVariable do return _self_var.as(not null) writable var _self_var: nullable ParamVariable # Block of the current method - readable writable var _top_block: nullable PExpr + readable writable var _top_block: nullable AExpr # List of explicit invocation of constructors of super-classes readable writable var _explicit_super_init_calls: nullable Array[MMMethod] @@ -67,26 +89,29 @@ special AbsSyntaxVisitor # Is a other constructor of the same class invoked readable writable var _explicit_other_init_call: Bool = false - # Make the if_true_variable_ctx of the expression effective - private fun use_if_true_variable_ctx(e: PExpr) + # Make the if_true_flow_ctx of the expression effective + private fun use_if_true_flow_ctx(e: AExpr) do - var ctx = e.if_true_variable_ctx - if ctx != null then variable_ctx = ctx + var ctx = e.if_true_flow_ctx + if ctx != null then flow_ctx = ctx end - # Make the if_false_variable_ctx of the expression effective - private fun use_if_false_variable_ctx(e: PExpr) + # Make the if_false_flow_ctx of the expression effective + private fun use_if_false_flow_ctx(e: AExpr) do - var ctx = e.if_false_variable_ctx - if ctx != null then variable_ctx = ctx + var ctx = e.if_false_flow_ctx + if ctx != null then flow_ctx = ctx end + # Are we inside a default closure definition ? + readable writable var _is_default_closure_definition: Bool = false + # Number of nested once readable writable var _once_count: Int = 0 - init(tc, module) do super + init(tc, mod) do super - private fun get_default_constructor_for(n: PNode, c: MMLocalClass, prop: MMSrcMethod): nullable MMMethod + private fun get_default_constructor_for(n: ANode, c: MMLocalClass, prop: MMSrcMethod): nullable MMMethod do var v = self #var prop = v.local_property @@ -118,14 +143,14 @@ special AbsSyntaxVisitor else if candidates.length > 0 then var a = new Array[String] for p in candidates do - a.add("{p.full_name}{p.signature}") + a.add("{p.full_name}{p.signature.as(not null)}") end v.error(n, "Error: Conflicting default constructor to call for {c}: {a.join(", ")}.") return null else if false_candidates.length > 0 then var a = new Array[String] for p in false_candidates do - a.add("{p.full_name}{p.signature}") + a.add("{p.full_name}{p.signature.as(not null)}") end v.error(n, "Error: there is no available compatible constrctor in {c}. Discarded candidates are {a.join(", ")}.") return null @@ -139,7 +164,7 @@ end ############################################################################### -redef class PNode +redef class ANode private fun accept_typing(v: TypingVisitor) do accept_abs_syntax_visitor(v) @@ -148,40 +173,46 @@ redef class PNode private fun after_typing(v: TypingVisitor) do end end -redef class PClassdef +redef class AClassdef redef fun accept_typing(v) do - v.variable_ctx = new RootVariableContext(v, self) - v.base_variable_ctx = v.variable_ctx v.self_var = new ParamVariable("self".to_symbol, self) v.self_var.stype = local_class.get_type super end end +redef class APropdef + redef fun self_var do return _self_var.as(not null) + var _self_var: nullable ParamVariable +end + redef class AAttrPropdef redef fun accept_typing(v) do - var old_var_ctx = v.variable_ctx - v.variable_ctx = old_var_ctx.sub(self) + v.flow_ctx = new RootFlowContext(v, self) + v.base_flow_ctx = v.flow_ctx + + v.scope_ctx.push(self) + _self_var = v.self_var super if n_expr != null then v.check_conform_expr(n_expr.as(not null), prop.signature.return_type.as(not null)) end - v.variable_ctx = old_var_ctx + v.scope_ctx.pop end end redef class AMethPropdef - redef fun self_var do return _self_var.as(not null) - var _self_var: nullable ParamVariable redef fun accept_typing(v) do - var old_var_ctx = v.variable_ctx - v.variable_ctx = old_var_ctx.sub(self) + v.flow_ctx = new RootFlowContext(v, self) + v.base_flow_ctx = v.flow_ctx + + v.scope_ctx.push(self) _self_var = v.self_var super - v.variable_ctx = old_var_ctx + v.scope_ctx.pop end end @@ -189,21 +220,24 @@ redef class AConcreteMethPropdef redef fun after_typing(v) do super - if v.variable_ctx.unreash == false and method.signature.return_type != null then + if not v.flow_ctx.unreash and method.signature.return_type != null then v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).") end end end redef class AConcreteInitPropdef - readable var _super_init_calls: Array[MMMethod] = new Array[MMMethod] - readable var _explicit_super_init_calls: Array[MMMethod] = new Array[MMMethod] redef fun accept_typing(v) do v.top_block = n_block v.explicit_super_init_calls = explicit_super_init_calls v.explicit_other_init_call = false super + end + + redef fun after_typing(v) + do + super if v.explicit_other_init_call or method.global.intro != method then # TODO: something? else @@ -213,7 +247,7 @@ redef class AConcreteInitPropdef var cur_c: nullable MMLocalClass = null if i < l then cur_m = explicit_super_init_calls[i] - cur_c = cur_m.global.intro.local_class.for_module(v.module) + cur_c = cur_m.global.intro.local_class.for_module(v.mmmodule) end var j = 0 while j < v.local_class.cshe.direct_greaters.length do @@ -226,7 +260,7 @@ redef class AConcreteInitPropdef i += 1 if i < l then cur_m = explicit_super_init_calls[i] - cur_c = cur_m.global.intro.local_class.for_module(v.module) + cur_c = cur_m.global.intro.local_class.for_module(v.mmmodule) else cur_m = null cur_c = null @@ -243,10 +277,10 @@ redef class AConcreteInitPropdef end end -redef class PParam +redef class AParam redef fun after_typing(v) do - v.variable_ctx.add(variable) + v.scope_ctx.add_variable(variable) end end @@ -257,39 +291,47 @@ redef class AClosureDecl redef fun accept_typing(v) do # Register the closure for ClosureCallExpr - v.variable_ctx.add(variable) + v.scope_ctx.add_variable(variable) - 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) + var old_flow_ctx = v.flow_ctx + var old_base_flow_ctx = v.base_flow_ctx + v.base_flow_ctx = v.flow_ctx - 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) + v.scope_ctx.push_escapable(escapable, null) + + v.is_default_closure_definition = true super + v.is_default_closure_definition = false + if n_expr != null then - if v.variable_ctx.unreash == false then + if v.flow_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 - v.base_variable_ctx = old_base_var_ctx - v.escapable_ctx.pop + v.flow_ctx = old_flow_ctx + v.base_flow_ctx = old_base_flow_ctx + v.scope_ctx.pop end end -redef class PType - fun stype: MMType do return _stype.as(not null) - fun is_typed: Bool do return _stype != null +redef class AType + redef fun stype: MMType do return _stype.as(not null) + redef fun is_typed: Bool do return _stype != null var _stype: nullable MMType redef fun after_typing(v) @@ -298,23 +340,29 @@ redef class PType end end -redef class PExpr +redef class AExpr redef readable var _is_typed: Bool = false redef fun is_statement: Bool do return _stype == null redef fun stype do if not is_typed then - print "{locate}: not is_typed" + print "{location}: not is_typed" abort end if is_statement then - print "{locate}: is_statement" + print "{location}: is_statement" abort end return _stype.as(not null) end var _stype: nullable MMType + redef fun after_typing(v) + do + # Default behavior is to be happy + _is_typed = true + end + # Is the expression the implicit receiver fun is_implicit_self: Bool do return false @@ -324,11 +372,11 @@ redef class PExpr # The variable accessed is any fun its_variable: nullable Variable do return null - # The variable type information if current boolean expression is true - readable private var _if_true_variable_ctx: nullable VariableContext + # The control flow information if current boolean expression is true + readable private var _if_true_flow_ctx: nullable FlowContext - # The variable type information if current boolean expression is false - readable private var _if_false_variable_ctx: nullable VariableContext + # The control flow information if current boolean expression is false + readable private var _if_false_flow_ctx: nullable FlowContext end redef class AVardeclExpr @@ -337,20 +385,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) + v.scope_ctx.add_variable(va) + var ne = n_expr + if ne != null then v.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 @@ -359,19 +410,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 - v.variable_ctx.already_unreash = true - v.warning(e, "Warning: unreachable statement.") + if not v.flow_ctx.unreash then + v.enter_visit(e) + else if not v.flow_ctx.already_unreash then + v.flow_ctx.already_unreash = true + v.error(e, "Error: unreachable statement.") end - v.visit(e) end - old_var_ctx.merge(v.variable_ctx) - v.variable_ctx = old_var_ctx _is_typed = true end end @@ -379,8 +426,14 @@ end redef class AReturnExpr redef fun after_typing(v) do - v.variable_ctx.unreash = true + v.mark_unreash(self) var t = v.local_property.signature.return_type + + if v.is_default_closure_definition then + v.error(self, "Error: 'return' invalid in default closure definitions. Use 'continue' or 'break'.") + return + end + var e = n_expr if e == null and t != null then v.error(self, "Error: Return without value in a function.") @@ -396,12 +449,12 @@ end redef class AContinueExpr redef fun after_typing(v) do - v.variable_ctx.unreash = true - var esc = compute_escapable_block(v.escapable_ctx) + v.mark_unreash(self) + var esc = compute_escapable_block(v.scope_ctx) if esc == null then return if esc.is_break_block then - v.error(self, "Error: 'continue' forbiden in break blocks.") + v.error(self, "Error: cannot 'continue', only 'break'.") return end @@ -420,10 +473,13 @@ end redef class ABreakExpr redef fun after_typing(v) do - v.variable_ctx.unreash = true - var esc = compute_escapable_block(v.escapable_ctx) + var old_flow_ctx = v.flow_ctx + v.mark_unreash(self) + var esc = compute_escapable_block(v.scope_ctx) if esc == null then return + esc.break_flow_contexts.add(old_flow_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.") @@ -440,154 +496,205 @@ end redef class AAbortExpr redef fun after_typing(v) do - v.variable_ctx.unreash = true + v.mark_unreash(self) + _is_typed = true + end +end + +# An abstract control structure with feature escapable block +class AAbsControl +special AExpr + # The corresponding escapable block + readable var _escapable: nullable EscapableBlock + + # Enter and process a control structure + private fun process_control(v: TypingVisitor, escapable: EscapableBlock, n_label: nullable ALabel, is_loop: Bool) + do + # Register the escapable block + _escapable = escapable + v.scope_ctx.push_escapable(escapable, n_label) + + # Save an prepare the contextes + var old_flow_ctx = v.flow_ctx + var old_base_flow_ctx = v.base_flow_ctx + if is_loop then v.base_flow_ctx = v.flow_ctx + + # Do the main processing + process_control_inside(v) + + # Add the end of the block as an exit context + if not v.flow_ctx.unreash then + escapable.break_flow_contexts.add(v.flow_ctx) + end + + # Merge all exit contexts + if escapable.break_flow_contexts.is_empty then + v.flow_ctx = old_flow_ctx + v.mark_unreash(self) + else + v.flow_ctx = old_base_flow_ctx.merge(self, escapable.break_flow_contexts) + end + + if is_loop then v.base_flow_ctx = old_base_flow_ctx + v.scope_ctx.pop + _is_typed = true + end + + # What to do inside the control block? + private fun process_control_inside(v: TypingVisitor) is abstract +end + +redef class ADoExpr +special AAbsControl + redef fun accept_typing(v) + do + process_control(v, new BreakOnlyEscapableBlock(self), n_label, false) + end + + redef fun process_control_inside(v) + do + v.enter_visit_block(n_block) end end redef class AIfExpr redef fun accept_typing(v) do - var old_var_ctx = v.variable_ctx - v.visit(n_expr) + v.enter_visit(n_expr) v.check_conform_expr(n_expr, v.type_bool) # Prepare 'then' context - v.use_if_true_variable_ctx(n_expr) + var old_flow_ctx = v.flow_ctx + v.use_if_true_flow_ctx(n_expr) # Process the 'then' - if n_then != null then - v.variable_ctx = v.variable_ctx.sub(n_then.as(not null)) - v.visit(n_then) - end + v.enter_visit_block(n_then) # Remember what appened in the 'then' - var then_var_ctx = v.variable_ctx + var then_flow_ctx = v.flow_ctx # Prepare 'else' context - v.variable_ctx = old_var_ctx - v.use_if_false_variable_ctx(n_expr) + v.flow_ctx = old_flow_ctx + v.use_if_false_flow_ctx(n_expr) # Process the 'else' - if n_else != null then - v.variable_ctx = v.variable_ctx.sub(n_else.as(not null)) - v.visit(n_else) - end + v.enter_visit_block(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.flow_ctx = v.base_flow_ctx.merge_reash(self, then_flow_ctx, v.flow_ctx) _is_typed = true end end redef class AWhileExpr - # The corresponding escapable block - readable var _escapable: nullable EscapableBlock - +special AAbsControl redef fun accept_typing(v) do - var escapable = new EscapableBlock(self) - _escapable = escapable - v.escapable_ctx.push(escapable) - 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_control(v, new EscapableBlock(self), n_label, true) + end + + redef fun process_control_inside(v) + do + var old_flow_ctx = v.flow_ctx # Process condition - v.visit(n_expr) + 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.use_if_true_flow_ctx(n_expr) # Process inside - if n_block != null then - v.variable_ctx = v.variable_ctx.sub(n_block.as(not null)) - v.visit(n_block) - end + v.enter_visit_block(n_block) - v.variable_ctx = old_var_ctx - v.base_variable_ctx = old_base_var_ctx - v.escapable_ctx.pop - _is_typed = true + # Compute outside context (assert !cond + all breaks) + v.flow_ctx = old_flow_ctx + v.use_if_false_flow_ctx(n_expr) + escapable.break_flow_contexts.add(v.flow_ctx) + end +end + +redef class ALoopExpr +special AAbsControl + redef fun accept_typing(v) + do + process_control(v, new EscapableBlock(self), n_label, true) + end + + redef fun process_control_inside(v) + do + # Process inside + v.enter_visit_block(n_block) + + # Never automatically reach after the loop + v.mark_unreash(self) end end redef class AForExpr +special AAbsControl var _variable: nullable AutoVariable redef fun variable do return _variable.as(not null) - # The corresponding escapable block - readable var _escapable: nullable EscapableBlock - - var _meth_iterator: nullable MMMethod - fun meth_iterator: MMMethod do return _meth_iterator.as(not null) - var _meth_is_ok: nullable MMMethod - fun meth_is_ok: MMMethod do return _meth_is_ok.as(not null) - var _meth_item: nullable MMMethod - fun meth_item: MMMethod do return _meth_item.as(not null) - var _meth_next: nullable MMMethod - fun meth_next: MMMethod do return _meth_next.as(not null) redef fun accept_typing(v) do - var escapable = new EscapableBlock(self) - _escapable = escapable - v.escapable_ctx.push(escapable) + process_control(v, new EscapableBlock(self), n_label, true) + end + + redef fun process_control_inside(v) + do + v.scope_ctx.push(self) + var old_flow_ctx = v.flow_ctx - 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) - var va = new AutoVariable(n_id.to_symbol, self) + # Create the automatic variable + var va = new AutoVariable(n_id.to_symbol, n_id) _variable = va - v.variable_ctx.add(va) + v.scope_ctx.add_variable(va) - v.visit(n_expr) + # Process collection + v.enter_visit(n_expr) if not v.check_conform_expr(n_expr, v.type_collection) then return var expr_type = n_expr.stype - _meth_iterator = expr_type.local_class.select_method(once ("iterator".to_symbol)) - if _meth_iterator == null then - v.error(self, "Error: Collection MUST have an iterate method") - return - end - var iter_type = _meth_iterator.signature_for(expr_type).return_type.as(not null) - _meth_is_ok = iter_type.local_class.select_method(once ("is_ok".to_symbol)) - if _meth_is_ok == null then - v.error(self, "Error: {iter_type} MUST have an is_ok method") - return - end - _meth_item = iter_type.local_class.select_method(once ("item".to_symbol)) - if _meth_item == null then - v.error(self, "Error: {iter_type} MUST have an item method") - return - end - _meth_next = iter_type.local_class.select_method(once ("next".to_symbol)) - if _meth_next == null then - v.error(self, "Error: {iter_type} MUST have a next method") - return - end - var t = _meth_item.signature_for(iter_type).return_type - if not n_expr.is_self then t = t.not_for_self - va.stype = t + # Get iterator + var meth_iterator = v.get_method(expr_type, once "iterator".to_symbol) + var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null) + var meth_item = v.get_method(iter_type, once ("item".to_symbol)) + var va_stype = meth_item.signature_for(iter_type).return_type.as(not null) + if not n_expr.is_self then va_stype = va_stype.not_for_self + va.stype = va_stype - if n_block != null then v.visit(n_block) + # Process inside + v.enter_visit_block(n_block) - # pop context - v.variable_ctx = old_var_ctx - v.base_variable_ctx = old_base_var_ctx - v.escapable_ctx.pop - _is_typed = true + # end == begin of the loop + v.flow_ctx = old_flow_ctx + v.scope_ctx.pop end 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) - v.use_if_true_variable_ctx(n_expr) + + # Process optional 'else' part + if n_else != null then + var old_flow_ctx = v.flow_ctx + v.use_if_false_flow_ctx(n_expr) + v.enter_visit(n_else) + v.flow_ctx = old_flow_ctx + end + + # Prepare outside + v.use_if_true_flow_ctx(n_expr) _is_typed = true end end @@ -602,8 +709,8 @@ redef class AVarExpr redef fun after_typing(v) do - v.variable_ctx.check_is_set(self, variable) - _stype = v.variable_ctx.stype(variable) + v.flow_ctx.check_is_set(self, variable) + _stype = v.flow_ctx.stype(variable) _is_typed = _stype != null end end @@ -611,15 +718,15 @@ end redef class AVarAssignExpr redef fun after_typing(v) do - v.variable_ctx.mark_is_set(variable) - var t = v.variable_ctx.stype(variable) + v.mark_is_set(variable) # Check the base type - var btype = v.base_variable_ctx.stype(variable) - if not v.check_conform_expr(n_value, btype) then return + var btype = v.base_flow_ctx.stype(variable) + 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 + v.flow_ctx = v.flow_ctx.sub_with(self, variable, n_value.stype) _is_typed = true end @@ -635,44 +742,49 @@ 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}.") return null end var prop = lc.select_method(name) - prop.global.check_visibility(v, self, v.module, false) + prop.global.check_visibility(v, self, v.mmmodule, false) var psig = prop.signature_for(type_lvalue) _assign_method = prop if not v.check_conform_expr(n_value, psig[0].not_for_self) then return null return psig.return_type.not_for_self end - # Method used through the reassigment operator (once computed) - readable var _assign_method: nullable MMMethod + redef fun assign_method do return _assign_method.as(not null) + var _assign_method: nullable MMMethod end redef class AVarReassignExpr redef fun after_typing(v) do - v.variable_ctx.check_is_set(self, variable) - v.variable_ctx.mark_is_set(variable) - var t = v.variable_ctx.stype(variable) + v.flow_ctx.check_is_set(self, variable) + v.mark_is_set(variable) + var t = v.flow_ctx.stype(variable) var t2 = do_rvalue_typing(v, t) if t2 == null then return # Check the base type - var btype = v.base_variable_ctx.stype(variable) - if not v.check_conform(n_value, t2, btype) then return + var btype = v.base_flow_ctx.stype(variable) + 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 + v.flow_ctx = v.flow_ctx.sub_with(self, variable, t2) _is_typed = true end end -redef class PAssignOp +redef class AAssignOp fun method_name: Symbol is abstract end redef class APlusAssignOp @@ -691,7 +803,7 @@ redef class ASelfExpr redef fun after_typing(v) do _variable = v.self_var - _stype = v.variable_ctx.stype(variable) + _stype = v.flow_ctx.stype(variable) _is_typed = true end @@ -705,19 +817,36 @@ end redef class AIfexprExpr redef fun accept_typing(v) do - var old_var_ctx = v.variable_ctx - - v.visit(n_expr) - v.use_if_true_variable_ctx(n_expr) - v.visit(n_then) - v.variable_ctx = old_var_ctx - v.use_if_false_variable_ctx(n_expr) - v.visit(n_else) + var old_flow_ctx = v.flow_ctx + # Process condition + v.enter_visit(n_expr) v.check_conform_expr(n_expr, v.type_bool) - _stype = v.check_conform_multiexpr(null, [n_then, n_else]) - _is_typed = _stype != null + # Prepare 'then' context + v.use_if_true_flow_ctx(n_expr) + + # Process 'then' + v.enter_visit_block(n_then) + + # Remember what appened in the 'then' + var then_flow_ctx = v.flow_ctx + + # Prepare 'else' context + v.flow_ctx = old_flow_ctx + v.use_if_false_flow_ctx(n_expr) + + # Process 'else' + v.enter_visit_block(n_else) + + # Merge 'then' and 'else' contexts + v.flow_ctx = v.base_flow_ctx.merge_reash(self, then_flow_ctx, v.flow_ctx) + + var stype = v.check_conform_multiexpr(null, [n_then, n_else]) + if stype == null then return + + _stype = stype + _is_typed = true end end @@ -732,23 +861,29 @@ end redef class AOrExpr redef fun accept_typing(v) do - var old_var_ctx = v.variable_ctx + var old_flow_ctx = v.flow_ctx + var stype = v.type_bool + _stype = stype - v.visit(n_expr) - v.use_if_false_variable_ctx(n_expr) + # Process left operand + v.enter_visit(n_expr) - v.visit(n_expr2) - if n_expr2.if_false_variable_ctx != null then - _if_false_variable_ctx = n_expr2.if_false_variable_ctx + # Prepare right operand context + v.use_if_false_flow_ctx(n_expr) + + # Process right operand + v.enter_visit(n_expr2) + if n_expr2.if_false_flow_ctx != null then + _if_false_flow_ctx = n_expr2.if_false_flow_ctx else - _if_false_variable_ctx = v.variable_ctx + _if_false_flow_ctx = v.flow_ctx end - v.variable_ctx = old_var_ctx + v.flow_ctx = old_flow_ctx - v.check_conform_expr(n_expr, v.type_bool) - v.check_conform_expr(n_expr2, v.type_bool) - _stype = v.type_bool + v.check_conform_expr(n_expr, stype) + v.check_conform_expr(n_expr2, stype) + _stype = stype _is_typed = true end end @@ -756,23 +891,28 @@ end redef class AAndExpr redef fun accept_typing(v) do - var old_var_ctx = v.variable_ctx + var old_flow_ctx = v.flow_ctx + var stype = v.type_bool - v.visit(n_expr) - v.use_if_true_variable_ctx(n_expr) + # Process left operand + v.enter_visit(n_expr) - v.visit(n_expr2) - if n_expr2.if_true_variable_ctx != null then - _if_true_variable_ctx = n_expr2.if_true_variable_ctx + # Prepare right operand context + v.use_if_true_flow_ctx(n_expr) + + # Process right operand + v.enter_visit(n_expr2) + if n_expr2.if_true_flow_ctx != null then + _if_true_flow_ctx = n_expr2.if_true_flow_ctx else - _if_true_variable_ctx = v.variable_ctx + _if_true_flow_ctx = v.flow_ctx end - v.variable_ctx = old_var_ctx + v.flow_ctx = old_flow_ctx - v.check_conform_expr(n_expr, v.type_bool) - v.check_conform_expr(n_expr2, v.type_bool) - _stype = v.type_bool + v.check_conform_expr(n_expr, stype) + v.check_conform_expr(n_expr2, stype) + _stype = stype _is_typed = true end end @@ -783,14 +923,53 @@ redef class ANotExpr v.check_conform_expr(n_expr, v.type_bool) # Invert if_true/if_false information - _if_false_variable_ctx = n_expr._if_true_variable_ctx - _if_true_variable_ctx = n_expr._if_false_variable_ctx + _if_false_flow_ctx = n_expr._if_true_flow_ctx + _if_true_flow_ctx = n_expr._if_false_flow_ctx _stype = v.type_bool _is_typed = true end end +redef class AOrElseExpr + redef fun after_typing(v) + do + var old_flow_ctx = v.flow_ctx + + # Process left operand + v.enter_visit(n_expr) + v.check_expr(n_expr) + + # Consider the type of the left operand + var t = n_expr.stype + if not t.is_nullable then + v.warning(n_expr, "Warning: left operant of a 'or else' is not a nullable type.") + else + t = t.as_notnull + end + + # Prepare the else context : ie the first expression is null + var variable = n_expr.its_variable + if variable != null then + v.flow_ctx.sub_with(self, variable, v.type_none) + end + + # Process right operand + v.enter_visit(n_expr2) + v.check_expr(n_expr) + + # Restore the context + v.flow_ctx = old_flow_ctx + + # Merge the types + var stype = v.check_conform_multiexpr(t, [n_expr2]) + if stype == null then return + + _stype = stype + _is_typed = true + end +end + redef class AIntExpr redef fun after_typing(v) do @@ -816,37 +995,24 @@ redef class ACharExpr end redef class AStringFormExpr - var _meth_with_native: nullable MMMethod - fun meth_with_native: MMMethod do return _meth_with_native.as(not null) redef fun after_typing(v) do _stype = v.type_string _is_typed = true - _meth_with_native = _stype.local_class.select_method(once "with_native".to_symbol) - if _meth_with_native == null then v.error(self, "{_stype} MUST have a with_native method.") end end redef class ASuperstringExpr - fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null) - var _meth_with_capacity: nullable MMMethod - fun meth_add: MMMethod do return _meth_add.as(not null) - var _meth_add: nullable MMMethod - fun meth_to_s: MMMethod do return _meth_to_s.as(not null) - var _meth_to_s: nullable MMMethod - readable var _atype: nullable MMType + redef fun atype do return _atype.as(not null) + var _atype: nullable MMType redef fun after_typing(v) do + var otype = v.type_object var stype = v.type_string _stype = stype + for e in n_exprs do v.check_conform_expr(e, otype) var atype = v.type_array(stype) _atype = atype - _meth_with_capacity = atype.local_class.select_method(once "with_capacity".to_symbol) - if _meth_with_capacity == null then v.error(self, "{_atype} MUST have a with_capacity method.") - _meth_add = atype.local_class.select_method(once "add".to_symbol) - if _meth_add == null then v.error(self, "{_atype} MUST have an add method.") - _meth_to_s = v.type_object.local_class.select_method(once "to_s".to_symbol) - if _meth_to_s == null then v.error(self, "Object MUST have a to_s method.") _is_typed = true end end @@ -860,11 +1026,6 @@ redef class ANullExpr end redef class AArrayExpr - fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null) - var _meth_with_capacity: nullable MMMethod - fun meth_add: MMMethod do return _meth_add.as(not null) - var _meth_add: nullable MMMethod - redef fun after_typing(v) do var stype = v.check_conform_multiexpr(null, n_exprs) @@ -874,19 +1035,11 @@ redef class AArrayExpr private fun do_typing(v: TypingVisitor, element_type: MMType) do _stype = v.type_array(element_type) - - _meth_with_capacity = _stype.local_class.select_method(once "with_capacity".to_symbol) - if _meth_with_capacity == null then v.error(self, "{_stype} MUST have a with_capacity method.") - _meth_add = _stype.local_class.select_method(once "add".to_symbol) - if _meth_add == null then v.error(self, "{_stype} MUST have an add method.") - _is_typed = true end end redef class ARangeExpr - fun meth_init: MMMethod do return _meth_init.as(not null) - var _meth_init: nullable MMMethod redef fun after_typing(v) do if not v.check_expr(n_expr) or not v.check_expr(n_expr2) then return @@ -905,28 +1058,9 @@ redef class ARangeExpr end end -redef class ACrangeExpr - redef fun after_typing(v) - do - super - if not is_typed then return - _meth_init = stype.local_class.select_method(once "init".to_symbol) - end -end -redef class AOrangeExpr - redef fun after_typing(v) - do - super - if not is_typed then return - _meth_init = stype.local_class.select_method(once "without_last".to_symbol) - end -end - - redef class ASuperExpr -special ASuperInitCall - # readable var _prop: MMSrcMethod - readable var _init_in_superclass: nullable MMMethod + redef readable var _init_in_superclass: nullable MMMethod + redef fun compute_raw_arguments do return n_args.to_a redef fun after_typing(v) do var precs: Array[MMLocalProperty] = v.local_property.prhe.direct_greaters @@ -954,7 +1088,7 @@ special ASuperInitCall register_super_init_call(v, p) if n_args.length > 0 then var signature = get_signature(v, v.self_var.stype.as(not null), p, true) - _arguments = process_signature(v, signature, p.name, n_args.to_a) + process_signature(v, signature, p.name, compute_raw_arguments) end else v.error(self, "Error: No super method to call for {v.local_property}.") @@ -966,7 +1100,7 @@ special ASuperInitCall var stype: nullable MMType = null for prop in precs do assert prop isa MMMethod - var t = prop.signature_for(v.self_var.stype.as(not null)).return_type.for_module(v.module).adapt_to(v.local_property.signature.recv) + var t = prop.signature_for(v.self_var.stype.as(not null)).return_type.for_module(v.mmmodule).adapt_to(v.local_property.signature.recv) stypes.add(t) if stype == null or stype < t then stype = t @@ -985,11 +1119,11 @@ special ASuperInitCall end redef class AAttrFormExpr - # Attribute accessed - readable var _prop: nullable MMAttribute + redef fun prop do return _prop.as(not null) + var _prop: nullable MMAttribute - # Attribute type of the acceded attribute - readable var _attr_type: nullable MMType + redef fun attr_type do return _attr_type.as(not null) + var _attr_type: nullable MMType # Compute the attribute accessed private fun do_typing(v: TypingVisitor) @@ -997,14 +1131,18 @@ 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}.") return end var prop = lc.select_attribute(name) - if v.module.visibility_for(prop.global.local_class.module) < 3 then - v.error(self, "Error: Attribute {name} from {prop.global.local_class.module} is invisible in {v.module}") + if v.mmmodule.visibility_for(prop.global.local_class.mmmodule) < 3 then + v.error(self, "Error: Attribute {name} from {prop.global.local_class.mmmodule} is invisible in {v.mmmodule}") end _prop = prop var at = prop.signature_for(type_recv).return_type @@ -1017,7 +1155,7 @@ redef class AAttrExpr redef fun after_typing(v) do do_typing(v) - if prop == null then return + if _prop == null then return _stype = attr_type _is_typed = true end @@ -1027,7 +1165,7 @@ redef class AAttrAssignExpr redef fun after_typing(v) do do_typing(v) - if prop == null then return + if _prop == null then return if not v.check_conform_expr(n_value, attr_type) then return _is_typed = true end @@ -1037,7 +1175,7 @@ redef class AAttrReassignExpr redef fun after_typing(v) do do_typing(v) - if prop == null then return + if _prop == null then return var t = do_rvalue_typing(v, attr_type) if t == null then return v.check_conform(self, t, n_value.stype) @@ -1049,7 +1187,7 @@ redef class AIssetAttrExpr redef fun after_typing(v) do do_typing(v) - if prop == null then return + if _prop == null then return if attr_type.is_nullable then v.error(self, "Error: isset on a nullable attribute.") end @@ -1058,53 +1196,65 @@ redef class AIssetAttrExpr end end -class AAbsAbsSendExpr -special PExpr +redef class AAbsAbsSendExpr # The signature of the called property - readable var _prop_signature: nullable MMSignature + redef fun prop_signature do return _prop_signature.as(not null) + var _prop_signature: nullable MMSignature - # The real arguments used (after star transformation) (once computed) - readable var _arguments: nullable Array[PExpr] + # Raw arguments used (without vararg transformation) + redef fun raw_arguments: Array[AExpr] + do + var res = _raw_arguments_cache + if res != null then + return res + else + res = compute_raw_arguments + if res == null then res = new Array[AExpr] + _raw_arguments_cache = res + return res + end + end + + var _raw_arguments_cache: nullable Array[AExpr] = null + + fun compute_raw_arguments: nullable Array[AExpr] + do + print "{location} no compute_raw_arguments" + return null + end # Check the conformity of a set of arguments `raw_args' to a signature. - private fun process_signature(v: TypingVisitor, psig: MMSignature, name: Symbol, raw_args: nullable Array[PExpr]): nullable Array[PExpr] + private fun process_signature(v: TypingVisitor, psig: MMSignature, name: Symbol, raw_args: nullable Array[AExpr]): Bool do var par_vararg = psig.vararg_rank var par_arity = psig.arity var raw_arity: Int if raw_args == null then raw_arity = 0 else raw_arity = raw_args.length if par_arity > raw_arity or (par_arity != raw_arity and par_vararg == -1) then - v.error(self, "Error: '{name}' arity missmatch.") - return null + v.error(self, "Error: arity missmatch; prototype is '{name}{psig}'.") + return false end var arg_idx = 0 - var args = new Array[PExpr] for par_idx in [0..par_arity[ do - var a: PExpr + var a: AExpr var par_type = psig[par_idx] if par_idx == par_vararg then - var star = new Array[PExpr] for i in [0..(raw_arity-par_arity)] do a = raw_args[arg_idx] v.check_conform_expr(a, par_type) - star.add(a) arg_idx = arg_idx + 1 end - var aa = new AArrayExpr.init_aarrayexpr(star) - aa.do_typing(v, par_type) - a = aa else a = raw_args[arg_idx] v.check_conform_expr(a, par_type) arg_idx = arg_idx + 1 end - args.add(a) end - return args + return true end # Check the conformity of a set of defined closures - private fun process_closures(v: TypingVisitor, psig: MMSignature, name: Symbol, cd: nullable Array[PClosureDef]): nullable MMType + private fun process_closures(v: TypingVisitor, psig: MMSignature, name: Symbol, cd: nullable Array[AClosureDef]): nullable MMType do var t = psig.return_type var cs = psig.closures # Declared closures @@ -1112,24 +1262,40 @@ special PExpr for c in cs do if not c.is_optional then min_arity += 1 end - if cd != null then - if cs.length == 0 then - v.error(self, "Error: {name} does not require blocks.") - else if cd.length > cs.length or cd.length < min_arity then + var arity = 0 + if cd != null then arity = cd.length + if cs.length > 0 then + if arity == 0 and min_arity > 0 then + v.error(self, "Error: {name} requires {cs.length} blocks.") + else if arity > cs.length or arity < min_arity then v.error(self, "Error: {name} requires {cs.length} blocks, {cd.length} found.") else # Initialize the break list if a value is required for breaks (ie. if the method is a function) var break_list: nullable Array[ABreakExpr] = null if t != null then break_list = new Array[ABreakExpr] + # The n_label, is any in only set on the last decl + var n_label = if arity > 0 then cd[arity-1].n_label else null + # Process each closure definition - for i in [0..cd.length[ do - var csi = cs[i] + for i in [0..arity[ do var cdi = cd[i] - var esc = new EscapableClosure(cdi, csi, break_list) - v.escapable_ctx.push(esc) - 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.scope_ctx.push_escapable(esc, n_label) + cdi.accept_typing2(v, esc) + v.scope_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 @@ -1137,43 +1303,49 @@ special PExpr t = v.check_conform_multiexpr(t, break_list) end end - else if min_arity != 0 then - v.error(self, "Error: {name} requires {cs.length} blocks.") + else if arity != 0 then + v.error(self, "Error: {name} does not require blocks.") end return t end end -class AAbsSendExpr -special AAbsAbsSendExpr +redef class AAbsSendExpr # Compute the called global property - private fun do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: nullable Array[PExpr], closure_defs: nullable Array[PClosureDef]) + private fun do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: nullable Array[AExpr], closure_defs: nullable Array[AClosureDef]) do var prop = get_property(v, type_recv, is_implicit_self, name) if prop == null then return var sig = get_signature(v, type_recv, prop, recv_is_self) - var args = process_signature(v, sig, prop.name, raw_args) - if args == null then return + if not process_signature(v, sig, prop.name, raw_args) then return var rtype = process_closures(v, sig, prop.name, closure_defs) if rtype == null and sig.return_type != null then return _prop = prop _prop_signature = sig - _arguments = args _return_type = rtype end 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 @@ -1193,29 +1365,29 @@ special AAbsAbsSendExpr # Get the signature for a local property and a receiver private fun get_signature(v: TypingVisitor, type_recv: MMType, prop: MMMethod, recv_is_self: Bool): MMSignature do - prop.global.check_visibility(v, self, v.module, recv_is_self) + prop.global.check_visibility(v, self, v.mmmodule, recv_is_self) var psig = prop.signature_for(type_recv) if not recv_is_self then psig = psig.not_for_self return psig end # The invoked method (once computed) - readable var _prop: nullable MMMethod + redef fun prop do return _prop.as(not null) + var _prop: nullable MMMethod # The return type (if any) (once computed) - readable var _return_type: nullable MMType + redef readable var _return_type: nullable MMType end # A possible call of constructor in a super class # Could be an explicit call or with the 'super' keyword -class ASuperInitCall -special AAbsSendExpr +redef class ASuperInitCall private fun register_super_init_call(v: TypingVisitor, property: MMMethod) do if parent != v.top_block and self != v.top_block then v.error(self, "Error: Constructor invocation {property} must not be in nested block.") end - var cla = v.module[property.global.intro.local_class.global] + var cla = v.mmmodule[property.global.intro.local_class.global] var prev_class: nullable MMLocalClass = null var esic = v.explicit_super_init_calls.as(not null) if not esic.is_empty then @@ -1229,12 +1401,11 @@ special AAbsSendExpr else if cla == prev_class then v.error(self, "Error: Only one super constructor invocation of class {cla} is allowed.") else - var last_is_found = prev_class == null for c in order do if c == prev_class then - last_is_found = true + prev_class = null else if c == cla then - if not last_is_found then + if prev_class != null then v.error(self, "Error: Constructor of {c} must be invoked before constructor of {prev_class}") end esic.add(property) @@ -1247,7 +1418,7 @@ special AAbsSendExpr end redef class ANewExpr -special AAbsSendExpr + redef fun compute_raw_arguments do return n_args.to_a redef fun after_typing(v) do if not n_type.is_typed then return @@ -1263,8 +1434,8 @@ special AAbsSendExpr name = n_id.to_symbol end - do_typing(v, t, false, false, name, n_args.to_a, null) - if prop == null then return + do_typing(v, t, false, false, name, raw_arguments, null) + if _prop == null then return if not prop.global.is_init then v.error(self, "Error: {prop} is not a constructor.") @@ -1277,15 +1448,11 @@ end redef class ASendExpr -special ASuperInitCall # Name of the invoked property fun name: Symbol is abstract - # Raw arguments used (withour star transformation) - fun raw_arguments: nullable Array[PExpr] is abstract - # Closure definitions - fun closure_defs: nullable Array[PClosureDef] do return null + redef fun closure_defs: nullable Array[AClosureDef] do return null redef fun after_typing(v) do @@ -1314,10 +1481,9 @@ special ASuperInitCall end end -class ASendReassignExpr -special ASendExpr -special AReassignFormExpr - readable var _read_prop: nullable MMMethod +redef class ASendReassignExpr + redef fun read_prop do return _read_prop.as(not null) + var _read_prop: nullable MMMethod redef fun do_all_typing(v) do if not v.check_expr(n_expr) then return @@ -1340,7 +1506,7 @@ special AReassignFormExpr v.check_conform(self, t2, n_value.stype) _read_prop = prop - var old_args = arguments + raw_args = raw_args.to_a raw_args.add(n_value) do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, "{name}=".to_symbol, raw_args, null) @@ -1352,13 +1518,12 @@ special AReassignFormExpr end end - _arguments = old_args # FIXME: What if star parameters do not match betwen the two methods? _is_typed = true end end redef class ABinopExpr - redef fun raw_arguments do return [n_expr2] + redef fun compute_raw_arguments do return [n_expr2] end redef class AEqExpr redef fun name do return once "==".to_symbol @@ -1372,17 +1537,22 @@ 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 end - private fun try_to_isa(v: TypingVisitor, n: PExpr) + private fun try_to_isa(v: TypingVisitor, n: AExpr) do var variable = n.its_variable - if variable != null then - _if_false_variable_ctx = v.variable_ctx.sub_with(self, variable, n.stype.as_notnull) + if variable != null and n.stype isa MMNullableType then + _if_false_flow_ctx = v.flow_ctx.sub_with(self, variable, n.stype.as_notnull) + _if_true_flow_ctx = v.flow_ctx.sub_with(self, variable, v.type_none) end end end @@ -1398,17 +1568,22 @@ 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 end - private fun try_to_isa(v: TypingVisitor, n: PExpr) + private fun try_to_isa(v: TypingVisitor, n: AExpr) do var variable = n.its_variable - if variable != null then - _if_true_variable_ctx = v.variable_ctx.sub_with(self, variable, n.stype.as_notnull) + if variable != null and n.stype isa MMNullableType then + _if_true_flow_ctx = v.flow_ctx.sub_with(self, variable, n.stype.as_notnull) + _if_false_flow_ctx = v.flow_ctx.sub_with(self, variable, v.type_none) end end end @@ -1418,12 +1593,18 @@ end redef class ALeExpr redef fun name do return once "<=".to_symbol end +redef class ALlExpr + redef fun name do return once "<<".to_symbol +end redef class AGtExpr redef fun name do return once ">".to_symbol end redef class AGeExpr redef fun name do return once ">=".to_symbol end +redef class AGgExpr + redef fun name do return once ">>".to_symbol +end redef class APlusExpr redef fun name do return once "+".to_symbol end @@ -1445,7 +1626,7 @@ end redef class AUminusExpr redef fun name do return once "unary -".to_symbol - redef fun raw_arguments do return null + redef fun compute_raw_arguments do return null end redef class ACallFormExpr @@ -1453,25 +1634,23 @@ redef class ACallFormExpr do if n_expr.is_implicit_self then var name = n_id.to_symbol - var variable = v.variable_ctx[name] + var variable = v.scope_ctx[name] if variable != null then + var n: AExpr if variable isa ClosureVariable then - var n = new AClosureCallExpr.init_aclosurecallexpr(n_id, n_args, n_closure_defs) - replace_with(n) + n = new AClosureCallExpr.init_aclosurecallexpr(n_id, n_args, n_closure_defs) n._variable = variable - n.after_typing(v) - return else if not n_args.is_empty then v.error(self, "Error: {name} is variable, not a function.") return end - var vform = variable_create(variable) - vform._variable = variable - replace_with(vform) - vform.after_typing(v) - return + n = variable_create(variable) + n._variable = variable end + replace_with(n) + n.after_typing(v) + return end end @@ -1498,7 +1677,7 @@ redef class ACallExpr end redef fun name do return n_id.to_symbol - redef fun raw_arguments do return n_args.to_a + redef fun compute_raw_arguments do return n_args.to_a end redef class ACallAssignExpr @@ -1508,7 +1687,7 @@ redef class ACallAssignExpr end redef fun name do return (n_id.text + "=").to_symbol - redef fun raw_arguments do + redef fun compute_raw_arguments do var res = n_args.to_a res.add(n_value) return res @@ -1516,24 +1695,31 @@ redef class ACallAssignExpr end redef class ACallReassignExpr -special ASendReassignExpr redef fun variable_create(variable) do return new AVarReassignExpr.init_avarreassignexpr(n_id, n_assign_op, n_value) end redef fun name do return n_id.to_symbol - redef fun raw_arguments do return n_args.to_a + redef fun compute_raw_arguments do return n_args.to_a end redef class ABraExpr redef fun name do return once "[]".to_symbol - redef fun raw_arguments do return n_args.to_a + redef fun compute_raw_arguments do return n_args.to_a + redef fun closure_defs + do + if n_closure_defs.is_empty then + return null + else + return n_closure_defs.to_a + end + end end redef class ABraAssignExpr redef fun name do return once "[]=".to_symbol - redef fun raw_arguments do + redef fun compute_raw_arguments do var res = n_args.to_a res.add(n_value) return res @@ -1541,39 +1727,47 @@ redef class ABraAssignExpr end redef class ABraReassignExpr -special ASendReassignExpr redef fun name do return once "[]".to_symbol - redef fun raw_arguments do return n_args.to_a + redef fun compute_raw_arguments do return n_args.to_a end redef class AInitExpr redef fun name do return once "init".to_symbol - redef fun raw_arguments do return n_args.to_a + redef fun compute_raw_arguments do return n_args.to_a end redef class AClosureCallExpr -special AAbsAbsSendExpr var _variable: nullable ClosureVariable redef fun variable do return _variable.as(not null) + redef fun compute_raw_arguments do return n_args.to_a redef fun after_typing(v) do var va = variable - if va.closure.is_break then v.variable_ctx.unreash = true + if va.closure.is_break then v.mark_unreash(self) var sig = va.closure.signature - var args = process_signature(v, sig, n_id.to_symbol, n_args.to_a) + var s = process_signature(v, sig, n_id.to_symbol, compute_raw_arguments) if not n_closure_defs.is_empty then process_closures(v, sig, n_id.to_symbol, n_closure_defs.to_a) end - if args == null then return + if not s then return _prop_signature = sig - _arguments = args _stype = sig.return_type _is_typed = true end end -redef class PClosureDef +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) @@ -1587,52 +1781,49 @@ redef class PClosureDef if _accept_typing2 then super end - private fun accept_typing2(v: TypingVisitor, esc: EscapableClosure) is abstract -end - -redef class AClosureDef - redef fun accept_typing2(v, esc) + private fun accept_typing2(v: TypingVisitor, esc: EscapableClosure) do _escapable = esc var sig = esc.closure.signature - if sig.arity != n_id.length then - v.error(self, "Error: {sig.arity} automatic variable names expected, {n_id.length} found.") + if sig.arity != n_ids.length then + v.error(self, "Error: {sig.arity} automatic variable names expected, {n_ids.length} found.") return end _closure = esc.closure - 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) + v.scope_ctx.push(self) + var old_flow_ctx = v.flow_ctx + var old_base_flow_ctx = v.base_flow_ctx + v.base_flow_ctx = v.flow_ctx variables = new Array[AutoVariable] - for i in [0..n_id.length[ do - var va = new AutoVariable(n_id[i].to_symbol, self) + for i in [0..n_ids.length[ do + var va = new AutoVariable(n_ids[i].to_symbol, n_ids[i]) variables.add(va) va.stype = sig[i] - v.variable_ctx.add(va) + v.scope_ctx.add_variable(va) end _accept_typing2 = true accept_typing(v) - if v.variable_ctx.unreash == false then + if v.flow_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.base_variable_ctx = old_base_var_ctx + v.flow_ctx = old_flow_ctx + v.base_flow_ctx = old_base_flow_ctx + v.scope_ctx.pop end end class ATypeCheckExpr -special PExpr - private fun check_expr_cast(v: TypingVisitor, n_expr: PExpr, n_type: PType) +special AExpr + private fun check_expr_cast(v: TypingVisitor, n_expr: AExpr, n_type: AType) do if not v.check_expr(n_expr) then return if not n_type.is_typed then return @@ -1642,6 +1833,9 @@ special PExpr 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 @@ -1666,7 +1860,7 @@ special ATypeCheckExpr if not n_type.is_typed then return var variable = n_expr.its_variable if variable != null then - _if_true_variable_ctx = v.variable_ctx.sub_with(self, variable, n_type.stype) + _if_true_flow_ctx = v.flow_ctx.sub_with(self, variable, n_type.stype) end _stype = v.type_bool _is_typed = true