From: Jean Privat Date: Mon, 10 Aug 2015 16:12:51 +0000 (-0400) Subject: Merge: Stricter default arguments X-Git-Tag: v0.7.8~100 X-Git-Url: http://nitlanguage.org?hp=-c Merge: Stricter default arguments This change the way that default arguments are handled. Now, defaults arguments can only follow the used one. This improve the readability of calls and the association between calls and declarations thus solve a lot of issues of users. ~~~ fun foo(a: nullable Int, b: Int, c: nullable Int) do ... foo(null,2,null) # OK foo(null,2) # equivalent foo(2) # now refused! but previously accepted as equivalent. ~~~ Only exception: the last parameter of an assignment method is always the last argument ~~~ fun foo=(a: nullable Int, b: Int, c: nullable Int, d: nullable Int) do ... foo(null,2,null) = 0 # OK foo(null,2) = 0 # equivalent foo(2) = 0 # now refused! but previously accepted as equivalent ~~~ No specific black magic is added to automatic constructor. Therefore an optional constructor parameter in a class can becore mandatory in a subclass if a new mandatory attribute is introduced. ~~~ class A var a: nullable Int end class B super A var b: Int end var a1 = new A(null) # OK var a2 = new A # equivalent var b1 = new B(null, 2) # OK var b2 = new B(2) # now refused! but previously accepted as equivalent ~~~ This issue only required some small adaptation in existing piece of code: nitdoc, sexp, serialization and dom. The latter is PRized independently in #1616 but included here for jenkins. Most of these changes (first commits) are in fact bugfixes or make the code cleaner so this is an argument in favor of this new stricter specification. This PR has also the advantage of simplifying the model as the whole policy of the default argument is moved to the typing analysis: it is now just a pure calling convention. Close #1453 Pull-Request: #1618 Reviewed-by: Lucas Bajolet Reviewed-by: Alexandre Terrasa --- 413b0f0a9eb8210ec7046afdec11abd20ee13680 diff --combined src/semantize/typing.nit index 41d2058,9b0d841..b5f5d0c --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@@ -414,26 -414,30 +414,30 @@@ private class TypeVisito 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 @@@ -445,10 -449,6 +449,6 @@@ 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 @@@ -456,10 -456,12 +456,12 @@@ return null end map.map[idx] = i 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 @@@ -468,16 -470,16 +470,16 @@@ 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 @@@ -491,6 -493,16 +493,16 @@@ self.visit_expr_subtype(arg, paramtype) 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 if vararg_rank >= 0 then var paramtype = msignature.mparameters[vararg_rank].mtype @@@ -1766,14 -1778,14 +1778,14 @@@ en 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 @@@ -1841,8 -1853,8 +1853,8 @@@ redef class ASendReassignFormExp 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 @@@ -2014,11 -2026,11 +2026,11 @@@ redef class ANewExp 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