X-Git-Url: http://nitlanguage.org diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index 904e58c..878de83 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -19,6 +19,7 @@ package typing import syntax_base import escape +import control_flow redef class MMSrcModule # Walk trough the module and type statments and expressions @@ -43,35 +44,49 @@ special AbsSyntaxVisitor end # Current knowledge about variables names and types - readable writable attr _variable_ctx: VariableContext + meth variable_ctx: VariableContext do return _variable_ctx.as(not null) + writable attr _variable_ctx: nullable VariableContext + + # Non-bypassable knowledge about variables names and types + meth base_variable_ctx: VariableContext do return _base_variable_ctx.as(not null) + writable attr _base_variable_ctx: nullable VariableContext # Current knowledge about escapable blocks readable writable attr _escapable_ctx: EscapableContext = new EscapableContext(self) # The current reciever - readable writable attr _self_var: ParamVariable + meth self_var: ParamVariable do return _self_var.as(not null) + writable attr _self_var: nullable ParamVariable # Block of the current method - readable writable attr _top_block: PExpr + readable writable attr _top_block: nullable PExpr # List of explicit invocation of constructors of super-classes - readable writable attr _explicit_super_init_calls: Array[MMMethod] + readable writable attr _explicit_super_init_calls: nullable Array[MMMethod] # Is a other constructor of the same class invoked - readable writable attr _explicit_other_init_call: Bool + readable writable attr _explicit_other_init_call: Bool = false # 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 + if ctx != null then variable_ctx = ctx + end + + # Make the if_false_variable_ctx of the expression effective + private meth use_if_false_variable_ctx(e: PExpr) + do + var ctx = e.if_false_variable_ctx + if ctx != null then variable_ctx = ctx 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 + private meth get_default_constructor_for(n: PNode, c: MMLocalClass, prop: MMSrcMethod): nullable MMMethod do var v = self #var prop = v.local_property @@ -85,7 +100,7 @@ special AbsSyntaxVisitor var gps = gp.signature_for(c.get_type) assert gp isa MMSrcMethod var garity = gps.arity - if prop != null and gp.name == prop.name then + if gp.name == prop.name then if garity == 0 or (parity == garity and prop.signature < gps) then return gp else @@ -121,100 +136,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) - end - - # Build a nested VariableContext with new variable information - meth sub_with(v: Variable, t: MMType): SubVariableContext - do - return new CastVariableContext.with_prev(self, v, t) - end - - init - do - _dico = new HashMap[Symbol, Variable] - end -end - -private class SubVariableContext -special VariableContext - readable attr _prev: VariableContext - - redef meth [](s) - do - if _dico.has_key(s) then - return _dico[s] - else - return prev[s] - end - end - - redef meth stype(v) - do - return prev.stype(v) - end - - init with_prev(p: VariableContext) - do - init - _prev = p - end -end - -private class CastVariableContext -special SubVariableContext - attr _variable: Variable - attr _var_type: MMType - - 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 - super(p) - _variable = v - _var_type =t - end -end ############################################################################### @@ -241,21 +162,33 @@ redef class AAttrPropdef do super if n_expr != null then - v.check_conform_expr(n_expr, prop.signature.return_type) + v.check_conform_expr(n_expr.as(not null), prop.signature.return_type.as(not null)) end end end redef class AMethPropdef - redef readable attr _self_var: ParamVariable + redef meth self_var do return _self_var.as(not null) + attr _self_var: nullable ParamVariable redef meth accept_typing(v) do - v.variable_ctx = new VariableContext + v.variable_ctx = new RootVariableContext(v, self) + v.base_variable_ctx = v.variable_ctx _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] @@ -270,8 +203,8 @@ redef class AConcreteInitPropdef else var i = 0 var l = explicit_super_init_calls.length - var cur_m: MMMethod = null - var cur_c: MMLocalClass = null + var cur_m: nullable MMMethod = null + 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) @@ -283,7 +216,7 @@ redef class AConcreteInitPropdef j += 1 else if cur_c != null and (c.cshe <= cur_c or cur_c.global.is_mixin) then if c == cur_c then j += 1 - super_init_calls.add(cur_m) + super_init_calls.add(cur_m.as(not null)) i += 1 if i < l then cur_m = explicit_super_init_calls[i] @@ -307,16 +240,13 @@ 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 + v.variable_ctx.add(variable) end end redef class AClosureDecl # The corresponding escapable object - readable attr _escapable: EscapableBlock + readable attr _escapable: nullable EscapableBlock redef meth accept_typing(v) do @@ -324,20 +254,37 @@ redef class AClosureDecl v.variable_ctx.add(variable) var old_var_ctx = v.variable_ctx - v.variable_ctx = v.variable_ctx.sub + var old_base_var_ctx = v.base_variable_ctx + v.base_variable_ctx = v.variable_ctx + v.variable_ctx = v.variable_ctx.sub(self) - _escapable = new EscapableClosure(self, variable.closure, null) - v.escapable_ctx.push(_escapable) + var escapable = new EscapableClosure(self, variable.closure, null) + _escapable = escapable + 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.base_variable_ctx = old_base_var_ctx v.escapable_ctx.pop end end redef class PType - readable attr _stype: MMType + meth stype: MMType do return _stype.as(not null) + attr _stype: nullable MMType + redef meth after_typing(v) do _stype = get_stype(v) @@ -345,8 +292,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.as(not null) + end + attr _stype: nullable MMType + # Is the expression the implicit receiver meth is_implicit_self: Bool do return false @@ -354,28 +315,36 @@ redef class PExpr meth is_self: Bool do return false # The variable accessed is any - meth its_variable: Variable do return null + meth its_variable: nullable Variable do return null # The variable type information if current boolean expression is true - readable private attr _if_true_variable_ctx: VariableContext + readable private attr _if_true_variable_ctx: nullable VariableContext + + # The variable type information if current boolean expression is false + readable private attr _if_false_variable_ctx: nullable VariableContext end redef class AVardeclExpr + attr _variable: nullable VarVariable + redef meth variable do return _variable.as(not null) + redef meth after_typing(v) do var va = new VarVariable(n_id.to_symbol, self) - variable = va + _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 if n_expr != null then - v.check_conform_expr(n_expr, va.stype) + v.check_conform_expr(n_expr.as(not null), va.stype) end else - v.check_expr(n_expr) + if not v.check_expr(n_expr.as(not null)) then return va.stype = n_expr.stype end + _is_typed = true end end @@ -383,31 +352,43 @@ 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 + var e = n_expr + if e == null and t != null then v.error(self, "Error: Return without value in a function.") - else if n_expr != null and t == null then + else if e != null and t == null then v.error(self, "Error: Return with value in a procedure.") - else if n_expr != null and t != null then - v.check_conform_expr(n_expr, t) + else if e != null and t != null then + v.check_conform_expr(e, 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 @@ -422,14 +403,16 @@ redef class AContinueExpr 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) + v.check_conform_expr(n_expr.as(not null), 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 @@ -440,8 +423,16 @@ redef class ABreakExpr 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) + bl.add(n_expr.as(not null)) end + _is_typed = true + end +end + +redef class AAbortExpr + redef meth after_typing(v) + do + v.variable_ctx.unreash = true end end @@ -452,65 +443,109 @@ redef class AIfExpr v.visit(n_expr) v.check_conform_expr(n_expr, v.type_bool) + # Prepare 'then' context v.use_if_true_variable_ctx(n_expr) - v.visit(n_then) - # Restore variable ctx + # 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 + + # 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 the 'else' if n_else != null then + v.variable_ctx = v.variable_ctx.sub(n_else.as(not null)) v.visit(n_else) - v.variable_ctx = old_var_ctx 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 + _is_typed = true end end redef class AWhileExpr # The corresponding escapable block - readable attr _escapable: EscapableBlock + readable attr _escapable: nullable EscapableBlock redef meth accept_typing(v) do - _escapable = new EscapableBlock(self) - v.escapable_ctx.push(_escapable) - - super + 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 condition + v.visit(n_expr) v.check_conform_expr(n_expr, v.type_bool) + + # Prepare inside context (assert cond) + v.use_if_true_variable_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.variable_ctx = old_var_ctx + v.base_variable_ctx = old_base_var_ctx v.escapable_ctx.pop + _is_typed = true end end redef class AForExpr - # The corresponding escapable block - readable attr _escapable: EscapableBlock + attr _variable: nullable AutoVariable + redef meth variable do return _variable.as(not null) - readable attr _meth_iterator: MMMethod - readable attr _meth_is_ok: MMMethod - readable attr _meth_item: MMMethod - readable attr _meth_next: MMMethod + # The corresponding escapable block + readable attr _escapable: nullable EscapableBlock + + attr _meth_iterator: nullable MMMethod + meth meth_iterator: MMMethod do return _meth_iterator.as(not null) + attr _meth_is_ok: nullable MMMethod + meth meth_is_ok: MMMethod do return _meth_is_ok.as(not null) + attr _meth_item: nullable MMMethod + meth meth_item: MMMethod do return _meth_item.as(not null) + attr _meth_next: nullable MMMethod + meth meth_next: MMMethod do return _meth_next.as(not null) redef meth accept_typing(v) do - _escapable = new EscapableBlock(self) - v.escapable_ctx.push(_escapable) + var escapable = new EscapableBlock(self) + _escapable = escapable + v.escapable_ctx.push(escapable) - v.variable_ctx = v.variable_ctx.sub + 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) - variable = va + _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 - return - end + _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 + 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") @@ -533,11 +568,10 @@ redef class AForExpr if n_block != null then v.visit(n_block) # pop context - var varctx = v.variable_ctx - assert varctx isa SubVariableContext - v.variable_ctx = varctx.prev - + v.variable_ctx = old_var_ctx + v.base_variable_ctx = old_base_var_ctx v.escapable_ctx.pop + _is_typed = true end end @@ -546,56 +580,87 @@ redef class AAssertExpr do v.check_conform_expr(n_expr, v.type_bool) v.use_if_true_variable_ctx(n_expr) + _is_typed = true end end +redef class AVarFormExpr + attr _variable: nullable Variable + redef meth variable do return _variable.as(not null) +end + redef class AVarExpr redef meth its_variable do return variable 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) + + # Check the base type + var btype = v.base_variable_ctx.stype(variable) + if not v.check_conform_expr(n_value, btype) then return + + # Always cast + v.variable_ctx.stype(variable) = n_value.stype + + _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: nullable MMType): nullable MMType do if type_lvalue == null then - return + return null end var name = n_assign_op.method_name 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) - readable attr _assign_method: MMMethod + readable attr _assign_method: nullable MMMethod 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 + + # Check the base type + var btype = v.base_variable_ctx.stype(variable) + if not v.check_conform(n_value, t2, btype) then return + + # Always cast + v.variable_ctx.stype(variable) = t2 + + _is_typed = true end end @@ -610,12 +675,16 @@ redef class AMinusAssignOp end redef class ASelfExpr + attr _variable: nullable ParamVariable + redef meth variable do return _variable.as(not null) + redef meth its_variable do return variable redef meth after_typing(v) do - variable = v.self_var + _variable = v.self_var _stype = v.variable_ctx.stype(variable) + _is_typed = true end redef meth is_self do return true @@ -634,11 +703,13 @@ redef class AIfexprExpr 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) v.check_conform_expr(n_expr, v.type_bool) _stype = v.check_conform_multiexpr(null, [n_then, n_else]) + _is_typed = _stype != null end end @@ -646,15 +717,31 @@ redef class ABoolExpr redef meth after_typing(v) do _stype = v.type_bool + _is_typed = true end end redef class AOrExpr - redef meth after_typing(v) + redef meth accept_typing(v) do + var old_var_ctx = v.variable_ctx + + v.visit(n_expr) + v.use_if_false_variable_ctx(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 + else + _if_false_variable_ctx = v.variable_ctx + end + + v.variable_ctx = old_var_ctx + 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 @@ -678,6 +765,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 @@ -685,7 +773,13 @@ redef class ANotExpr redef meth after_typing(v) do 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 + _stype = v.type_bool + _is_typed = true end end @@ -693,7 +787,7 @@ redef class AIntExpr redef meth after_typing(v) do _stype = v.type_int - + _is_typed = true end end @@ -701,6 +795,7 @@ redef class AFloatExpr redef meth after_typing(v) do _stype = v.type_float + _is_typed = true end end @@ -708,34 +803,43 @@ 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 + attr _meth_with_native: nullable MMMethod + meth meth_with_native: MMMethod do return _meth_with_native.as(not null) 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 + meth meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null) + attr _meth_with_capacity: nullable MMMethod + meth meth_add: MMMethod do return _meth_add.as(not null) + attr _meth_add: nullable MMMethod + meth meth_to_s: MMMethod do return _meth_to_s.as(not null) + attr _meth_to_s: nullable MMMethod + readable attr _atype: nullable 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) + var stype = v.type_string + _stype = stype + 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) + _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 @@ -743,18 +847,20 @@ redef class ANullExpr redef meth after_typing(v) do _stype = v.type_none + _is_typed = true end end redef class AArrayExpr - readable attr _meth_with_capacity: MMMethod - readable attr _meth_add: MMMethod + meth meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null) + attr _meth_with_capacity: nullable MMMethod + meth meth_add: MMMethod do return _meth_add.as(not null) + attr _meth_add: nullable MMMethod redef meth after_typing(v) do var stype = v.check_conform_multiexpr(null, n_exprs) - if stype == null then return - do_typing(v, stype) + if stype != null then do_typing(v, stype) end private meth do_typing(v: TypingVisitor, element_type: MMType) @@ -765,18 +871,19 @@ redef class AArrayExpr 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 + meth meth_init: MMMethod do return _meth_init.as(not null) + attr _meth_init: nullable 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 @@ -784,9 +891,9 @@ 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 @@ -809,7 +916,7 @@ end redef class ASuperExpr special ASuperInitCall # readable attr _prop: MMSrcMethod - readable attr _init_in_superclass: MMMethod + readable attr _init_in_superclass: nullable MMMethod redef meth after_typing(v) do var precs: Array[MMLocalProperty] = v.local_property.prhe.direct_greaters @@ -836,7 +943,7 @@ special ASuperInitCall _init_in_superclass = p register_super_init_call(v, p) if n_args.length > 0 then - var signature = get_signature(v, v.self_var.stype, p, true) + 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) end else @@ -844,34 +951,35 @@ special ASuperInitCall return end - if precs.first.signature_for(v.self_var.stype).return_type != null then + if precs.first.signature_for(v.self_var.stype.as(not null)).return_type != null then var stypes = new Array[MMType] - var stype: MMType = null + var stype: nullable MMType = null for prop in precs do assert prop isa MMMethod - var t = prop.signature_for(v.self_var.stype).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.module).adapt_to(v.local_property.signature.recv) stypes.add(t) if stype == null or stype < t then stype = t end end for t in stypes do - v.check_conform(self, t, stype) + v.check_conform(self, t, stype.as(not null)) end _stype = stype end var p = v.local_property assert p isa MMSrcMethod _prop = p + _is_typed = true end end redef class AAttrFormExpr # Attribute accessed - readable attr _prop: MMAttribute + readable attr _prop: nullable MMAttribute # Attribute type of the acceded attribute - readable attr _attr_type: MMType + readable attr _attr_type: nullable MMType # Compute the attribute accessed private meth do_typing(v: TypingVisitor) @@ -899,10 +1007,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 @@ -910,10 +1017,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 @@ -921,74 +1027,37 @@ redef class AAttrReassignExpr redef meth after_typing(v) do do_typing(v) - if prop == null then - return - end - do_lvalue_typing(v, attr_type) + 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 AAbsSendExpr -special PExpr - # The signature of the called property - readable attr _prop_signature: MMSignature - - # 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], closure_defs: Array[PClosureDef]) +redef class AIssetAttrExpr + redef meth after_typing(v) do - var prop = get_property(v, type_recv, is_implicit_self, name) + do_typing(v) 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.name, raw_args) - if args == null then return - var rtype = process_closures(v, sig, prop.name, closure_defs) - _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 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 - 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] - assert p isa MMMethod - prop = p - end - - end - if prop == null then - if is_implicit_self then - v.error(self, "Error: Method or variable '{name}' unknown in {type_recv}.") - else - v.error(self, "Error: Method '{name}' doesn't exists in {type_recv}.") - end - return null + if attr_type.is_nullable then + v.error(self, "Error: isset on a nullable attribute.") end - return prop + _stype = v.type_bool + _is_typed = true end +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) - var psig = prop.signature_for(type_recv) - if not recv_is_self then psig = psig.not_for_self - return psig - end +class AAbsAbsSendExpr +special PExpr + # The signature of the called property + readable attr _prop_signature: nullable MMSignature + + # The real arguments used (after star transformation) (once computed) + readable attr _arguments: nullable 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] + private meth process_signature(v: TypingVisitor, psig: MMSignature, name: Symbol, raw_args: nullable Array[PExpr]): nullable Array[PExpr] do var par_vararg = psig.vararg_rank var par_arity = psig.arity @@ -1025,7 +1094,7 @@ special PExpr end # Check the conformity of a set of defined closures - private meth process_closures(v: TypingVisitor, psig: MMSignature, name: Symbol, cd: Array[PClosureDef]): MMType + private meth process_closures(v: TypingVisitor, psig: MMSignature, name: Symbol, cd: nullable Array[PClosureDef]): nullable MMType do var t = psig.return_type var cs = psig.closures # Declared closures @@ -1040,7 +1109,7 @@ special PExpr 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 + var break_list: nullable Array[ABreakExpr] = null if t != null then break_list = new Array[ABreakExpr] # Process each closure definition @@ -1063,15 +1132,68 @@ special PExpr end return t end +end - # The invoked method (once computed) - readable attr _prop: MMMethod +class AAbsSendExpr +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: nullable Array[PExpr], closure_defs: nullable 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) + 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 - # The real arguments used (after star transformation) (once computed) - readable attr _arguments: Array[PExpr] + private meth get_property(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, name: Symbol): nullable MMMethod + do + 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) + 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] + assert p isa MMMethod + prop = p + end + + end + if prop == null then + if is_implicit_self then + v.error(self, "Error: Method or variable '{name}' unknown in {type_recv}.") + else + v.error(self, "Error: Method '{name}' doesn't exists in {type_recv}.") + end + return null + end + 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) + 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 attr _prop: nullable MMMethod # The return type (if any) (once computed) - readable attr _return_type: MMType + readable attr _return_type: nullable MMType end # A possible call of constructor in a super class @@ -1084,9 +1206,10 @@ special AAbsSendExpr 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 prev_class: MMLocalClass = null - if not v.explicit_super_init_calls.is_empty then - prev_class = v.explicit_super_init_calls.last.global.intro.local_class + var prev_class: nullable MMLocalClass = null + var esic = v.explicit_super_init_calls.as(not null) + if not esic.is_empty then + prev_class = esic.last.global.intro.local_class end var order = v.local_class.cshe.reverse_linear_extension if cla == v.local_class then @@ -1104,7 +1227,7 @@ special AAbsSendExpr if not last_is_found then v.error(self, "Error: Constructor of {c} must be invoked before constructor of {prev_class}") end - v.explicit_super_init_calls.add(property) + esic.add(property) break end end @@ -1117,8 +1240,8 @@ redef class ANewExpr special AAbsSendExpr redef meth after_typing(v) do + if n_type._stype == null then return var t = n_type.stype - if t == null then return if t.local_class.global.is_abstract then v.error(self, "Error: try to instantiate abstract class {t.local_class}.") return @@ -1135,8 +1258,10 @@ special AAbsSendExpr 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 @@ -1147,10 +1272,10 @@ special ASuperInitCall meth name: Symbol is abstract # Raw arguments used (withour star transformation) - meth raw_arguments: Array[PExpr] is abstract + meth raw_arguments: nullable Array[PExpr] is abstract # Closure definitions - meth closure_defs: Array[PClosureDef] do return null + meth closure_defs: nullable Array[PClosureDef] do return null redef meth after_typing(v) do @@ -1161,7 +1286,8 @@ special ASuperInitCall 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, closure_defs) - if prop == null then return + if _prop == null then return + var prop = _prop.as(not null) if prop.global.is_init then if not v.local_property.global.is_init then @@ -1174,18 +1300,20 @@ special ASuperInitCall end _stype = return_type + _is_typed = true end end class ASendReassignExpr special ASendExpr special AReassignFormExpr - readable attr _read_prop: MMMethod + readable attr _read_prop: nullable MMMethod redef meth do_all_typing(v) 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, null) + var prop = _prop if prop == null then return if prop.global.is_init then if not v.local_property.global.is_init then @@ -1194,17 +1322,18 @@ special AReassignFormExpr v.error(self, "Error: constructor {prop} is not invoken on 'self'.") end end - var t = prop.signature_for(n_expr.stype).return_type + var t = prop.signature_for(n_expr.stype).return_type.as(not null) 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, null) - 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.") @@ -1214,6 +1343,7 @@ special AReassignFormExpr end _arguments = old_args # FIXME: What if star parameters do not match betwen the two methods? + _is_typed = true end end @@ -1222,9 +1352,55 @@ redef class ABinopExpr end redef class AEqExpr redef meth name do return once "==".to_symbol + redef meth after_typing(v) + do + super + if not is_typed then return + if n_expr.stype isa MMTypeNone and not n_expr2.stype.is_nullable or + n_expr2.stype isa MMTypeNone and not n_expr.stype.is_nullable then + v.warning(self, "Warning: comparaison between null and a non nullable value.") + end + + if n_expr.stype isa MMTypeNone then + try_to_isa(v, n_expr2) + else if n_expr2.stype isa MMTypeNone then + try_to_isa(v, n_expr) + end + end + + private meth try_to_isa(v: TypingVisitor, n: PExpr) + 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) + end + end end redef class ANeExpr redef meth name do return once "!=".to_symbol + redef meth after_typing(v) + do + super + if not is_typed then return + if n_expr.stype isa MMTypeNone and not n_expr2.stype.is_nullable or + n_expr2.stype isa MMTypeNone and not n_expr.stype.is_nullable then + v.warning(self, "Warning: comparaison between null and a non nullable value.") + end + + if n_expr.stype isa MMTypeNone then + try_to_isa(v, n_expr2) + else if n_expr2.stype isa MMTypeNone then + try_to_isa(v, n_expr) + end + end + + private meth try_to_isa(v: TypingVisitor, n: PExpr) + 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) + end + end end redef class ALtExpr redef meth name do return once "<".to_symbol @@ -1265,22 +1441,23 @@ end redef class ACallFormExpr redef meth after_typing(v) do - if n_expr != null and n_expr.is_implicit_self then + if n_expr.is_implicit_self then var name = n_id.to_symbol var variable = v.variable_ctx[name] if variable != null then if variable isa ClosureVariable then - var n = new AClosureCallExpr(n_id, n_args, n_closure_defs) + var n = new AClosureCallExpr.init_aclosurecallexpr(n_id, n_args, n_closure_defs) replace_with(n) - n.variable = variable + 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 + vform._variable = variable replace_with(vform) vform.after_typing(v) return @@ -1293,7 +1470,7 @@ redef class ACallFormExpr redef meth closure_defs do - if n_closure_defs == null or n_closure_defs.is_empty then + if n_closure_defs.is_empty then return null else return n_closure_defs.to_a @@ -1365,27 +1542,35 @@ redef class AInitExpr end redef class AClosureCallExpr +special AAbsAbsSendExpr + attr _variable: nullable ClosureVariable + redef meth variable do return _variable.as(not null) + 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 closure_defs != null then - process_closures(v, sig, n_id.to_symbol, closure_defs) + 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 = null _prop_signature = sig _arguments = args _stype = sig.return_type + _is_typed = true end end redef class PClosureDef + attr _closure: nullable MMClosure + redef meth closure do return _closure.as(not null) + # The corresponding escapable object - readable attr _escapable: EscapableBlock + readable attr _escapable: nullable EscapableBlock - attr _accept_typing2: Bool + attr _accept_typing2: Bool = false redef meth accept_typing(v) do # Typing is deferred, wait accept_typing2(v) @@ -1406,9 +1591,12 @@ redef class AClosureDef return end - closure = esc.closure + _closure = esc.closure - v.variable_ctx = v.variable_ctx.sub + 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) variables = new Array[AutoVariable] for i in [0..n_id.length[ do var va = new AutoVariable(n_id[i].to_symbol, self) @@ -1419,31 +1607,107 @@ redef class AClosureDef _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 + v.base_variable_ctx = old_base_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}.") + 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 + # type T: nullable A + # var x: nullable T + # 'x.as(not null)' != 'x.as(T)' + # 'x != null' != 'x isa T' + else if self isa AIsaExpr then + v.warning(self, "Warning: Prefer '!= null'.") + else + v.warning(self, "Warning: Prefer '.as(not null)'.") + end + 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 AAsNotnullExpr + redef meth after_typing(v) + do + if not v.check_expr(n_expr) then return + var t = n_expr.stype + if t isa MMTypeNone then + v.error(n_expr, "Type error: 'as(not null)' on 'null' value.") + return + else if not t.is_nullable then + v.warning(n_expr, "Warning: 'as(not null)' on non nullable type.") + end + _stype = n_expr.stype.as_notnull + _is_typed = true 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 +