Skip to content

Commit a3c4be9

Browse files
committed
Merge: Handle gracefuly multi-varargs
This solve a very borderline issue when a signature contains more than one vararg parameter. They are still refused in the common case but can occurs when multiple initializers are combined into a single constructor. The implemented semantic is the following: * vararg parameters remains varag * if more than one vararg parameter exists in a signature, then the signature does not accepts additional arguments, it means that each vararg is associated to a single argument (a discarded alternative was to only keep the first/last parameter as the main vararg one) * the associated argument can be either a single value, of a reverse vararg with an ellipsis. ~~~nit class A fun x(i: Int...) is autoinit do end fun y(j: Int...) is autoinit do end end var a a = new A(10, 20) # OK: i=[10], j=[20] a = new A(10, 11, 20) # Refused a = new A([10, 11]..., 20) # OK: i=[10, 11], j=[20] a = new A([10, 11]..., [20, 21, 22]...) # OK: i=[10, 11], j=[20, 21, 22] a = new A([10, 11], [20, 21, 22]) # Refused but a hint is given that `...` may be missing ~~~ Pull-Request: #1825 Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
2 parents b12bfdd + 872b872 commit a3c4be9

File tree

9 files changed

+147
-39
lines changed

9 files changed

+147
-39
lines changed

src/compiler/abstract_compiler.nit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,8 +1224,8 @@ abstract class AbstractCompilerVisitor
12241224
res.add(null_instance)
12251225
continue
12261226
end
1227-
if param.is_vararg and map.vararg_decl > 0 then
1228-
var vararg = exprs.sub(j, map.vararg_decl)
1227+
if param.is_vararg and args[i].vararg_decl > 0 then
1228+
var vararg = exprs.sub(j, args[i].vararg_decl)
12291229
var elttype = param.mtype
12301230
var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
12311231
res.add(arg)

src/compiler/java_compiler.nit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,8 +526,8 @@ class JavaCompilerVisitor
526526
res.add(null_instance)
527527
continue
528528
end
529-
if param.is_vararg and map.vararg_decl > 0 then
530-
var vararg = exprs.sub(j, map.vararg_decl)
529+
if param.is_vararg and args[i].vararg_decl > 0 then
530+
var vararg = exprs.sub(j, args[i].vararg_decl)
531531
var elttype = param.mtype
532532
var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
533533
res.add(arg)

src/interpreter/naive_interpreter.nit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,8 @@ class NaiveInterpreter
471471
res.add(null_instance)
472472
continue
473473
end
474-
if param.is_vararg and map.vararg_decl > 0 then
475-
var vararg = exprs.sub(j, map.vararg_decl)
474+
if param.is_vararg and args[i].vararg_decl > 0 then
475+
var vararg = exprs.sub(j, args[i].vararg_decl)
476476
var elttype = param.mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
477477
var arg = self.array_instance(vararg, elttype)
478478
res.add(arg)

src/model/model.nit

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,16 +1818,26 @@ class MSignature
18181818
for i in [0..mparameters.length[ do
18191819
var parameter = mparameters[i]
18201820
if parameter.is_vararg then
1821-
assert vararg_rank == -1
1821+
if vararg_rank >= 0 then
1822+
# If there is more than one vararg,
1823+
# consider that additional arguments cannot be mapped.
1824+
vararg_rank = -1
1825+
break
1826+
end
18221827
vararg_rank = i
18231828
end
18241829
end
18251830
self.vararg_rank = vararg_rank
18261831
end
18271832

1828-
# The rank of the ellipsis (`...`) for vararg (starting from 0).
1833+
# The rank of the main ellipsis (`...`) for vararg (starting from 0).
18291834
# value is -1 if there is no vararg.
18301835
# Example: for "(a: Int, b: Bool..., c: Char)" #-> vararg_rank=1
1836+
#
1837+
# From a model POV, a signature can contain more than one vararg parameter,
1838+
# the `vararg_rank` just indicates the one that will receive the additional arguments.
1839+
# However, currently, if there is more that one vararg parameter, no one will be the main one,
1840+
# and additional arguments will be refused.
18311841
var vararg_rank: Int is noinit
18321842

18331843
# The number of parameters

src/modelize/modelize_property.nit

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,7 @@ redef class ModelBuilder
186186
var sig = mpropdef.msignature
187187
if sig == null then continue # Skip broken method
188188

189-
for param in sig.mparameters do
190-
var ret_type = param.mtype
191-
var mparameter = new MParameter(param.name, ret_type, false)
192-
mparameters.add(mparameter)
193-
end
189+
mparameters.add_all sig.mparameters
194190
initializers.add(mpropdef.mproperty)
195191
mpropdef.mproperty.is_autoinit = true
196192
end

src/semantize/typing.nit

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,12 @@ private class TypeVisitor
490490
continue # skip the vararg
491491
end
492492

493-
var paramtype = param.mtype
494-
self.visit_expr_subtype(arg, paramtype)
493+
if not param.is_vararg then
494+
var paramtype = param.mtype
495+
self.visit_expr_subtype(arg, paramtype)
496+
else
497+
check_one_vararg(arg, param)
498+
end
495499
end
496500

497501
if min_arity > 0 then
@@ -509,27 +513,9 @@ private class TypeVisitor
509513
var paramtype = msignature.mparameters[vararg_rank].mtype
510514
var first = args[vararg_rank]
511515
if vararg_decl == 0 then
512-
var mclass = get_mclass(node, "Array")
513-
if mclass == null then return null # Forward error
514-
var array_mtype = mclass.get_mtype([paramtype])
515-
if first isa AVarargExpr then
516-
self.visit_expr_subtype(first.n_expr, array_mtype)
517-
first.mtype = first.n_expr.mtype
518-
else
519-
# only one vararg, maybe `...` was forgot, so be gentle!
520-
var t = visit_expr(first)
521-
if t == null then return null # Forward error
522-
if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then
523-
# Not acceptable but could be a `...`
524-
error(first, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?")
525-
return null
526-
end
527-
# Standard valid vararg, finish the job
528-
map.vararg_decl = 1
529-
self.visit_expr_subtype(first, paramtype)
530-
end
516+
if not check_one_vararg(first, msignature.mparameters[vararg_rank]) then return null
531517
else
532-
map.vararg_decl = vararg_decl + 1
518+
first.vararg_decl = vararg_decl + 1
533519
for i in [vararg_rank..vararg_rank+vararg_decl] do
534520
self.visit_expr_subtype(args[i], paramtype)
535521
end
@@ -539,6 +525,33 @@ private class TypeVisitor
539525
return map
540526
end
541527

528+
# Check an expression as a single vararg.
529+
# The main point of the method if to handle the case of reversed vararg (see `AVarargExpr`)
530+
fun check_one_vararg(arg: AExpr, param: MParameter): Bool
531+
do
532+
var paramtype = param.mtype
533+
var mclass = get_mclass(arg, "Array")
534+
if mclass == null then return false # Forward error
535+
var array_mtype = mclass.get_mtype([paramtype])
536+
if arg isa AVarargExpr then
537+
self.visit_expr_subtype(arg.n_expr, array_mtype)
538+
arg.mtype = arg.n_expr.mtype
539+
else
540+
# only one vararg, maybe `...` was forgot, so be gentle!
541+
var t = visit_expr(arg)
542+
if t == null then return false # Forward error
543+
if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then
544+
# Not acceptable but could be a `...`
545+
error(arg, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?")
546+
return false
547+
end
548+
# Standard valid vararg, finish the job
549+
arg.vararg_decl = 1
550+
self.visit_expr_subtype(arg, paramtype)
551+
end
552+
return true
553+
end
554+
542555
fun error(node: ANode, message: String)
543556
do
544557
self.modelbuilder.error(node, message)
@@ -614,10 +627,6 @@ end
614627
class SignatureMap
615628
# Associate a parameter to an argument
616629
var map = new ArrayMap[Int, Int]
617-
618-
# The length of the vararg sequence
619-
# 0 if no vararg or if reverse vararg (cf `AVarargExpr`)
620-
var vararg_decl: Int = 0
621630
end
622631

623632
# A specific method call site with its associated informations.
@@ -853,6 +862,15 @@ redef class AExpr
853862
# The result of the evaluation of `self` must be
854863
# stored inside the designated array (there is an implicit `push`)
855864
var comprehension: nullable AArrayExpr = null
865+
866+
# It indicates the number of arguments collected as a vararg.
867+
#
868+
# When 0, the argument is used as is, without transformation.
869+
# When 1, the argument is transformed into an singleton array.
870+
# Above 1, the arguments and the next ones are transformed into a common array.
871+
#
872+
# This attribute is meaning less on expressions not used as attributes.
873+
var vararg_decl: Int = 0
856874
end
857875

858876
redef class ABlockExpr

tests/base_vararg_mult.nit

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# This file is part of NIT ( http://www.nitlanguage.org ).
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import array
16+
17+
class A
18+
fun x(ints: Int...) is autoinit do
19+
for i in ints do
20+
'X'.output
21+
i.output
22+
end
23+
'\n'.output
24+
end
25+
end
26+
27+
class B
28+
super A
29+
fun y(objs: Object...) is autoinit do
30+
for i in objs do
31+
'Y'.output
32+
i.output
33+
end
34+
'\n'.output
35+
end
36+
end
37+
38+
var x
39+
40+
#alt1#x = new A
41+
x = new A(1)
42+
x = new A(10, 20)
43+
x = new A([100, 200, 300]...)
44+
45+
#aly1#x = new B(1)
46+
x = new B(1, 2)
47+
#alt1#x = new B(1, 2, 3)
48+
#alt1#x = new B([10, 20], 33)
49+
x = new B([10, 11]..., 20)
50+
x = new B(10, [20, 21]...)
51+
x = new B([10, 11]..., [20, 21, 23]...)

tests/sav/base_vararg_mult.res

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
X1
2+
3+
X10
4+
X20
5+
6+
X100
7+
X200
8+
X300
9+
10+
X1
11+
12+
Y2
13+
14+
X10
15+
X11
16+
17+
Y20
18+
19+
X10
20+
21+
Y20
22+
Y21
23+
24+
X10
25+
X11
26+
27+
Y20
28+
Y21
29+
Y23
30+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
alt/base_vararg_mult_alt1.nit:40,5--7: Error: expected at least 1 argument(s) for `init(ints: Int...)`; got 0. See introduction at `core::Object::init`.
2+
alt/base_vararg_mult_alt1.nit:47,5--7: Error: expected 2 argument(s) for `init(ints: Int..., objs: Object...)`; got 3. See introduction at `core::Object::init`.
3+
alt/base_vararg_mult_alt1.nit:48,11--18: Type Error: expected `Int`, got `Array[Int]`. Is an ellipsis `...` missing on the argument?

0 commit comments

Comments
 (0)