X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 4aad2d2..5340c14 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -20,6 +20,7 @@ module typing import modelize import local_var_init +import literal redef class ToolContext var typing_phase: Phase = new TypingPhase(self, [flow_phase, modelize_property_phase, local_var_init_phase]) @@ -80,11 +81,6 @@ private class TypeVisitor fun anchor_to(mtype: MType): MType do - var anchor = anchor - if anchor == null then - assert not mtype.need_anchor - return mtype - end return mtype.anchor_to(mmodule, anchor) end @@ -101,9 +97,9 @@ private class TypeVisitor end # Check that `sub` is a subtype of `sup`. - # If `sub` is not a valid suptype, then display an error on `node` an return null. - # If `sub` is a safe subtype of `sup` then return `sub`. - # If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`. + # If `sub` is not a valid suptype, then display an error on `node` and return `null`. + # If `sub` is a safe subtype of `sup`, then return `sub`. + # If `sub` is an unsafe subtype (i.e., an implicit cast is required), then return `sup`. # # The point of the return type is to determinate the usable type on an expression when `autocast` is true: # If the suptype is safe, then the return type is the one on the expression typed by `sub`. @@ -116,6 +112,7 @@ private class TypeVisitor #node.debug("Unsafe typing: expected {sup}, got {sub}") return sup end + if sup isa MErrorType then return null # Skip error if sub.need_anchor then var u = anchor_to(sub) self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.") @@ -150,7 +147,11 @@ private class TypeVisitor end return null # forward error end - self.error(nexpr, "Error: expected an expression.") + var more_message = null + var p = nexpr.parent + if p != null then more_message = p.bad_expr_message(nexpr) + if more_message == null then more_message = "" else more_message = " " + more_message + self.error(nexpr, "Error: expected an expression{more_message}.") return null end @@ -184,12 +185,12 @@ private class TypeVisitor end - fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType + fun check_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType do - var sub = visit_expr(nexpr) + var sub = nexpr.mtype if sub == null then return null # Forward error - var sup = self.resolve_mtype(ntype) + var sup = ntype.mtype if sup == null then return null # Forward error if sup == sub then @@ -217,6 +218,10 @@ private class TypeVisitor # Else return true. fun check_can_be_null(anode: ANode, mtype: MType): Bool do + if mtype isa MNullType then + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is always `null`.") + return true + end if can_be_null(mtype) then return true if mtype isa MFormalType then @@ -240,7 +245,7 @@ private class TypeVisitor if not mtype2 isa MNullType then return # Check of useless null - if not check_can_be_null(anode.n_expr, mtype) then return + if not can_be_null(mtype) then return if mtype isa MNullType then # Because of type adaptation, we cannot just stop here @@ -384,7 +389,7 @@ private class TypeVisitor end end - var callsite = new CallSite(node, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) + var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) return callsite end @@ -409,26 +414,30 @@ private class TypeVisitor return null end else if args.length != msignature.arity then - if msignature.arity == msignature.min_arity then - modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") - return null - end + # Too much argument if args.length > msignature.arity then - modelbuilder.error(node, "Error: expected at most {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") - return null - end - if args.length < msignature.min_arity then - modelbuilder.error(node, "Error: expected at least {msignature.min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") return null end + # Other cases are managed later end + #debug("CALL {unsafe_type}.{msignature}") # Associate each parameter to a position in the arguments var map = new SignatureMap - var setted = args.length - msignature.min_arity + # Special case for the isolated last argument + # TODO: reify this method characteristics (where? the param, the signature, the method?) + var last_is_padded = mproperty.name.chars.last == '=' + var nbargs = args.length + if last_is_padded then + nbargs -= 1 + assert not args.last isa ANamedargExpr + map.map[msignature.arity - 1] = args.length - 1 + self.visit_expr_subtype(args.last, msignature.mparameters.last.mtype) + end # First, handle named arguments for i in [0..args.length[ do @@ -440,10 +449,6 @@ private class TypeVisitor modelbuilder.error(e.n_id, "Error: no parameter `{name}` for `{mproperty}{msignature}`.") return null end - if not param.is_default then - modelbuilder.error(e, "Error: parameter `{name}` is not optional for `{mproperty}{msignature}`.") - return null - end var idx = msignature.mparameters.index_of(param) var prev = map.map.get_or_null(idx) if prev != null then @@ -451,10 +456,12 @@ private class TypeVisitor return null end map.map[idx] = i - setted -= 1 e.mtype = self.visit_expr_subtype(e.n_expr, param.mtype) end + # Number of minimum mandatory remaining parameters + var min_arity = 0 + # Second, associate remaining parameters var vararg_decl = args.length - msignature.arity var j = 0 @@ -463,16 +470,16 @@ private class TypeVisitor if map.map.has_key(i) then continue var param = msignature.mparameters[i] - if param.is_default then - if setted > 0 then - setted -= 1 - else - continue - end - end # Search the next free argument: skip named arguments since they are already associated - while args[j] isa ANamedargExpr do j += 1 + while j < nbargs and args[j] isa ANamedargExpr do j += 1 + if j >= nbargs then + if not param.mtype isa MNullableType then + min_arity = j + 1 + end + j += 1 + continue + end var arg = args[j] map.map[i] = j j += 1 @@ -482,8 +489,22 @@ private class TypeVisitor continue # skip the vararg end - var paramtype = param.mtype - self.visit_expr_subtype(arg, paramtype) + if not param.is_vararg then + var paramtype = param.mtype + self.visit_expr_subtype(arg, paramtype) + else + check_one_vararg(arg, param) + end + end + + if min_arity > 0 then + if last_is_padded then min_arity += 1 + if min_arity < msignature.arity then + modelbuilder.error(node, "Error: expected at least {min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + else + modelbuilder.error(node, "Error: expected {min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + end + return null end # Third, check varargs @@ -491,27 +512,9 @@ private class TypeVisitor var paramtype = msignature.mparameters[vararg_rank].mtype var first = args[vararg_rank] if vararg_decl == 0 then - var mclass = get_mclass(node, "Array") - if mclass == null then return null # Forward error - var array_mtype = mclass.get_mtype([paramtype]) - if first isa AVarargExpr then - self.visit_expr_subtype(first.n_expr, array_mtype) - first.mtype = first.n_expr.mtype - else - # only one vararg, maybe `...` was forgot, so be gentle! - var t = visit_expr(first) - if t == null then return null # Forward error - if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then - # Not acceptable but could be a `...` - error(first, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?") - return null - end - # Standard valid vararg, finish the job - map.vararg_decl = 1 - self.visit_expr_subtype(first, paramtype) - end + if not check_one_vararg(first, msignature.mparameters[vararg_rank]) then return null else - map.vararg_decl = vararg_decl + 1 + first.vararg_decl = vararg_decl + 1 for i in [vararg_rank..vararg_rank+vararg_decl] do self.visit_expr_subtype(args[i], paramtype) end @@ -521,9 +524,36 @@ private class TypeVisitor return map end + # Check an expression as a single vararg. + # The main point of the method if to handle the case of reversed vararg (see `AVarargExpr`) + fun check_one_vararg(arg: AExpr, param: MParameter): Bool + do + var paramtype = param.mtype + var mclass = get_mclass(arg, "Array") + if mclass == null then return false # Forward error + var array_mtype = mclass.get_mtype([paramtype]) + if arg isa AVarargExpr then + self.visit_expr_subtype(arg.n_expr, array_mtype) + arg.mtype = arg.n_expr.mtype + else + # only one vararg, maybe `...` was forgot, so be gentle! + var t = visit_expr(arg) + if t == null then return false # Forward error + if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then + # Not acceptable but could be a `...` + error(arg, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?") + return false + end + # Standard valid vararg, finish the job + arg.vararg_decl = 1 + self.visit_expr_subtype(arg, paramtype) + end + return true + end + fun error(node: ANode, message: String) do - self.modelbuilder.toolcontext.error(node.hot_location, message) + self.modelbuilder.error(node, message) end fun get_variable(node: AExpr, variable: Variable): nullable MType @@ -545,7 +575,16 @@ private class TypeVisitor return mtypes.first else var res = merge_types(node,mtypes) - if res == null then res = variable.declared_type + if res == null then + res = variable.declared_type + # Try to fallback to a non-null version + if res != null and can_be_null(res) then do + for t in mtypes do + if t != null and can_be_null(t) then break label + end + res = res.as_notnull + end label + end return res end end @@ -565,6 +604,29 @@ private class TypeVisitor flow.set_var(self, variable, mtype) end + # Find the exact representable most specific common super-type in `col`. + # + # Try to find the most specific common type that is a super-type of each types + # in `col`. + # In most cases, the result is simply the most general type in `col`. + # If nullables types are involved, then the nullable information is correctly preserved. + # If incomparable super-types exists in `col`, them no solution is given and the `null` + # value is returned (since union types are non representable in Nit) + # + # The `null` values in `col` are ignored, nulltypes (MNullType) are considered. + # + # Returns the `null` value if: + # + # * `col` is empty + # * `col` only have null values + # * there is a conflict + # + # Example (with a diamond A,B,C,D): + # + # * merge(A,B,C) -> A, because A is the most general type in {A,B,C} + # * merge(C,B) -> null, there is conflict, because `B or C` cannot be represented + # * merge(A,nullable B) -> nullable A, because A is the most general type and + # the nullable information is preserved fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType do if col.length == 1 then return col.first @@ -586,6 +648,82 @@ private class TypeVisitor #self.modelbuilder.warning(node, "Type Error: {col.length} conflicting types: <{col.join(", ")}>") return null end + + # Find a most general common subtype between `type1` and `type2`. + # + # Find the most general type that is a subtype of `type2` and, if possible, a subtype of `type1`. + # Basically, this return the most specific type between `type1` and `type2`. + # If nullable types are involved, the information is correctly preserved. + # If `type1` and `type2` are incomparable then `type2` is preferred (since intersection types + # are not representable in Nit). + # + # The `null` value is returned if both `type1` and `type2` are null. + # + # Examples (with diamond A,B,C,D): + # + # * intersect_types(A,B) -> B, because B is a subtype of A + # * intersect_types(B,A) -> B, because B is a subtype of A + # * intersect_types(B,C) -> C, B and C are incomparable, + # `type2` is then preferred (`B and C` cannot be represented) + # * intersect_types(nullable B,A) -> B, because B<:A and the non-null information is preserved + # * intersect_types(B,nullable C) -> C, `type2` is preferred and the non-null information is preserved + fun intersect_types(node: ANode, type1, type2: nullable MType): nullable MType + do + if type1 == null then return type2 + if type2 == null then return type1 + + if not can_be_null(type2) or not can_be_null(type1) then + type1 = type1.as_notnull + type2 = type2.as_notnull + end + + var res + if is_subtype(type1, type2) then + res = type1 + else + res = type2 + end + return res + end + + # Find a most general type that is a subtype of `type1` but not one of `type2`. + # + # Basically, this returns `type1`-`type2` but since there is no substraction type + # in Nit this just returns `type1` most of the case. + # + # The few other cases are if `type2` is a super-type and if some nullable information + # is present. + # + # The `null` value is returned if `type1` is null. + # + # Examples (with diamond A,B,C,D): + # + # * diff_types(A,B) -> A, because the notB cannot be represented + # * diff_types(B,A) -> None (absurd type), because B<:A + # * diff_types(nullable A, nullable B) -> A, because null is removed + # * diff_types(nullable B, A) -> Null, because anything but null is removed + fun diff_types(node: ANode, type1, type2: nullable MType): nullable MType + do + if type1 == null then return null + if type2 == null then return type1 + + # if t1 <: t2 then t1-t2 = bottom + if is_subtype(type1, type2) then + return modelbuilder.model.null_type.as_notnull + end + + # else if t1 <: nullable t2 then t1-t2 = nulltype + if is_subtype(type1, type2.as_nullable) then + return modelbuilder.model.null_type + end + + # else t2 can be null and type2 must accept null then null is excluded in t1 + if can_be_null(type1) and (type2 isa MNullableType or type2 isa MNullType) then + return type1.as_notnull + end + + return type1 + end end # Mapping between parameters and arguments in a call. @@ -596,16 +734,13 @@ end class SignatureMap # Associate a parameter to an argument var map = new ArrayMap[Int, Int] - - # The length of the vararg sequence - # 0 if no vararg or if reverse vararg (cf `AVarargExpr`) - var vararg_decl: Int = 0 end # A specific method call site with its associated informations. class CallSite - # The associated node for location - var node: ANode + super MEntity + + redef var location # The static type of the receiver (possibly unresolved) var recv: MType @@ -638,17 +773,25 @@ class CallSite # If null then no specific association is required. var signaturemap: nullable SignatureMap = null - private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool + private fun check_signature(v: TypeVisitor, node: ANode, args: Array[AExpr]): Bool do - var map = v.check_signature(self.node, args, self.mproperty, self.msignature) + var map = v.check_signature(node, args, self.mproperty, self.msignature) signaturemap = map + if map == null then is_broken = true return map == null end + + # Information about the callsite to display on a node + fun dump_info(v: ASTDump): String do + return "{recv}.{mpropdef}{msignature}" + end + + redef fun mdoc_or_fallback do return mproperty.intro.mdoc end redef class Variable # The declared type of the variable - var declared_type: nullable MType + var declared_type: nullable MType = null is writable # Was the variable type-adapted? # This is used to speedup type retrieval while it remains `false` @@ -718,9 +861,6 @@ end redef class AMethPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var nblock = self.n_block - if nblock == null then return - var mpropdef = self.mpropdef if mpropdef == null then return # skip error @@ -742,12 +882,18 @@ redef class AMethPropdef variable.declared_type = mtype end + var nblock = self.n_block + if nblock == null then return + loop v.dirty = false v.visit_stmt(nblock) if not v.has_loop or not v.dirty then break end + var post_visitor = new PostTypingVisitor(v) + post_visitor.enter_visit(self) + if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then # We reach the end of the function without having a return, it is bad v.error(self, "Error: reached end of function; expected `return` with a value.") @@ -755,20 +901,42 @@ redef class AMethPropdef end end +private class PostTypingVisitor + super Visitor + var type_visitor: TypeVisitor + redef fun visit(n) do + n.visit_all(self) + n.accept_post_typing(type_visitor) + if n isa AExpr and n.mtype == null and not n.is_typed then + n.is_broken = true + end + end +end + +redef class ANode + private fun accept_post_typing(v: TypeVisitor) do end + + # An additional information message to explain the role of a child expression. + # + # The point of the method is to allow some kind of double dispatch so the parent + # choose how to describe its children. + private fun bad_expr_message(child: AExpr): nullable String do return null +end + redef class AAttrPropdef redef fun do_typing(modelbuilder: ModelBuilder) do if not has_value then return - var mpropdef = self.mpropdef - if mpropdef == null then return # skip error + var mpropdef = self.mreadpropdef + if mpropdef == null or mpropdef.msignature == null then return # skip error var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable var nexpr = self.n_expr if nexpr != null then - var mtype = self.mpropdef.static_mtype + var mtype = self.mtype v.visit_expr_subtype(nexpr, mtype) end var nblock = self.n_block @@ -813,6 +981,28 @@ redef class AExpr # The result of the evaluation of `self` must be # stored inside the designated array (there is an implicit `push`) var comprehension: nullable AArrayExpr = null + + # It indicates the number of arguments collected as a vararg. + # + # When 0, the argument is used as is, without transformation. + # When 1, the argument is transformed into an singleton array. + # Above 1, the arguments and the next ones are transformed into a common array. + # + # This attribute is meaning less on expressions not used as attributes. + var vararg_decl: Int = 0 + + redef fun dump_info(v) do + var res = super + var mtype = self.mtype + if mtype != null then + res += v.yellow(":{mtype}") + end + var ict = self.implicit_cast_to + if ict != null then + res += v.yellow("(.as({ict}))") + end + return res + end end redef class ABlockExpr @@ -1063,6 +1253,7 @@ redef class ADoExpr redef fun accept_typing(v) do v.visit_stmt(n_block) + v.visit_stmt(n_catch) self.is_typed = true end end @@ -1087,6 +1278,25 @@ redef class ALoopExpr end redef class AForExpr + redef fun accept_typing(v) + do + v.has_loop = true + + for g in n_groups do + var mtype = v.visit_expr(g.n_expr) + if mtype == null then return + g.do_type_iterator(v, mtype) + if g.is_broken then is_broken = true + end + + v.visit_stmt(n_block) + + self.mtype = n_block.mtype + self.is_typed = true + end +end + +redef class AForGroup var coltype: nullable MClassType var method_iterator: nullable CallSite @@ -1213,20 +1423,6 @@ redef class AForExpr self.method_successor = v.get_method(self, vtype, "successor", false) end end - - redef fun accept_typing(v) - do - v.has_loop = true - var mtype = v.visit_expr(n_expr) - if mtype == null then return - - self.do_type_iterator(v, mtype) - - v.visit_stmt(n_block) - - self.mtype = n_block.mtype - self.is_typed = true - end end redef class AWithExpr @@ -1304,8 +1500,9 @@ redef class AOrElseExpr end if t1 isa MNullType then - v.error(n_expr, "Type Error: `or else` on `null`.") - else if v.check_can_be_null(n_expr, t1) then + self.mtype = t2 + return + else if v.can_be_null(t1) then t1 = t1.as_notnull end @@ -1321,35 +1518,51 @@ redef class AOrElseExpr end self.mtype = t end -end -redef class ATrueExpr - redef fun accept_typing(v) + redef fun accept_post_typing(v) do - self.mtype = v.type_bool(self) + var t1 = n_expr.mtype + if t1 == null then + return + else + v.check_can_be_null(n_expr, t1) + end end end -redef class AFalseExpr +redef class ATrueExpr redef fun accept_typing(v) do self.mtype = v.type_bool(self) end end -redef class AIntExpr +redef class AFalseExpr redef fun accept_typing(v) do - var mclass = v.get_mclass(self, "Int") - if mclass == null then return # Forward error - self.mtype = mclass.mclass_type + self.mtype = v.type_bool(self) end end -redef class AByteExpr +redef class AIntegerExpr redef fun accept_typing(v) do - var mclass = v.get_mclass(self, "Byte") + var mclass: nullable MClass = null + if value isa Byte then + mclass = v.get_mclass(self, "Byte") + else if value isa Int then + mclass = v.get_mclass(self, "Int") + else if value isa Int8 then + mclass = v.get_mclass(self, "Int8") + else if value isa Int16 then + mclass = v.get_mclass(self, "Int16") + else if value isa UInt16 then + mclass = v.get_mclass(self, "UInt16") + else if value isa Int32 then + mclass = v.get_mclass(self, "Int32") + else if value isa UInt32 then + mclass = v.get_mclass(self, "UInt32") + end if mclass == null then return # Forward error self.mtype = mclass.mclass_type end @@ -1365,29 +1578,73 @@ redef class AFloatExpr end redef class ACharExpr - redef fun accept_typing(v) - do - var mclass = v.get_mclass(self, "Char") + redef fun accept_typing(v) do + var mclass: nullable MClass = null + if is_ascii then + mclass = v.get_mclass(self, "Byte") + else if is_code_point then + mclass = v.get_mclass(self, "Int") + else + mclass = v.get_mclass(self, "Char") + end if mclass == null then return # Forward error self.mtype = mclass.mclass_type end end -redef class AStringFormExpr - redef fun accept_typing(v) - do +redef class AugmentedStringFormExpr + super AExpr + + # Text::to_re, used for prefix `re` + var to_re: nullable CallSite = null + # Regex::ignore_case, used for suffix `i` on `re` + var ignore_case: nullable CallSite = null + # Regex::newline, used for suffix `m` on `re` + var newline: nullable CallSite = null + # Regex::extended, used for suffix `b` on `re` + var extended: nullable CallSite = null + # CString::to_bytes_with_copy, used for prefix `b` + var to_bytes_with_copy: nullable CallSite = null + + redef fun accept_typing(v) do var mclass = v.get_mclass(self, "String") if mclass == null then return # Forward error - self.mtype = mclass.mclass_type + if is_bytestring then + to_bytes_with_copy = v.get_method(self, v.mmodule.c_string_type, "to_bytes_with_copy", false) + mclass = v.get_mclass(self, "Bytes") + else if is_re then + to_re = v.get_method(self, mclass.mclass_type, "to_re", false) + for i in suffix.chars do + mclass = v.get_mclass(self, "Regex") + if mclass == null then + v.error(self, "Error: `Regex` class unknown") + return + end + var service = "" + if i == 'i' then + service = "ignore_case=" + ignore_case = v.get_method(self, mclass.mclass_type, service, false) + else if i == 'm' then + service = "newline=" + newline = v.get_method(self, mclass.mclass_type, service, false) + else if i == 'b' then + service = "extended=" + extended = v.get_method(self, mclass.mclass_type, service, false) + else + v.error(self, "Type Error: Unrecognized suffix {i} in prefixed Regex") + abort + end + end + end + if mclass == null then return # Forward error + mtype = mclass.mclass_type end end redef class ASuperstringExpr redef fun accept_typing(v) do - var mclass = v.get_mclass(self, "String") - if mclass == null then return # Forward error - self.mtype = mclass.mclass_type + super var objclass = v.get_mclass(self, "Object") if objclass == null then return # Forward error var objtype = objclass.mclass_type @@ -1524,26 +1781,62 @@ redef class AIsaExpr var cast_type: nullable MType redef fun accept_typing(v) do - var mtype = v.visit_expr_cast(self, self.n_expr, self.n_type) + v.visit_expr(n_expr) + + var mtype = v.resolve_mtype(n_type) + self.cast_type = mtype var variable = self.n_expr.its_variable if variable != null then - #var orig = self.n_expr.mtype + var orig = self.n_expr.mtype #var from = if orig != null then orig.to_s else "invalid" #var to = if mtype != null then mtype.to_s else "invalid" #debug("adapt {variable}: {from} -> {to}") - self.after_flow_context.when_true.set_var(v, variable, mtype) + + var thentype = v.intersect_types(self, orig, mtype) + if thentype != orig then + self.after_flow_context.when_true.set_var(v, variable, thentype) + #debug "{variable}:{orig or else "?"} isa {mtype or else "?"} -> then {thentype or else "?"}" + end + + var elsetype = v.diff_types(self, orig, mtype) + if elsetype != orig then + self.after_flow_context.when_false.set_var(v, variable, elsetype) + #debug "{variable}:{orig or else "?"} isa {mtype or else "?"} -> else {elsetype or else "?"}" + end end self.mtype = v.type_bool(self) end + + redef fun accept_post_typing(v) + do + v.check_expr_cast(self, self.n_expr, self.n_type) + end + + redef fun dump_info(v) do + var res = super + var mtype = self.cast_type + if mtype != null then + res += v.yellow(".as({mtype})") + end + return res + end + end redef class AAsCastExpr redef fun accept_typing(v) do - self.mtype = v.visit_expr_cast(self, self.n_expr, self.n_type) + v.visit_expr(n_expr) + + self.mtype = v.resolve_mtype(n_type) + end + + redef fun accept_post_typing(v) + do + v.check_expr_cast(self, self.n_expr, self.n_type) end end @@ -1558,12 +1851,19 @@ redef class AAsNotnullExpr return end - if v.check_can_be_null(n_expr, mtype) then + if v.can_be_null(mtype) then mtype = mtype.as_notnull end self.mtype = mtype end + + redef fun accept_post_typing(v) + do + var mtype = n_expr.mtype + if mtype == null then return + v.check_can_be_null(n_expr, mtype) + end end redef class AParExpr @@ -1608,6 +1908,14 @@ redef class ASendExpr # The property invoked by the send. var callsite: nullable CallSite + redef fun bad_expr_message(child) + do + if child == self.n_expr then + return "to be the receiver of `{self.property_name}`" + end + return null + end + redef fun accept_typing(v) do var nrecv = self.n_expr @@ -1648,7 +1956,7 @@ redef class ASendExpr var args = compute_raw_arguments - callsite.check_signature(v, args) + callsite.check_signature(v, node, args) if callsite.mproperty.is_init then var vmpropdef = v.mpropdef @@ -1681,6 +1989,15 @@ redef class ASendExpr fun raw_arguments: Array[AExpr] do return compute_raw_arguments private fun compute_raw_arguments: Array[AExpr] is abstract + + redef fun dump_info(v) do + var res = super + var callsite = self.callsite + if callsite != null then + res += v.yellow(" call="+callsite.dump_info(v)) + end + return res + end end redef class ABinopExpr @@ -1688,18 +2005,24 @@ redef class ABinopExpr redef fun property_name do return operator redef fun property_node do return n_op end -redef class AEqExpr + +redef class AEqFormExpr redef fun accept_typing(v) do super v.null_test(self) end -end -redef class ANeExpr - redef fun accept_typing(v) + + redef fun accept_post_typing(v) do - super - v.null_test(self) + var mtype = n_expr.mtype + var mtype2 = n_expr2.mtype + + if mtype == null or mtype2 == null then return + + if not mtype2 isa MNullType then return + + v.check_can_be_null(n_expr, mtype) end end @@ -1710,14 +2033,14 @@ end redef class ACallExpr - redef fun property_name do return n_id.text - redef fun property_node do return n_id + redef fun property_name do return n_qid.n_id.text + redef fun property_node do return n_qid redef fun compute_raw_arguments do return n_args.to_a end redef class ACallAssignExpr - redef fun property_name do return n_id.text + "=" - redef fun property_node do return n_id + redef fun property_name do return n_qid.n_id.text + "=" + redef fun property_node do return n_qid redef fun compute_raw_arguments do var res = n_args.to_a @@ -1761,7 +2084,7 @@ redef class ASendReassignFormExpr var args = compute_raw_arguments - callsite.check_signature(v, args) + callsite.check_signature(v, node, args) var readtype = callsite.msignature.return_mtype if readtype == null then @@ -1778,15 +2101,15 @@ redef class ASendReassignFormExpr args = args.to_a # duplicate so raw_arguments keeps only the getter args args.add(self.n_value) - wcallsite.check_signature(v, args) + wcallsite.check_signature(v, node, args) self.is_typed = true end end redef class ACallReassignExpr - redef fun property_name do return n_id.text - redef fun property_node do return n_id + redef fun property_name do return n_qid.n_id.text + redef fun property_node do return n_qid.n_id redef fun compute_raw_arguments do return n_args.to_a end @@ -1899,12 +2222,12 @@ redef class ASuperExpr var msignature = superprop.new_msignature or else superprop.msignature.as(not null) msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) - var callsite = new CallSite(self, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) + var callsite = new CallSite(hot_location, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) self.callsite = callsite var args = self.n_args.to_a if args.length > 0 then - callsite.check_signature(v, args) + callsite.check_signature(v, self, args) else # Check there is at least enough parameters if mpropdef.msignature.arity < msignature.arity then @@ -1925,6 +2248,19 @@ redef class ASuperExpr self.is_typed = true end + + redef fun dump_info(v) do + var res = super + var callsite = self.callsite + if callsite != null then + res += v.yellow(" super-init="+callsite.dump_info(v)) + end + var mpropdef = self.mpropdef + if mpropdef != null then + res += v.yellow(" call-next-method="+mpropdef.to_s) + end + return res + end end #### @@ -1958,11 +2294,11 @@ redef class ANewExpr var kind = recvtype.mclass.kind var name: String - var nid = self.n_id + var nqid = self.n_qid var node: ANode - if nid != null then - name = nid.text - node = nid + if nqid != null then + name = nqid.n_id.text + node = nqid else name = "new" node = self.n_kwnew @@ -2003,7 +2339,16 @@ redef class ANewExpr end var args = n_args.to_a - callsite.check_signature(v, args) + callsite.check_signature(v, node, args) + end + + redef fun dump_info(v) do + var res = super + var callsite = self.callsite + if callsite != null then + res += v.yellow(" call="+callsite.dump_info(v)) + end + return res end end @@ -2045,6 +2390,16 @@ redef class AAttrFormExpr attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr) self.attr_type = attr_type end + + redef fun dump_info(v) do + var res = super + var mproperty = self.mproperty + var attr_type = self.attr_type + if mproperty != null then + res += v.yellow(" attr={mproperty}:{attr_type or else "BROKEN"}") + end + return res + end end redef class AAttrExpr