Skip to content

Commit 280310d

Browse files
committed
Merge pull request #11242 from JuliaLang/teh/ntuple
NTuples made me sad (so I nixed them)
2 parents bd582b3 + 92ac59a commit 280310d

18 files changed

+679
-516
lines changed

NEWS.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ New language features
1212
However note that the argument types refer to the syntax tree representation, and not
1313
to the types of run time values.
1414

15+
* Varargs functions like `foo{T}(x::T...)` may now restrict the number
16+
of such arguments using `foo{T,N}(x::Vararg{T,N})` ([#11242]).
17+
1518
* `x ∈ X` is now a synonym for `x in X` in `for` loops and comprehensions,
1619
as it already was in comparisons ([#13824]).
1720

@@ -42,6 +45,9 @@ Language changes
4245

4346
* The `if` keyword cannot be followed immediately by a line break ([#15763]).
4447

48+
* The built-in `NTuple` type has been removed; `NTuple{N,T}` is now
49+
implemented internally as `Tuple{Vararg{T,N}}` ([#11242]).
50+
4551
Command-line option changes
4652
---------------------------
4753

@@ -175,11 +181,13 @@ Deprecated or removed
175181
<!--- generated by NEWS-update.jl: -->
176182
[#4163]: https://github.com/JuliaLang/julia/issues/4163
177183
[#4211]: https://github.com/JuliaLang/julia/issues/4211
184+
[#6190]: https://github.com/JuliaLang/julia/issues/6190
178185
[#8036]: https://github.com/JuliaLang/julia/issues/8036
179186
[#8846]: https://github.com/JuliaLang/julia/issues/8846
180187
[#9503]: https://github.com/JuliaLang/julia/issues/9503
181188
[#9627]: https://github.com/JuliaLang/julia/issues/9627
182189
[#11196]: https://github.com/JuliaLang/julia/issues/11196
190+
[#11242]: https://github.com/JuliaLang/julia/issues/11242
183191
[#13062]: https://github.com/JuliaLang/julia/issues/13062
184192
[#13232]: https://github.com/JuliaLang/julia/issues/13232
185193
[#13338]: https://github.com/JuliaLang/julia/issues/13338
@@ -211,4 +219,3 @@ Deprecated or removed
211219
[#15550]: https://github.com/JuliaLang/julia/issues/15550
212220
[#15609]: https://github.com/JuliaLang/julia/issues/15609
213221
[#15763]: https://github.com/JuliaLang/julia/issues/15763
214-
[#6190]: https://github.com/JuliaLang/julia/issues/6190

base/boot.jl

+2
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ convert{T}(::Type{T}, x::T) = x
293293
cconvert{T}(::Type{T}, x) = convert(T, x)
294294
unsafe_convert{T}(::Type{T}, x::T) = x
295295

296+
typealias NTuple{N,T} Tuple{Vararg{T,N}}
297+
296298
# primitive array constructors
297299
(::Type{Array{T,N}}){T,N}(d::NTuple{N,Int}) =
298300
ccall(:jl_new_array, Array{T,N}, (Any,Any), Array{T,N}, d)

base/exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export
7676
Irrational,
7777
Matrix,
7878
MergeSort,
79+
NTuple,
7980
Nullable,
8081
ObjectIdDict,
8182
OrdinalRange,

base/inference.jl

+14-12
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ function istopfunction(topmod, f::ANY, sym)
202202
return false
203203
end
204204

205-
isknownlength(t::DataType) = !isvatuple(t) && !(t.name===NTuple.name && !isa(t.parameters[1],Int))
205+
isknownlength(t::DataType) = !isvatuple(t) || (length(t.parameters) == 1 && isa(t.parameters[1].parameters[2],Int))
206206

207207
# t[n:end]
208208
tupletype_tail(t::ANY, n) = Tuple{t.parameters[n:end]...}
@@ -303,7 +303,7 @@ function typeof_tfunc(t::ANY)
303303
Type{typeof(t)}
304304
end
305305
elseif isa(t,DataType)
306-
if isleaftype(t)
306+
if isleaftype(t) || isvarargtype(t)
307307
Type{t}
308308
elseif t === Any
309309
DataType
@@ -397,7 +397,7 @@ function limit_type_depth(t::ANY, d::Int, cov::Bool, vars)
397397
else
398398
return t
399399
end
400-
if inexact
400+
if inexact && !isvarargtype(R)
401401
R = TypeVar(:_,R)
402402
push!(vars, R)
403403
end
@@ -428,9 +428,6 @@ function getfield_tfunc(s0::ANY, name)
428428
return reduce(tmerge, Bottom, map(t->getfield_tfunc(t, name)[1], s.types)), false
429429
end
430430
if isa(s,DataType)
431-
if is(s.name,NTuple.name)
432-
return (name Symbol ? Bottom : s.parameters[2]), true
433-
end
434431
if s.abstract
435432
return Any, false
436433
end
@@ -501,7 +498,7 @@ function fieldtype_tfunc(s::ANY, name)
501498
if is(t,Bottom)
502499
return t
503500
end
504-
Type{exact || isleaftype(t) || isa(t,TypeVar) ? t : TypeVar(:_, t)}
501+
Type{exact || isleaftype(t) || isa(t,TypeVar) || isvarargtype(t) ? t : TypeVar(:_, t)}
505502
end
506503
add_tfunc(fieldtype, 2, 2, fieldtype_tfunc)
507504

@@ -581,7 +578,7 @@ function apply_type_tfunc(args...)
581578
if type_too_complex(appl,0)
582579
return Type{TypeVar(:_,headtype)}
583580
end
584-
!isa(appl,TypeVar) ? Type{TypeVar(:_,appl)} : Type{appl}
581+
!(isa(appl,TypeVar) || isvarargtype(appl)) ? Type{TypeVar(:_,appl)} : Type{appl}
585582
end
586583
add_tfunc(apply_type, 1, IInf, apply_type_tfunc)
587584

@@ -593,6 +590,9 @@ add_tfunc(apply_type, 1, IInf, apply_type_tfunc)
593590
end
594591

595592
function invoke_tfunc(f::ANY, types::ANY, argtype::ANY, sv::InferenceState)
593+
if !isleaftype(Type{types})
594+
return Any
595+
end
596596
argtype = typeintersect(types,limit_tuple_type(argtype))
597597
if is(argtype,Bottom)
598598
return Bottom
@@ -861,7 +861,9 @@ function precise_container_types(args, types, vtypes::VarTable, sv)
861861
assert(n == length(types))
862862
result = cell(n)
863863
for i = 1:n
864-
ai = args[i]; ti = types[i]; tti = widenconst(ti)
864+
ai = args[i]
865+
ti = types[i]
866+
tti = widenconst(ti)
865867
if isa(ai,Expr) && ai.head === :call && (abstract_evals_to_constant(ai.args[1], svec, vtypes, sv) ||
866868
abstract_evals_to_constant(ai.args[1], tuple, vtypes, sv))
867869
aa = ai.args
@@ -873,8 +875,8 @@ function precise_container_types(args, types, vtypes::VarTable, sv)
873875
return nothing
874876
elseif ti Tuple
875877
if i == n
876-
if tti.name === NTuple.name
877-
result[i] = Any[Vararg{tti.parameters[2]}]
878+
if isvatuple(tti) && length(tti.parameters) == 1
879+
result[i] = Any[Vararg{tti.parameters[1].parameters[1]}]
878880
else
879881
result[i] = tti.parameters
880882
end
@@ -1121,7 +1123,7 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState)
11211123
# abstract types yield Type{<:T} instead of Type{T}.
11221124
# this doesn't really model the situation perfectly, but
11231125
# "isleaftype(inference_stack.types)" should be good enough.
1124-
if isa(t,TypeVar)
1126+
if isa(t,TypeVar) || isvarargtype(t)
11251127
t = Type{t}
11261128
else
11271129
t = Type{TypeVar(:_,t)}

base/methodshow.jl

+28-4
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,41 @@ function argtype_decl(env, n, t) # -> (argname, argtype)
1515
return s, ""
1616
end
1717
if isvarargtype(t)
18-
if t.parameters[1] === Any
19-
return string(s, "..."), ""
20-
else
21-
return s, string_with_env(env, t.parameters[1]) * "..."
18+
tt, tn = t.parameters[1], t.parameters[2]
19+
if isa(tn, TypeVar) && !tn.bound
20+
if tt === Any || (isa(tt, TypeVar) && !tt.bound)
21+
return string(s, "..."), ""
22+
else
23+
return s, string_with_env(env, tt) * "..."
24+
end
2225
end
26+
return s, string_with_env(env, "Vararg{", tt, ",", tn, "}")
2327
elseif t == String
2428
return s, "String"
2529
end
2630
return s, string_with_env(env, t)
2731
end
2832

33+
function argtype_decl_vararg(env, n, t)
34+
if isa(n, Expr)
35+
s = string(n.args[1])
36+
if n.args[2].head == :...
37+
# x... or x::T... declaration
38+
if t.parameters[1] === Any
39+
return string(s, "..."), ""
40+
else
41+
return s, string_with_env(env, t.parameters[1]) * "..."
42+
end
43+
elseif t == String
44+
return s, "String"
45+
end
46+
end
47+
# x::Vararg, x::Vararg{T}, or x::Vararg{T,N} declaration
48+
s, length(n.args[2].args) < 4 ?
49+
string_with_env(env, "Vararg{", t.parameters[1], "}") :
50+
string_with_env(env, "Vararg{", t.parameters[1], ",", t.parameters[2], "}")
51+
end
52+
2953
function arg_decl_parts(m::Method)
3054
tv = m.tvars
3155
if !isa(tv,SimpleVector)

base/promotion.jl

+40-22
Original file line numberDiff line numberDiff line change
@@ -32,38 +32,40 @@ function typejoin(a::ANY, b::ANY)
3232
return Any
3333
end
3434
ap, bp = a.parameters, b.parameters
35-
la = length(ap)::Int; lb = length(bp)::Int
36-
if la==0 || lb==0
35+
lar = length(ap)::Int; lbr = length(bp)::Int
36+
laf, afixed = full_va_len(ap)
37+
lbf, bfixed = full_va_len(bp)
38+
if lar==0 || lbr==0
3739
return Tuple
3840
end
39-
if la < lb
40-
if isvarargtype(ap[la])
41-
c = cell(la)
42-
c[la] = Vararg{typejoin(ap[la].parameters[1], tailjoin(bp,la))}
43-
n = la-1
41+
if laf < lbf
42+
if isvarargtype(ap[lar]) && !afixed
43+
c = cell(laf)
44+
c[laf] = Vararg{typejoin(ap[lar].parameters[1], tailjoin(bp,laf))}
45+
n = laf-1
4446
else
45-
c = cell(la+1)
46-
c[la+1] = Vararg{tailjoin(bp,la+1)}
47-
n = la
47+
c = cell(laf+1)
48+
c[laf+1] = Vararg{tailjoin(bp,laf+1)}
49+
n = laf
4850
end
49-
elseif lb < la
50-
if isvarargtype(bp[lb])
51-
c = cell(lb)
52-
c[lb] = Vararg{typejoin(bp[lb].parameters[1], tailjoin(ap,lb))}
53-
n = lb-1
51+
elseif lbf < laf
52+
if isvarargtype(bp[lbr]) && !bfixed
53+
c = cell(lbf)
54+
c[lbf] = Vararg{typejoin(bp[lbr].parameters[1], tailjoin(ap,lbf))}
55+
n = lbf-1
5456
else
55-
c = cell(lb+1)
56-
c[lb+1] = Vararg{tailjoin(ap,lb+1)}
57-
n = lb
57+
c = cell(lbf+1)
58+
c[lbf+1] = Vararg{tailjoin(ap,lbf+1)}
59+
n = lbf
5860
end
5961
else
60-
c = cell(la)
61-
n = la
62+
c = cell(laf)
63+
n = laf
6264
end
6365
for i = 1:n
64-
ai = ap[i]; bi = bp[i]
66+
ai = ap[min(i,lar)]; bi = bp[min(i,lbr)]
6567
ci = typejoin(unwrapva(ai),unwrapva(bi))
66-
c[i] = isvarargtype(ai) || isvarargtype(bi) ? Vararg{ci} : ci
68+
c[i] = i == length(c) && (isvarargtype(ai) || isvarargtype(bi)) ? Vararg{ci} : ci
6769
end
6870
return Tuple{c...}
6971
elseif b <: Tuple
@@ -92,8 +94,24 @@ function typejoin(a::ANY, b::ANY)
9294
return Any
9395
end
9496

97+
# Returns length, isfixed
98+
function full_va_len(p)
99+
isempty(p) && return 0, true
100+
if isvarargtype(p[end])
101+
N = p[end].parameters[2]
102+
if isa(N, Integer)
103+
return (length(p) + N - 1)::Int, true
104+
end
105+
return length(p)::Int, false
106+
end
107+
return length(p)::Int, true
108+
end
109+
95110
# reduce typejoin over A[i:end]
96111
function tailjoin(A, i)
112+
if i > length(A)
113+
return unwrapva(A[end])
114+
end
97115
t = Bottom
98116
for j = i:length(A)
99117
t = typejoin(t, unwrapva(A[j]))

doc/manual/functions.rst

+2
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ the zero or more values passed to ``bar`` after its first two arguments:
333333
In all these cases, ``x`` is bound to a tuple of the trailing values
334334
passed to ``bar``.
335335

336+
It is possible to constrain the number of values passed as a variable argument; this will be discussed later in :ref:`man-vararg-fixedlen`.
337+
336338
On the flip side, it is often handy to "splice" the values contained in
337339
an iterable collection into a function call as individual arguments. To
338340
do this, one also uses ``...`` but in the function call instead:

doc/manual/methods.rst

+26
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,32 @@ can also constrain type parameters of methods::
550550
The ``same_type_numeric`` function behaves much like the ``same_type``
551551
function defined above, but is only defined for pairs of numbers.
552552

553+
.. _man-vararg-fixedlen:
554+
555+
Parametrically-constrained Varargs methods
556+
------------------------------------------
557+
558+
Function parameters can also be used to constrain the number of arguments that may be supplied to a "varargs" function (:ref:`man-varargs-functions`). The notation ``Vararg{T,N}`` is used to indicate such a constraint. For example:
559+
560+
.. doctest::
561+
562+
julia> bar(a,b,x::Vararg{Any,2}) = (a,b,x)
563+
564+
julia> bar(1,2,3)
565+
ERROR: MethodError: `bar` has no matching method bar(::Int, ::Int, ::Int)
566+
567+
julia> bar(1,2,3,4)
568+
(1,2,(3,4))
569+
570+
julia> bar(1,2,3,4,5)
571+
ERROR: MethodError: `bar` has no method matching bar(::Int, ::Int, ::Int, ::Int, ::Int)
572+
573+
More usefully, it is possible to constrain varargs methods by a parameter. For example::
574+
575+
function getindex{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Number,N})
576+
577+
would be called only when the number of ``indexes`` matches the dimensionality of the array.
578+
553579
.. _man-note-on-optional-and-keyword-arguments:
554580

555581
Note on Optional and keyword Arguments

src/Makefile

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ FLAGS += -Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fvisibility=hidden
2424
override CFLAGS += -Wold-style-definition -Wstrict-prototypes -Wc++-compat
2525
endif
2626

27-
2827
SRCS := \
2928
jltypes gf typemap ast builtins module interpreter \
3029
alloc dlload sys init task array dump toplevel jl_uv jlapi signal-handling \

src/alloc.c

-14
Original file line numberDiff line numberDiff line change
@@ -110,20 +110,6 @@ typedef struct {
110110
// Note that this function updates len
111111
static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len)
112112
{
113-
if (jl_is_ntuple_type(dt)) {
114-
jl_value_t *lenvar = jl_tparam0(dt);
115-
jl_value_t *elty = jl_tparam1(dt);
116-
assert(jl_is_datatype(elty));
117-
size_t alignment = ((jl_datatype_t*)elty)->alignment;
118-
*len = LLT_ALIGN((*len), alignment);
119-
assert(jl_is_long(lenvar));
120-
size_t l = jl_unbox_long(lenvar);
121-
size_t nb = l*LLT_ALIGN(jl_datatype_size(elty), alignment);
122-
jl_value_t *v = (jl_value_t*)newobj(dt, NWORDS(nb));
123-
memcpy(jl_data_ptr(v), data, nb);
124-
return v;
125-
}
126-
127113
assert(jl_is_datatype(dt));
128114
jl_datatype_t *bt = (jl_datatype_t*)dt;
129115
size_t nb = jl_datatype_size(bt);

src/builtins.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1129,7 +1129,6 @@ void jl_init_primitives(void)
11291129
add_builtin("TypeName", (jl_value_t*)jl_typename_type);
11301130
add_builtin("TypeConstructor", (jl_value_t*)jl_typector_type);
11311131
add_builtin("Tuple", (jl_value_t*)jl_anytuple_type);
1132-
add_builtin("NTuple", (jl_value_t*)jl_ntuple_type);
11331132
add_builtin("Vararg", (jl_value_t*)jl_vararg_type);
11341133
add_builtin("Type", (jl_value_t*)jl_type_type);
11351134
add_builtin("DataType", (jl_value_t*)jl_datatype_type);
@@ -1580,7 +1579,7 @@ JL_DLLEXPORT void jl_(void *jl_value)
15801579

15811580
JL_DLLEXPORT void jl_breakpoint(jl_value_t *v)
15821581
{
1583-
// put a breakpoint in you debugger here
1582+
// put a breakpoint in your debugger here
15841583
}
15851584

15861585
#ifdef __cplusplus

src/dump.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -2446,7 +2446,7 @@ void jl_init_serializer(void)
24462446
jl_labelnode_type, jl_linenumbernode_type,
24472447
jl_gotonode_type, jl_quotenode_type, jl_topnode_type,
24482448
jl_type_type, jl_bottom_type, jl_ref_type, jl_pointer_type,
2449-
jl_vararg_type, jl_ntuple_type, jl_abstractarray_type,
2449+
jl_vararg_type, jl_abstractarray_type,
24502450
jl_densearray_type, jl_void_type, jl_function_type,
24512451
jl_typector_type, jl_typename_type, jl_builtin_type,
24522452
jl_task_type, jl_uniontype_type, jl_typetype_type, jl_typetype_tvar,
@@ -2460,7 +2460,7 @@ void jl_init_serializer(void)
24602460
jl_datatype_type->name, jl_uniontype_type->name, jl_array_type->name,
24612461
jl_expr_type->name, jl_typename_type->name, jl_type_type->name,
24622462
jl_methtable_type->name, jl_typemap_level_type->name, jl_typemap_entry_type->name, jl_tvar_type->name,
2463-
jl_ntuple_type->name, jl_abstractarray_type->name, jl_vararg_type->name,
2463+
jl_abstractarray_type->name, jl_vararg_type->name,
24642464
jl_densearray_type->name, jl_void_type->name, jl_lambda_info_type->name, jl_method_type->name,
24652465
jl_module_type->name, jl_function_type->name, jl_typedslot_type->name,
24662466
jl_abstractslot_type->name, jl_slotnumber_type->name,

0 commit comments

Comments
 (0)