X-Git-Url: http://nitlanguage.org diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index 8580534..679b88e 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -18,6 +18,8 @@ package typing import syntax_base +import escape +import control_flow redef class MMSrcModule # Walk trough the module and type statments and expressions @@ -44,6 +46,9 @@ special AbsSyntaxVisitor # Current knowledge about variables names and types readable writable attr _variable_ctx: VariableContext + # Current knowledge about escapable blocks + readable writable attr _escapable_ctx: EscapableContext = new EscapableContext(self) + # The current reciever readable writable attr _self_var: ParamVariable @@ -56,6 +61,18 @@ special AbsSyntaxVisitor # Is a other constructor of the same class invoked readable writable attr _explicit_other_init_call: Bool + # Make the if_true_variable_ctx of the expression effective + private meth use_if_true_variable_ctx(e: PExpr) + do + var ctx = e.if_true_variable_ctx + if ctx != null then + variable_ctx = ctx + end + end + + # Number of nested once + readable writable attr _once_count: Int = 0 + init(tc, module) do super private meth get_default_constructor_for(n: PNode, c: MMLocalClass, prop: MMSrcMethod): MMMethod @@ -108,87 +125,6 @@ special AbsSyntaxVisitor end end -# Associate symbols to variable and variables to type -# Can be nested -private class VariableContext - # Look for the variable from its name - # Return null if nothing found - meth [](s: Symbol): Variable - do - if _dico.has_key(s) then - return _dico[s] - else - return null - end - end - - # Register a new variable with its name - meth add(v: Variable) - do - _dico[v.name] = v - end - - - # The effective static type of a given variable - # May be different from the declaration static type - meth stype(v: Variable): MMType - do - return v.stype - end - - # Variables by name (in the current context only) - attr _dico: Map[Symbol, Variable] - - # Build a new VariableContext - meth sub: SubVariableContext - do - return new SubVariableContext.with_prev(self, null, null) - end - - # Build a nested VariableContext with new variable information - meth sub_with(v: Variable, t: MMType): SubVariableContext - do - return new SubVariableContext.with_prev(self, v, t) - end - - init - do - _dico = new HashMap[Symbol, Variable] - end -end - -private class SubVariableContext -special VariableContext - readable attr _prev: VariableContext - attr _variable: Variable - attr _var_type: MMType - - redef meth [](s) - do - if _dico.has_key(s) then - return _dico[s] - else - return prev[s] - end - end - - redef meth stype(v) - do - if _variable == v then - return _var_type - end - return prev.stype(v) - end - - init with_prev(p: VariableContext, v: Variable, t: MMType) - do - init - _prev = p - _variable = v - _var_type =t - end -end - ############################################################################### @@ -224,12 +160,22 @@ redef class AMethPropdef redef readable attr _self_var: ParamVariable redef meth accept_typing(v) do - v.variable_ctx = new VariableContext + v.variable_ctx = new RootVariableContext(v, self) _self_var = v.self_var super end end +redef class AConcreteMethPropdef + redef meth accept_typing(v) + do + super + if v.variable_ctx.unreash == false 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 attr _super_init_calls: Array[MMMethod] = new Array[MMMethod] readable attr _explicit_super_init_calls: Array[MMMethod] = new Array[MMMethod] @@ -281,12 +227,46 @@ end redef class PParam redef meth after_typing(v) do + # TODO: why the test? if v.variable_ctx != null then v.variable_ctx.add(variable) end end end +redef class AClosureDecl + # The corresponding escapable object + readable attr _escapable: EscapableBlock + + redef meth accept_typing(v) + do + # Register the closure for ClosureCallExpr + v.variable_ctx.add(variable) + + var old_var_ctx = v.variable_ctx + v.variable_ctx = v.variable_ctx.sub(self) + + _escapable = new EscapableClosure(self, variable.closure, null) + v.escapable_ctx.push(_escapable) + + super + + if n_expr != null then + 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).") + end + end + end + + old_var_ctx.merge(v.variable_ctx) + v.variable_ctx = old_var_ctx + v.escapable_ctx.pop + end +end + redef class PType readable attr _stype: MMType redef meth after_typing(v) @@ -296,8 +276,22 @@ redef class PType end redef class PExpr - redef readable attr _stype: MMType - + redef readable attr _is_typed: Bool = false + redef meth is_statement: Bool do return _stype == null + redef meth stype + do + if not is_typed then + print "{locate}: not is_typed" + abort + end + if is_statement then + print "{locate}: is_statement" + abort + end + return _stype + end + attr _stype: MMType + # Is the expression the implicit receiver meth is_implicit_self: Bool do return false @@ -317,6 +311,7 @@ redef class AVardeclExpr var va = new VarVariable(n_id.to_symbol, self) variable = va v.variable_ctx.add(va) + if n_expr != null then v.variable_ctx.mark_is_set(va) if n_type != null then va.stype = n_type.stype @@ -324,9 +319,10 @@ redef class AVardeclExpr v.check_conform_expr(n_expr, va.stype) end else - v.check_expr(n_expr) + if not v.check_expr(n_expr) then return va.stype = n_expr.stype end + _is_typed = true end end @@ -334,17 +330,26 @@ redef class ABlockExpr redef meth accept_typing(v) do var old_var_ctx = v.variable_ctx - v.variable_ctx = v.variable_ctx.sub + v.variable_ctx = v.variable_ctx.sub(self) - super + 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.") + end + v.visit(e) + end + old_var_ctx.merge(v.variable_ctx) v.variable_ctx = old_var_ctx + _is_typed = true end end redef class AReturnExpr redef meth after_typing(v) do + v.variable_ctx.unreash = true var t = v.local_property.signature.return_type if n_expr == null and t != null then v.error(self, "Error: Return without value in a function.") @@ -353,6 +358,58 @@ redef class AReturnExpr else if n_expr != null and t != null then v.check_conform_expr(n_expr, t) end + _is_typed = true + end +end + +redef class AContinueExpr + redef meth after_typing(v) + do + v.variable_ctx.unreash = true + var esc = compute_escapable_block(v.escapable_ctx) + if esc == null then return + + if esc.is_break_block then + v.error(self, "Error: 'continue' forbiden in break blocks.") + return + end + + var t = esc.continue_stype + if n_expr == null and t != null then + v.error(self, "Error: continue with a value required in this block.") + else if n_expr != null and t == null then + v.error(self, "Error: continue without value required in this block.") + else if n_expr != null and t != null then + v.check_conform_expr(n_expr, t) + end + _is_typed = true + end +end + +redef class ABreakExpr + redef meth after_typing(v) + do + v.variable_ctx.unreash = true + var esc = compute_escapable_block(v.escapable_ctx) + if esc == null then return + + 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.") + else if n_expr != null and bl == null then + v.error(self, "Error: break without value required in this block.") + else if n_expr != null and bl != null then + # Typing check can only be done later + bl.add(n_expr) + end + _is_typed = true + end +end + +redef class AAbortExpr + redef meth after_typing(v) + do + v.variable_ctx.unreash = true end end @@ -363,64 +420,102 @@ redef class AIfExpr v.visit(n_expr) v.check_conform_expr(n_expr, v.type_bool) - if n_expr.if_true_variable_ctx != null then - v.variable_ctx = n_expr.if_true_variable_ctx - end + v.use_if_true_variable_ctx(n_expr) + v.variable_ctx = v.variable_ctx.sub(n_then) v.visit(n_then) - # Restore variable ctx - v.variable_ctx = old_var_ctx - if n_else != null then + if n_else == null then + # Restore variable ctx since the 'then' block is optional + v.variable_ctx = old_var_ctx + else + # Remember what appened in the 'then' + var then_var_ctx = v.variable_ctx + # Reset to process the 'else' + v.variable_ctx = old_var_ctx.sub(n_else) v.visit(n_else) + # Merge then and else in the old control_flow + old_var_ctx.merge2(then_var_ctx, v.variable_ctx) v.variable_ctx = old_var_ctx end + _is_typed = true end end redef class AWhileExpr - redef meth after_typing(v) + # The corresponding escapable block + readable attr _escapable: EscapableBlock + + redef meth accept_typing(v) do + _escapable = new EscapableBlock(self) + v.escapable_ctx.push(_escapable) + var old_var_ctx = v.variable_ctx + v.variable_ctx = v.variable_ctx.sub(self) + + super + v.check_conform_expr(n_expr, v.type_bool) + v.variable_ctx = old_var_ctx + v.escapable_ctx.pop + _is_typed = true end end redef class AForExpr - redef meth after_typing(v) - do - # pop context created in AForVardeclExpr - var varctx = v.variable_ctx - assert varctx isa SubVariableContext - v.variable_ctx = varctx.prev - end -end + # The corresponding escapable block + readable attr _escapable: EscapableBlock -redef class AForVardeclExpr - redef meth after_typing(v) + readable attr _meth_iterator: MMMethod + readable attr _meth_is_ok: MMMethod + readable attr _meth_item: MMMethod + readable attr _meth_next: MMMethod + redef meth accept_typing(v) do - v.variable_ctx = v.variable_ctx.sub + _escapable = new EscapableBlock(self) + v.escapable_ctx.push(_escapable) + + var old_var_ctx = v.variable_ctx + v.variable_ctx = v.variable_ctx.sub(self) var va = new AutoVariable(n_id.to_symbol, self) variable = va v.variable_ctx.add(va) + v.visit(n_expr) + + if not v.check_conform_expr(n_expr, v.type_collection) then return var expr_type = n_expr.stype - if not v.check_conform_expr(n_expr, v.type_collection) then + _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 prop = expr_type.local_class.select_method(once ("iterator".to_symbol)) - if prop == null then - v.error(self, "Error: Collection MUST have an iterate method") + var iter_type = _meth_iterator.signature_for(expr_type).return_type + _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 - var iter_type = prop.signature_for(expr_type).return_type - var prop2 = iter_type.local_class.select_method(once ("item".to_symbol)) - if prop2 == null then + _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 - var t = prop2.signature_for(iter_type).return_type + _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 + + if n_block != null then v.visit(n_block) + + # pop context + v.variable_ctx = old_var_ctx + v.escapable_ctx.pop + _is_typed = true end end @@ -428,7 +523,8 @@ redef class AAssertExpr redef meth after_typing(v) do v.check_conform_expr(n_expr, v.type_bool) - if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx + v.use_if_true_variable_ctx(n_expr) + _is_typed = true end end @@ -437,36 +533,48 @@ redef class AVarExpr redef meth after_typing(v) do + v.variable_ctx.check_is_set(self, variable) _stype = v.variable_ctx.stype(variable) + _is_typed = _stype != null end end redef class AVarAssignExpr redef meth after_typing(v) do + v.variable_ctx.mark_is_set(variable) var t = v.variable_ctx.stype(variable) - v.check_conform_expr(n_value, t) + if v.check_conform_expr(n_value, variable.stype) then + # Fall back to base type if current type does not match + if not n_value.stype < t then + v.variable_ctx.stype(variable) = variable.stype + end + end + _is_typed = true end end redef class AReassignFormExpr - # Compute and check method used through the reassigment operator - private meth do_lvalue_typing(v: TypingVisitor, type_lvalue: MMType) + # Compute and check method used through the reassigment operator + # On success return the static type of the result of the reassigment operator + # Else display an error and return null + private meth do_rvalue_typing(v: TypingVisitor, type_lvalue: MMType): MMType do if type_lvalue == null then - return + return null end var name = n_assign_op.method_name - var prop = type_lvalue.local_class.select_method(name) - if prop == null then + 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 + return null end + var prop = lc.select_method(name) prop.global.check_visibility(v, self, v.module, false) var psig = prop.signature_for(type_lvalue) _assign_method = prop - v.check_conform_expr(n_value, psig[0].not_for_self) - v.check_conform(self, psig.return_type.not_for_self, n_value.stype) + 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) @@ -476,8 +584,18 @@ end redef class AVarReassignExpr redef meth 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) - do_lvalue_typing(v, t) + var t2 = do_rvalue_typing(v, t) + if t2 == null then return + if v.check_conform(self, t2, variable.stype) then + # Fall back to base type if current type does not match + if not t2 < t then + v.variable_ctx.stype(variable) = variable.stype + end + end + _is_typed = true end end @@ -498,6 +616,7 @@ redef class ASelfExpr do variable = v.self_var _stype = v.variable_ctx.stype(variable) + _is_typed = true end redef meth is_self do return true @@ -513,25 +632,15 @@ redef class AIfexprExpr var old_var_ctx = v.variable_ctx v.visit(n_expr) - if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx + v.use_if_true_variable_ctx(n_expr) v.visit(n_then) v.variable_ctx = old_var_ctx v.visit(n_else) v.check_conform_expr(n_expr, v.type_bool) - if not v.check_expr(n_then) or not v.check_expr(n_else) then return - - var t = n_then.stype - var te = n_else.stype - if t < te then - t = te - else if not te < t then - v.error(self, "Type error: {te} is not a subtype of {t}.") - return - end - - _stype = t + _stype = v.check_conform_multiexpr(null, [n_then, n_else]) + _is_typed = _stype != null end end @@ -539,6 +648,7 @@ redef class ABoolExpr redef meth after_typing(v) do _stype = v.type_bool + _is_typed = true end end @@ -548,6 +658,7 @@ redef class AOrExpr v.check_conform_expr(n_expr, v.type_bool) v.check_conform_expr(n_expr2, v.type_bool) _stype = v.type_bool + _is_typed = true end end @@ -557,7 +668,7 @@ redef class AAndExpr var old_var_ctx = v.variable_ctx v.visit(n_expr) - if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx + v.use_if_true_variable_ctx(n_expr) v.visit(n_expr2) if n_expr2.if_true_variable_ctx != null then @@ -571,6 +682,7 @@ redef class AAndExpr v.check_conform_expr(n_expr, v.type_bool) v.check_conform_expr(n_expr2, v.type_bool) _stype = v.type_bool + _is_typed = true end end @@ -579,6 +691,7 @@ redef class ANotExpr do v.check_conform_expr(n_expr, v.type_bool) _stype = v.type_bool + _is_typed = true end end @@ -586,7 +699,7 @@ redef class AIntExpr redef meth after_typing(v) do _stype = v.type_int - + _is_typed = true end end @@ -594,6 +707,7 @@ redef class AFloatExpr redef meth after_typing(v) do _stype = v.type_float + _is_typed = true end end @@ -601,20 +715,37 @@ redef class ACharExpr redef meth after_typing(v) do _stype = v.type_char + _is_typed = true end end redef class AStringFormExpr + readable attr _meth_with_native: MMMethod redef meth 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 + readable attr _meth_with_capacity: MMMethod + readable attr _meth_add: MMMethod + readable attr _meth_to_s: MMMethod + readable attr _atype: MMType redef meth after_typing(v) do _stype = v.type_string + _atype = v.type_array(_stype) + _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 @@ -622,36 +753,41 @@ redef class ANullExpr redef meth after_typing(v) do _stype = v.type_none + _is_typed = true end end redef class AArrayExpr - private meth stype=(t: MMType) do _stype = t + readable attr _meth_with_capacity: MMMethod + readable attr _meth_add: MMMethod redef meth after_typing(v) do - var stype: MMType = null - for n in n_exprs do - var ntype = n.stype - if stype == null or (ntype != null and stype < ntype) then - stype = ntype - end - end - for n in n_exprs do - v.check_conform_expr(n, stype) - end - _stype = v.type_array(stype) + var stype = v.check_conform_multiexpr(null, n_exprs) + if stype == null then return + do_typing(v, stype) + end + + private meth 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 + readable attr _meth_init: MMMethod redef meth after_typing(v) do + if not v.check_expr(n_expr) or not v.check_expr(n_expr2) then return var ntype = n_expr.stype var ntype2 = n_expr2.stype - if ntype == null or ntype == null then - return - end if ntype < ntype2 then ntype = ntype2 else if not ntype2 < ntype then @@ -659,12 +795,28 @@ redef class ARangeExpr return end var dtype = v.type_discrete - v.check_conform_expr(n_expr, dtype) - v.check_conform_expr(n_expr2, dtype) + if not v.check_conform_expr(n_expr, dtype) or not v.check_conform_expr(n_expr2, dtype) then return _stype = v.type_range(ntype) + _is_typed = true + end +end + +redef class ACrangeExpr + redef meth after_typing(v) + do + super + _meth_init = stype.local_class.select_method(once "init".to_symbol) + end +end +redef class AOrangeExpr + redef meth after_typing(v) + do + super + _meth_init = stype.local_class.select_method(once "without_last".to_symbol) end end + redef class ASuperExpr special ASuperInitCall # readable attr _prop: MMSrcMethod @@ -696,7 +848,7 @@ special ASuperInitCall register_super_init_call(v, p) if n_args.length > 0 then var signature = get_signature(v, v.self_var.stype, p, true) - _arguments = process_signature(v, signature, p, n_args.to_a) + _arguments = process_signature(v, signature, p.name, n_args.to_a) end else v.error(self, "Error: No super method to call for {v.local_property}.") @@ -722,6 +874,7 @@ special ASuperInitCall var p = v.local_property assert p isa MMSrcMethod _prop = p + _is_typed = true end end @@ -738,11 +891,13 @@ redef class AAttrFormExpr if not v.check_expr(n_expr) then return var type_recv = n_expr.stype var name = n_id.to_symbol - var prop = type_recv.local_class.select_attribute(name) - if prop == null then + 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 - else if v.module.visibility_for(prop.global.local_class.module) < 3 then + 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}") end _prop = prop @@ -756,10 +911,9 @@ redef class AAttrExpr redef meth after_typing(v) do do_typing(v) - if prop == null then - return - end + if prop == null then return _stype = attr_type + _is_typed = true end end @@ -767,10 +921,9 @@ redef class AAttrAssignExpr redef meth after_typing(v) do do_typing(v) - if prop == null then - return - end - v.check_conform_expr(n_value, attr_type) + if prop == null then return + if not v.check_conform_expr(n_value, attr_type) then return + _is_typed = true end end @@ -778,32 +931,125 @@ redef class AAttrReassignExpr redef meth 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) + _is_typed = true + end +end + +class AAbsAbsSendExpr +special PExpr + # The signature of the called property + readable attr _prop_signature: MMSignature + + # The real arguments used (after star transformation) (once computed) + readable attr _arguments: Array[PExpr] + + # Check the conformity of a set of arguments `raw_args' to a signature. + private meth process_signature(v: TypingVisitor, psig: MMSignature, name: Symbol, raw_args: Array[PExpr]): Array[PExpr] + 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 end - do_lvalue_typing(v, attr_type) + var arg_idx = 0 + var args = new Array[PExpr] + for par_idx in [0..par_arity[ do + var a: PExpr + 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 + end + + # Check the conformity of a set of defined closures + private meth process_closures(v: TypingVisitor, psig: MMSignature, name: Symbol, cd: Array[PClosureDef]): MMType + do + var t = psig.return_type + var cs = psig.closures # Declared closures + var min_arity = 0 + 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 + 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: Array[ABreakExpr] = null + if t != null then break_list = new Array[ABreakExpr] + + # Process each closure definition + for i in [0..cd.length[ do + var csi = cs[i] + 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 + end + + # Check break type conformity + if break_list != null then + 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.") + end + return t end end class AAbsSendExpr -special PExpr +special AAbsAbsSendExpr # Compute the called global property - private meth do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: Array[PExpr]) + private meth do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: Array[PExpr], closure_defs: Array[PClosureDef]) 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) if sig == null then return - var args = process_signature(v, sig, prop, raw_args) + var args = process_signature(v, sig, prop.name, raw_args) if args == null 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 meth get_property(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, name: Symbol): MMMethod do if type_recv == null then return null - var prop = type_recv.local_class.select_method(name) + var lc = type_recv.local_class + var prop: 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) if props.length > 1 then @@ -827,6 +1073,7 @@ special PExpr return prop end + # Get the signature for a local property and a receiver private meth 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) @@ -834,49 +1081,12 @@ special PExpr if not recv_is_self then psig = psig.not_for_self return psig end - - # Check the conformity of a set of arguments `raw_args' to a signature. - private meth process_signature(v: TypingVisitor, psig: MMSignature, prop: MMMethod, raw_args: Array[PExpr]): Array[PExpr] - 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: Method '{prop}' arity missmatch.") - return null - end - var arg_idx = 0 - var args = new Array[PExpr] - for par_idx in [0..par_arity[ do - var a: PExpr - 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.stype = v.type_array(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 - end # The invoked method (once computed) readable attr _prop: MMMethod - # The real arguments used (after star transformation) (once computed) - readable attr _arguments: Array[PExpr] + # The return type (if any) (once computed) + readable attr _return_type: MMType end # A possible call of constructor in a super class @@ -935,13 +1145,15 @@ special AAbsSendExpr name = n_id.to_symbol end - do_typing(v, t, false, false, name, n_args.to_a) + do_typing(v, t, false, false, name, n_args.to_a, null) if prop == null then return if not prop.global.is_init then v.error(self, "Error: {prop} is not a constructor.") + return end _stype = t + _is_typed = true end end @@ -954,6 +1166,9 @@ special ASuperInitCall # Raw arguments used (withour star transformation) meth raw_arguments: Array[PExpr] is abstract + # Closure definitions + meth closure_defs: Array[PClosureDef] do return null + redef meth after_typing(v) do do_all_typing(v) @@ -962,8 +1177,9 @@ special ASuperInitCall private meth do_all_typing(v: TypingVisitor) do if not v.check_expr(n_expr) then return - do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_arguments) + do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_arguments, closure_defs) if prop == null then return + if prop.global.is_init then if not v.local_property.global.is_init then v.error(self, "Error: try to invoke constructor {prop} in a method.") @@ -973,9 +1189,9 @@ special ASuperInitCall register_super_init_call(v, prop) end end - var t = prop.signature_for(n_expr.stype).return_type - if t != null and not n_expr.is_self then t = t.not_for_self - _stype = t + + _stype = return_type + _is_typed = true end end @@ -987,7 +1203,7 @@ special AReassignFormExpr do if not v.check_expr(n_expr) then return var raw_args = raw_arguments - do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_args) + do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_args, null) if prop == null then return if prop.global.is_init then if not v.local_property.global.is_init then @@ -999,13 +1215,15 @@ special AReassignFormExpr var t = prop.signature_for(n_expr.stype).return_type if not n_expr.is_self then t = t.not_for_self - do_lvalue_typing(v, t) + var t2 = do_rvalue_typing(v, t) + if t2 == null then return + v.check_conform(self, t2, n_value.stype) _read_prop = prop var old_args = arguments 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) + do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, "{name}=".to_symbol, raw_args, null) if prop == null then return if prop.global.is_init then if not v.local_property.global.is_init then @@ -1016,6 +1234,7 @@ special AReassignFormExpr end _arguments = old_args # FIXME: What if star parameters do not match betwen the two methods? + _is_typed = true end end @@ -1067,23 +1286,42 @@ end redef class ACallFormExpr redef meth after_typing(v) do - if n_expr.is_implicit_self then + if n_expr != null and n_expr.is_implicit_self then var name = n_id.to_symbol var variable = v.variable_ctx[name] if variable != null then - if not n_args.is_empty then - v.error(self, "Error: {name} is variable, not a function.") + if variable isa ClosureVariable then + var n = new AClosureCallExpr.init_aclosurecallexpr(n_id, n_args, n_closure_defs) + replace_with(n) + 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 end - var vform = variable_create(variable) - vform.variable = variable - replace_with(vform) - vform.after_typing(v) - return end end + super end + redef meth closure_defs + do + if n_closure_defs == null or n_closure_defs.is_empty then + return null + else + return n_closure_defs.to_a + end + end + # Create a variable acces corresponding to the call form meth variable_create(variable: Variable): AVarFormExpr is abstract end @@ -1148,28 +1386,136 @@ redef class AInitExpr redef meth raw_arguments do return n_args.to_a end +redef class AClosureCallExpr +special AAbsAbsSendExpr + redef meth after_typing(v) + do + var va = variable + if va.closure.is_break then v.variable_ctx.unreash = true + var sig = va.closure.signature + var args = process_signature(v, sig, n_id.to_symbol, n_args.to_a) + 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 + _prop_signature = sig + _arguments = args + _stype = sig.return_type + _is_typed = true + end +end + +redef class PClosureDef + # The corresponding escapable object + readable attr _escapable: EscapableBlock + + attr _accept_typing2: Bool + redef meth accept_typing(v) + do + # Typing is deferred, wait accept_typing2(v) + if _accept_typing2 then super + end + + private meth accept_typing2(v: TypingVisitor, esc: EscapableClosure) is abstract +end + +redef class AClosureDef + redef meth accept_typing2(v, esc) + 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.") + return + end + + closure = esc.closure + + var old_var_ctx = v.variable_ctx + v.variable_ctx = v.variable_ctx.sub(self) + variables = new Array[AutoVariable] + for i in [0..n_id.length[ do + var va = new AutoVariable(n_id[i].to_symbol, self) + variables.add(va) + va.stype = sig[i] + v.variable_ctx.add(va) + end + + _accept_typing2 = true + accept_typing(v) + + 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).") + end + end + v.variable_ctx = old_var_ctx + end +end + +class ATypeCheckExpr +special PExpr + private meth check_expr_cast(v: TypingVisitor, n_expr: PExpr, n_type: PType) + do + if not v.check_expr(n_expr) then return + var etype = n_expr.stype + var ttype = n_type.stype + if etype == ttype then + 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}.") + end + end +end + redef class AIsaExpr +special ATypeCheckExpr redef meth after_typing(v) do + check_expr_cast(v, n_expr, n_type) var variable = n_expr.its_variable if variable != null then - _if_true_variable_ctx = v.variable_ctx.sub_with(variable, n_type.stype) + _if_true_variable_ctx = v.variable_ctx.sub_with(self, variable, n_type.stype) end _stype = v.type_bool + _is_typed = true end end redef class AAsCastExpr +special ATypeCheckExpr redef meth after_typing(v) do - v.check_expr(n_expr) + check_expr_cast(v, n_expr, n_type) _stype = n_type.stype + _is_typed = _stype != null end end redef class AProxyExpr redef meth after_typing(v) do + if not n_expr.is_typed then return + _is_typed = true + if n_expr.is_statement then return _stype = n_expr.stype end end + +redef class AOnceExpr + redef meth accept_typing(v) + do + if v.once_count > 0 then + v.warning(self, "Useless once in a once expression.") + end + v.once_count = v.once_count + 1 + + super + + v.once_count = v.once_count - 1 + end +end +