X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 212f43e..9676168 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -105,22 +105,22 @@ private class TypeVisitor # 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`. # - # The point of the return type is to determinate the usable type on an expression: + # 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`. # Is the subtype is unsafe, then the return type is the one of an implicit cast on `sup`. - fun check_subtype(node: ANode, sub, sup: MType): nullable MType + fun check_subtype(node: ANode, sub, sup: MType, autocast: Bool): nullable MType do if self.is_subtype(sub, sup) then return sub - if self.is_subtype(sub, self.anchor_to(sup)) then + if autocast and self.is_subtype(sub, self.anchor_to(sup)) then # FIXME workaround to the current unsafe typing policy. To remove once fixed virtual types exists. #node.debug("Unsafe typing: expected {sup}, got {sub}") return sup end if sub.need_anchor then var u = anchor_to(sub) - self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}") + self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.") else - self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}`.") end return null end @@ -150,7 +150,7 @@ private class TypeVisitor end return null # forward error end - self.error(nexpr, "Type error: expected expression.") + self.error(nexpr, "Error: expected an expression.") return null end @@ -166,7 +166,7 @@ private class TypeVisitor if sup == null then return null # Forward error - var res = check_subtype(nexpr, sub, sup) + var res = check_subtype(nexpr, sub, sup, true) if res != sub then nexpr.implicit_cast_to = res end @@ -193,9 +193,9 @@ private class TypeVisitor if sup == null then return null # Forward error if sup == sub then - self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup}.") + self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}`.") else if self.is_subtype(sub, sup) then - self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup} since it is a {sub}.") + self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}` since it is a `{sub}`.") end return sup end @@ -239,12 +239,16 @@ private class TypeVisitor if not mtype2 isa MNullType then return - if mtype isa MNullType then return - # Check of useless null if not check_can_be_null(anode.n_expr, mtype) then return - mtype = mtype.as_notnull + if mtype isa MNullType then + # Because of type adaptation, we cannot just stop here + # so return use `null` as a bottom type that will be merged easily (cf) `merge_types` + mtype = null + else + mtype = mtype.as_notnull + end # Check for type adaptation var variable = anode.n_expr.its_variable @@ -297,15 +301,9 @@ private class TypeVisitor #debug("recv: {recvtype} (aka {unsafe_type})") if recvtype isa MNullType then - # `null` only accepts some methods of object. - if name == "==" or name == "!=" or name == "is_same_instance" then - var objclass = get_mclass(node, "Object") - if objclass == null then return null # Forward error - unsafe_type = objclass.mclass_type - else - self.error(node, "Error: Method '{name}' call on 'null'.") - return null - end + var objclass = get_mclass(node, "Object") + if objclass == null then return null # Forward error + unsafe_type = objclass.mclass_type end var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) @@ -315,26 +313,35 @@ private class TypeVisitor end if mproperty == null then - #self.modelbuilder.error(node, "Type error: property {name} not found in {unsafe_type} (ie {recvtype})") if recv_is_self then - self.modelbuilder.error(node, "Error: Method or variable '{name}' unknown in {recvtype}.") + self.modelbuilder.error(node, "Error: method or variable `{name}` unknown in `{recvtype}`.") + else if recvtype.need_anchor then + self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}: {unsafe_type}`.") else - self.modelbuilder.error(node, "Error: Method '{name}' doesn't exists in {recvtype}.") + self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}`.") end return null end assert mproperty isa MMethod + # `null` only accepts some methods of object. + if recvtype isa MNullType and not mproperty.is_null_safe then + self.error(node, "Error: method `{name}` called on `null`.") + return null + else if unsafe_type isa MNullableType and not mproperty.is_null_safe then + modelbuilder.advice(node, "call-on-nullable", "Warning: method call on a nullable receiver `{recvtype}`.") + end + if is_toplevel_context and recv_is_self and not mproperty.is_toplevel then - error(node, "Error: '{name}' is not a top-level method, thus need a receiver.") + error(node, "Error: `{name}` is not a top-level method, thus need a receiver.") end if not recv_is_self and mproperty.is_toplevel then - error(node, "Error: cannot call '{name}', a top-level method, with a receiver.") + error(node, "Error: cannot call `{name}`, a top-level method, with a receiver.") end if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then - self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self.") + self.modelbuilder.error(node, "Error: method `{name}` is protected and can only accessed by `self`.") return null end @@ -342,21 +349,21 @@ private class TypeVisitor if info != null and self.mpropdef.mproperty.deprecation == null then var mdoc = info.mdoc if mdoc != null then - self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated: {mdoc.content.first}") + self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated: {mdoc.content.first}") else - self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated.") + self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated.") end end var propdefs = mproperty.lookup_definitions(self.mmodule, unsafe_type) var mpropdef if propdefs.length == 0 then - self.modelbuilder.error(node, "Type error: no definition found for property {name} in {unsafe_type}") + self.modelbuilder.error(node, "Type Error: no definition found for property `{name}` in `{unsafe_type}`.") return null else if propdefs.length == 1 then mpropdef = propdefs.first else - self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}") + self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property `{name}` in `{unsafe_type}`: {propdefs.join(" ")}") mpropdef = mproperty.intro end @@ -393,48 +400,125 @@ private class TypeVisitor # Visit the expressions of args and check their conformity with the corresponding type in signature # The point of this method is to handle varargs correctly # Note: The signature must be correctly adapted - fun check_signature(node: ANode, args: Array[AExpr], name: String, msignature: MSignature): Bool + fun check_signature(node: ANode, args: Array[AExpr], mproperty: MProperty, msignature: MSignature): nullable SignatureMap do var vararg_rank = msignature.vararg_rank if vararg_rank >= 0 then if args.length < msignature.arity then - #self.modelbuilder.error(node, "Error: Incorrect number of parameters. Got {args.length}, expected at least {msignature.arity}. Signature is {msignature}") - self.modelbuilder.error(node, "Error: arity mismatch; prototype is '{name}{msignature}'") - return false + modelbuilder.error(node, "Error: expected at least {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + return null end else if args.length != msignature.arity then - self.modelbuilder.error(node, "Error: Incorrect number of parameters. Got {args.length}, expected {msignature.arity}. Signature is {msignature}") - return false + 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 + 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}`.") + return null + end 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 + + # First, handle named arguments + for i in [0..args.length[ do + var e = args[i] + if not e isa ANamedargExpr then continue + var name = e.n_id.text + var param = msignature.mparameter_by_name(name) + if param == null then + 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 + modelbuilder.error(e, "Error: parameter `{name}` already associated with argument #{prev} for `{mproperty}{msignature}`.") + return null + end + map.map[idx] = i + setted -= 1 + e.mtype = self.visit_expr_subtype(e.n_expr, param.mtype) + end + + # Second, associate remaining parameters var vararg_decl = args.length - msignature.arity + var j = 0 for i in [0..msignature.arity[ do - var j = i - if i == vararg_rank then continue # skip the vararg - if i > vararg_rank then - j = i + vararg_decl + # Skip parameters associated by name + 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 + var arg = args[j] + map.map[i] = j + j += 1 + + if i == vararg_rank then + j += vararg_decl + continue # skip the vararg end - var paramtype = msignature.mparameters[i].mtype - self.visit_expr_subtype(args[j], paramtype) + + var paramtype = param.mtype + self.visit_expr_subtype(arg, paramtype) end + + # Third, check varargs if vararg_rank >= 0 then var paramtype = msignature.mparameters[vararg_rank].mtype var first = args[vararg_rank] - if vararg_decl == 0 and first isa AVarargExpr then + if vararg_decl == 0 then var mclass = get_mclass(node, "Array") - if mclass == null then return false # Forward error + if mclass == null then return null # Forward error var array_mtype = mclass.get_mtype([paramtype]) - self.visit_expr_subtype(first.n_expr, array_mtype) - first.mtype = first.n_expr.mtype + 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 else - for j in [vararg_rank..vararg_rank+vararg_decl] do - self.visit_expr_subtype(args[j], paramtype) + map.vararg_decl = vararg_decl + 1 + for i in [vararg_rank..vararg_rank+vararg_decl] do + self.visit_expr_subtype(args[i], paramtype) end end end - return true + + return map end fun error(node: ANode, message: String) @@ -444,11 +528,10 @@ private class TypeVisitor fun get_variable(node: AExpr, variable: Variable): nullable MType do + if not variable.is_adapted then return variable.declared_type + var flow = node.after_flow_context - if flow == null then - self.error(node, "No context!") - return null - end + if flow == null then return null # skip error if flow.vars.has_key(variable) then return flow.vars[variable] @@ -505,6 +588,20 @@ private class TypeVisitor end end +# Mapping between parameters and arguments in a call. +# +# Parameters and arguments are not stored in the class but referenced by their position (starting from 0) +# +# The point of this class is to help engine and other things to map arguments in the AST to parameters of the model. +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 @@ -537,15 +634,25 @@ class CallSite # Is a implicit cast required on erasure typing policy? var erasure_cast: Bool + # The mapping used on the call to associate arguments to parameters + # If null then no specific association is required. + var signaturemap: nullable SignatureMap = null + private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool do - return v.check_signature(self.node, args, self.mproperty.name, self.msignature) + var map = v.check_signature(self.node, args, self.mproperty, self.msignature) + signaturemap = map + return map == null end end redef class Variable # The declared type of the variable var declared_type: nullable MType + + # Was the variable type-adapted? + # This is used to speedup type retrieval while it remains `false` + private var is_adapted = false end redef class FlowContext @@ -557,9 +664,11 @@ redef class FlowContext # Warning2: sub-flow may have cached a unadapted variable private fun set_var(v: TypeVisitor, variable: Variable, mtype: nullable MType) do + if variable.declared_type == mtype and not variable.is_adapted then return if vars.has_key(variable) and vars[variable] == mtype then return self.vars[variable] = mtype v.dirty = true + variable.is_adapted = true #node.debug "set {variable} to {mtype or else "X"}" end @@ -641,7 +750,7 @@ redef class AMethPropdef 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, "Control error: Reached end of function (a 'return' with a value was expected).") + v.error(self, "Error: reached end of function; expected `return` with a value.") end end end @@ -651,15 +760,15 @@ redef class AAttrPropdef 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 @@ -667,7 +776,7 @@ redef class AAttrPropdef v.visit_stmt(nblock) if not nblock.after_flow_context.is_unreachable then # We reach the end of the init without having a return, it is bad - v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).") + v.error(self, "Error: reached end of block; expected `return`.") end end end @@ -813,18 +922,11 @@ redef class AReassignFormExpr # Return the static type of the value to store. private fun resolve_reassignment(v: TypeVisitor, readtype, writetype: MType): nullable MType do - var reassign_name: String - if self.n_assign_op isa APlusAssignOp then - reassign_name = "+" - else if self.n_assign_op isa AMinusAssignOp then - reassign_name = "-" - else - abort - end + var reassign_name = self.n_assign_op.operator self.read_type = readtype - var callsite = v.get_method(self, readtype, reassign_name, false) + var callsite = v.get_method(self.n_assign_op, readtype, reassign_name, false) if callsite == null then return null # Skip error self.reassign_callsite = callsite @@ -835,7 +937,7 @@ redef class AReassignFormExpr var value_type = v.visit_expr_subtype(self.n_value, msignature.mparameters.first.mtype) if value_type == null then return null # Skip error - v.check_subtype(self, rettype, writetype) + v.check_subtype(self, rettype, writetype, false) return rettype end end @@ -903,10 +1005,10 @@ redef class AReturnExpr v.visit_expr_subtype(nexpr, ret_type) else v.visit_expr(nexpr) - v.error(self, "Error: Return with value in a procedure.") + v.error(nexpr, "Error: `return` with value in a procedure.") end else if ret_type != null then - v.error(self, "Error: Return without value in a function.") + v.error(self, "Error: `return` without value in a function.") end self.is_typed = true end @@ -949,7 +1051,7 @@ redef class AIfexprExpr var t = v.merge_types(self, [t1, t2]) if t == null then - v.error(self, "Type Error: ambiguous type {t1} vs {t2}") + v.error(self, "Type Error: ambiguous type `{t1}` vs `{t2}`.") end self.mtype = t end @@ -998,7 +1100,7 @@ redef class AForExpr private fun do_type_iterator(v: TypeVisitor, mtype: MType) do if mtype isa MNullType then - v.error(self, "Type error: 'for' cannot iterate over 'null'") + v.error(self, "Type Error: `for` cannot iterate over `null`.") return end @@ -1009,7 +1111,7 @@ redef class AForExpr # check iterator method var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr) if itdef == null then - v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.") + v.error(self, "Type Error: `for` expects a type providing an `iterator` method, got `{mtype}`.") return end self.method_iterator = itdef @@ -1017,7 +1119,7 @@ redef class AForExpr # check that iterator return something var ittype = itdef.msignature.return_mtype if ittype == null then - v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.") + v.error(self, "Type Error: `for` expects the method `iterator` to return an `Iterator` or `MapIterator` type.") return end @@ -1032,7 +1134,7 @@ redef class AForExpr var coltype = ittype.supertype_to(v.mmodule, v.anchor, colit_cla) var variables = self.variables if variables.length != 1 then - v.error(self, "Type Error: 'for' expects only one variable when using 'Iterator'.") + v.error(self, "Type Error: `for` expects only one variable when using `Iterator`.") else variables.first.declared_type = coltype.arguments.first end @@ -1044,7 +1146,7 @@ redef class AForExpr var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla) var variables = self.variables if variables.length != 2 then - v.error(self, "Type Error: 'for' expects two variables when using 'MapIterator'.") + v.error(self, "Type Error: `for` expects two variables when using `MapIterator`.") else variables[0].declared_type = coltype.arguments[0] variables[1].declared_type = coltype.arguments[1] @@ -1053,7 +1155,7 @@ redef class AForExpr end if not is_col and not is_map then - v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.") + v.error(self, "Type Error: `for` expects the method `iterator` to return an `Iterator` or `MapIterator` type.") return end @@ -1066,21 +1168,21 @@ redef class AForExpr # get methods is_ok, next, item var ikdef = v.get_method(self, ittype, "is_ok", false) if ikdef == null then - v.error(self, "Type Error: 'for' expects a method 'is_ok' in 'Iterator' type {ittype}.") + v.error(self, "Type Error: `for` expects a method `is_ok` in type `{ittype}`.") return end self.method_is_ok = ikdef var itemdef = v.get_method(self, ittype, "item", false) if itemdef == null then - v.error(self, "Type Error: 'for' expects a method 'item' in 'Iterator' type {ittype}.") + v.error(self, "Type Error: `for` expects a method `item` in type `{ittype}`.") return end self.method_item = itemdef var nextdef = v.get_method(self, ittype, "next", false) if nextdef == null then - v.error(self, "Type Error: 'for' expects a method 'next' in 'Iterator' type {ittype}.") + v.error(self, "Type Error: `for` expects a method `next` in type {ittype}.") return end self.method_next = nextdef @@ -1090,7 +1192,7 @@ redef class AForExpr if is_map then var keydef = v.get_method(self, ittype, "key", false) if keydef == null then - v.error(self, "Type Error: 'for' expects a method 'key' in 'Iterator' type {ittype}.") + v.error(self, "Type Error: `for` expects a method `key` in type `{ittype}`.") return end self.method_key = keydef @@ -1200,7 +1302,7 @@ redef class AOrElseExpr end if t1 isa MNullType then - v.error(n_expr, "Type error: or else on null") + v.error(n_expr, "Type Error: `or else` on `null`.") else if v.check_can_be_null(n_expr, t1) then t1 = t1.as_notnull end @@ -1242,6 +1344,15 @@ redef class AIntExpr end end +redef class AByteExpr + redef fun accept_typing(v) + do + var mclass = v.get_mclass(self, "Byte") + if mclass == null then return # Forward error + self.mtype = mclass.mclass_type + end +end + redef class AFloatExpr redef fun accept_typing(v) do @@ -1327,13 +1438,15 @@ redef class AArrayExpr end set_comprehension(e) if mtype != null then - if v.check_subtype(e, t, mtype) == null then return # Skip error + if v.check_subtype(e, t, mtype, false) == null then return # Forward error if t == mtype then useless = true else mtypes.add(t) end end if mtype == null then + # Ensure monotony for type adaptation on loops + if self.element_mtype != null then mtypes.add self.element_mtype mtype = v.merge_types(self, mtypes) end if mtype == null or mtype isa MNullType then @@ -1377,7 +1490,7 @@ redef class ARangeExpr else if v.is_subtype(t2, t1) then mtype = mclass.get_mtype([t1]) else - v.error(self, "Type Error: Cannot create range: {t1} vs {t2}") + v.error(self, "Type Error: cannot create range: `{t1}` vs `{t2}`.") return end @@ -1439,7 +1552,7 @@ redef class AAsNotnullExpr if mtype == null then return # Forward error if mtype isa MNullType then - v.error(self, "Type error: as(not null) on null") + v.error(self, "Type Error: `as(not null)` on `null`.") return end @@ -1470,7 +1583,7 @@ redef class ASelfExpr redef fun accept_typing(v) do if v.is_toplevel_context and not self isa AImplicitSelfExpr then - v.error(self, "Error: self cannot be used in top-level method.") + v.error(self, "Error: `self` cannot be used in top-level method.") end var variable = v.selfvariable self.its_variable = variable @@ -1498,20 +1611,21 @@ redef class ASendExpr var nrecv = self.n_expr var recvtype = v.visit_expr(nrecv) var name = self.property_name + var node = self.property_node if recvtype == null then return # Forward error var callsite = null var unsafe_type = v.anchor_to(recvtype) - var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name) + var mproperty = v.try_get_mproperty_by_name2(node, unsafe_type, name) if mproperty == null and nrecv isa AImplicitSelfExpr then # Special fall-back search in `sys` when noting found in the implicit receiver. - var sysclass = v.try_get_mclass(self, "Sys") + var sysclass = v.try_get_mclass(node, "Sys") if sysclass != null then var systype = sysclass.mclass_type - mproperty = v.try_get_mproperty_by_name2(self, systype, name) + mproperty = v.try_get_mproperty_by_name2(node, systype, name) if mproperty != null then - callsite = v.get_method(self, systype, name, false) + callsite = v.get_method(node, systype, name, false) if callsite == null then return # Forward error # Update information, we are looking at `sys` now, not `self` nrecv.is_sys = true @@ -1523,7 +1637,7 @@ redef class ASendExpr end if callsite == null then # If still nothing, just exit - callsite = v.get_method(self, recvtype, name, nrecv isa ASelfExpr) + callsite = v.get_method(node, recvtype, name, nrecv isa ASelfExpr) if callsite == null then return end @@ -1537,10 +1651,10 @@ redef class ASendExpr if callsite.mproperty.is_init then var vmpropdef = v.mpropdef if not (vmpropdef isa MMethodDef and vmpropdef.mproperty.is_init) then - v.error(self, "Can call a init only in another init") + v.error(node, "Error: an `init` can only be called from another `init`.") end if vmpropdef isa MMethodDef and vmpropdef.mproperty.is_root_init and not callsite.mproperty.is_root_init then - v.error(self, "Error: {vmpropdef} cannot call a factory {callsite.mproperty}") + v.error(node, "Error: `{vmpropdef}` cannot call a factory `{callsite.mproperty}`.") end end @@ -1556,6 +1670,11 @@ redef class ASendExpr # Each subclass simply provide the correct name. private fun property_name: String is abstract + # The node identifying the name (id, operator, etc) for messages. + # + # Is `self` by default + private fun property_node: ANode do return self + # An array of all arguments (excluding self) fun raw_arguments: Array[AExpr] do return compute_raw_arguments @@ -1564,9 +1683,10 @@ end redef class ABinopExpr redef fun compute_raw_arguments do return [n_expr2] + redef fun property_name do return operator + redef fun property_node do return n_op end redef class AEqExpr - redef fun property_name do return "==" redef fun accept_typing(v) do super @@ -1574,66 +1694,28 @@ redef class AEqExpr end end redef class ANeExpr - redef fun property_name do return "!=" redef fun accept_typing(v) do super v.null_test(self) end end -redef class ALtExpr - redef fun property_name do return "<" -end -redef class ALeExpr - redef fun property_name do return "<=" -end -redef class ALlExpr - redef fun property_name do return "<<" -end -redef class AGtExpr - redef fun property_name do return ">" -end -redef class AGeExpr - redef fun property_name do return ">=" -end -redef class AGgExpr - redef fun property_name do return ">>" -end -redef class APlusExpr - redef fun property_name do return "+" -end -redef class AMinusExpr - redef fun property_name do return "-" -end -redef class AStarshipExpr - redef fun property_name do return "<=>" -end -redef class AStarExpr - redef fun property_name do return "*" -end -redef class AStarstarExpr - redef fun property_name do return "**" -end -redef class ASlashExpr - redef fun property_name do return "/" -end -redef class APercentExpr - redef fun property_name do return "%" -end -redef class AUminusExpr - redef fun property_name do return "unary -" +redef class AUnaryopExpr + redef fun property_name do return "unary {operator}" redef fun compute_raw_arguments do return new Array[AExpr] end redef class ACallExpr redef fun property_name do return n_id.text + redef fun property_node do return n_id 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 compute_raw_arguments do var res = n_args.to_a @@ -1665,11 +1747,12 @@ redef class ASendReassignFormExpr do var recvtype = v.visit_expr(self.n_expr) var name = self.property_name + var node = self.property_node if recvtype == null then return # Forward error var for_self = self.n_expr isa ASelfExpr - var callsite = v.get_method(self, recvtype, name, for_self) + var callsite = v.get_method(node, recvtype, name, for_self) if callsite == null then return self.callsite = callsite @@ -1680,11 +1763,11 @@ redef class ASendReassignFormExpr var readtype = callsite.msignature.return_mtype if readtype == null then - v.error(self, "Error: {name} is not a function") + v.error(node, "Error: `{name}` is not a function.") return end - var wcallsite = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr) + var wcallsite = v.get_method(node, recvtype, name + "=", self.n_expr isa ASelfExpr) if wcallsite == null then return self.write_callsite = wcallsite @@ -1701,6 +1784,7 @@ end redef class ACallReassignExpr redef fun property_name do return n_id.text + redef fun property_node do return n_id redef fun compute_raw_arguments do return n_args.to_a end @@ -1711,6 +1795,7 @@ end redef class AInitExpr redef fun property_name do return "init" + redef fun property_node do return n_kwinit redef fun compute_raw_arguments do return n_args.to_a end @@ -1737,7 +1822,7 @@ redef class ASuperExpr assert recvtype != null var mproperty = v.mpropdef.mproperty if not mproperty isa MMethod then - v.error(self, "Error: super only usable in a method") + v.error(self, "Error: `super` only usable in a `method`.") return end var superprops = mproperty.lookup_super_definitions(v.mmodule, anchor) @@ -1746,7 +1831,7 @@ redef class ASuperExpr process_superinit(v) return end - v.error(self, "Error: No super method to call for {mproperty}.") + v.error(self, "Error: no super method to call for `{mproperty}`.") return end # FIXME: covariance of return type in linear extension? @@ -1756,7 +1841,7 @@ redef class ASuperExpr msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) var args = self.n_args.to_a if args.length > 0 then - v.check_signature(self, args, mproperty.name, msignature) + signaturemap = v.check_signature(self, args, mproperty, msignature) end self.mtype = msignature.return_mtype self.is_typed = true @@ -1764,6 +1849,10 @@ redef class ASuperExpr mpropdef = v.mpropdef.as(MMethodDef) end + # The mapping used on the call to associate arguments to parameters. + # If null then no specific association is required. + var signaturemap: nullable SignatureMap + private fun process_superinit(v: TypeVisitor) do var anchor = v.anchor @@ -1786,7 +1875,7 @@ redef class ASuperExpr continue end if superprop != null and superprop.mproperty != candidate and not superprop.mproperty.is_root_init then - v.error(self, "Error: conflicting super constructor to call for {mproperty}: {candidate.full_name}, {superprop.mproperty.full_name}") + v.error(self, "Error: conflicting super constructor to call for `{mproperty}`: `{candidate.full_name}`, `{superprop.mproperty.full_name}`") return end var candidatedefs = candidate.lookup_definitions(v.mmodule, anchor) @@ -1795,13 +1884,13 @@ redef class ASuperExpr candidatedefs.add(superprop) end if candidatedefs.length > 1 then - v.error(self, "Error: conflicting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}") + v.error(self, "Error: conflicting property definitions for property `{mproperty}` in `{recvtype}`: {candidatedefs.join(", ")}") return end superprop = candidatedefs.first end if superprop == null then - v.error(self, "Error: No super method to call for {mproperty}.") + v.error(self, "Error: no super method to call for `{mproperty}`.") return end @@ -1817,7 +1906,7 @@ redef class ASuperExpr else # Check there is at least enough parameters if mpropdef.msignature.arity < msignature.arity then - v.error(self, "Error: Not enough implicit arguments to pass. Got {mpropdef.msignature.arity}, expected at least {msignature.arity}. Signature is {msignature}") + v.error(self, "Error: not enough implicit arguments to pass. Got `{mpropdef.msignature.arity}`, expected at least `{msignature.arity}`. Signature is `{msignature}`.") return end # Check that each needed parameter is conform @@ -1825,7 +1914,7 @@ redef class ASuperExpr for sp in msignature.mparameters do var p = mpropdef.msignature.mparameters[i] if not v.is_subtype(p.mtype, sp.mtype) then - v.error(self, "Type error: expected argument #{i} of type {sp.mtype}, got implicit argument {p.name} of type {p.mtype}. Signature is {msignature}") + v.error(self, "Type Error: expected argument #{i} of type `{sp.mtype}`, got implicit argument `{p.name}` of type `{p.mtype}`. Signature is {msignature}") return end i += 1 @@ -1852,30 +1941,50 @@ redef class ANewExpr if not recvtype isa MClassType then if recvtype isa MNullableType then - v.error(self, "Type error: cannot instantiate the nullable type {recvtype}.") + v.error(self, "Type Error: cannot instantiate the nullable type `{recvtype}`.") + return + else if recvtype isa MFormalType then + v.error(self, "Type Error: cannot instantiate the formal type `{recvtype}`.") return else - v.error(self, "Type error: cannot instantiate the formal type {recvtype}.") + v.error(self, "Type Error: cannot instantiate the type `{recvtype}`.") return end end self.recvtype = recvtype + var kind = recvtype.mclass.kind var name: String var nid = self.n_id + var node: ANode if nid != null then name = nid.text + node = nid else name = "new" + node = self.n_kwnew + end + if name == "intern" then + if kind != concrete_kind then + v.error(self, "Type Error: cannot instantiate {kind} {recvtype}.") + return + end + if n_args.n_exprs.not_empty then + v.error(n_args, "Type Error: the intern constructor expects no arguments.") + return + end + # Our job is done + self.mtype = recvtype + return end - var callsite = v.get_method(self, recvtype, name, false) + + var callsite = v.get_method(node, recvtype, name, false) if callsite == null then return if not callsite.mproperty.is_new then - var kind = recvtype.mclass.kind if kind != concrete_kind then - v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.") + v.error(self, "Type Error: cannot instantiate {kind} `{recvtype}`.") return end self.mtype = recvtype @@ -1887,7 +1996,7 @@ redef class ANewExpr self.callsite = callsite if not callsite.mproperty.is_init_for(recvtype.mclass) then - v.error(self, "Error: {name} is not a constructor.") + v.error(self, "Error: `{name}` is not a constructor.") return end @@ -1899,27 +2008,28 @@ end #### redef class AAttrFormExpr - # The attribute acceded. + # The attribute accessed. var mproperty: nullable MAttribute # The static type of the attribute. var attr_type: nullable MType - # Resolve the attribute acceded. + # Resolve the attribute accessed. private fun resolve_property(v: TypeVisitor) do var recvtype = v.visit_expr(self.n_expr) if recvtype == null then return # Skip error - var name = self.n_id.text + var node = self.n_id + var name = node.text if recvtype isa MNullType then - v.error(self, "Error: Attribute '{name}' access on 'null'.") + v.error(node, "Error: attribute `{name}` access on `null`.") return end var unsafe_type = v.anchor_to(recvtype) - var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name) + var mproperty = v.try_get_mproperty_by_name2(node, unsafe_type, name) if mproperty == null then - v.modelbuilder.error(self, "Error: Attribute {name} doesn't exists in {recvtype}.") + v.modelbuilder.error(node, "Error: attribute `{name}` does not exist in `{recvtype}`.") return end assert mproperty isa MAttribute @@ -1978,7 +2088,7 @@ redef class AIssetAttrExpr var recvtype = self.n_expr.mtype.as(not null) var bound = v.resolve_for(mtype, recvtype, false) if bound isa MNullableType then - v.error(self, "Error: isset on a nullable attribute.") + v.error(n_id, "Type Error: `isset` on a nullable attribute.") end self.mtype = v.type_bool(self) end @@ -1990,7 +2100,7 @@ redef class AVarargExpr # This kind of pseudo-expression can be only processed trough a signature # See `check_signature` # Other cases are a syntax error. - v.error(self, "Syntax error: unexpected `...`") + v.error(self, "Syntax Error: unexpected `...`.") end end