Skip to content

Commit 4e66163

Browse files
committed
cleanup Vararg instantiation code
Various simplifications and improvements from investigating #51228. Improves the logic for showing of NTuple to handle constant lengths. Improves the logic for showing NTuple of bound length (e.g. NTuple itself). Also makes a choice to avoid showing non-types as NTuple, but instead try to write them out, to make it more visually obvious when the parameters have been swapped.
1 parent 572fa50 commit 4e66163

File tree

9 files changed

+115
-56
lines changed

9 files changed

+115
-56
lines changed

base/show.jl

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,29 +1084,68 @@ function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[])
10841084

10851085
# Print tuple types with homogeneous tails longer than max_n compactly using `NTuple` or `Vararg`
10861086
if istuple
1087+
if n == 0
1088+
print(io, "Tuple{}")
1089+
return
1090+
end
1091+
1092+
# find the length of the homogeneous tail
10871093
max_n = 3
10881094
taillen = 1
1089-
for i in (n-1):-1:1
1090-
if parameters[i] === parameters[n]
1091-
taillen += 1
1095+
pn = parameters[n]
1096+
fulln = n
1097+
vakind = :none
1098+
vaN = 0
1099+
if pn isa Core.TypeofVararg
1100+
if isdefined(pn, :N)
1101+
vaN = pn.N
1102+
if vaN isa Int
1103+
taillen = vaN
1104+
fulln += taillen - 1
1105+
vakind = :fixed
1106+
else
1107+
vakind = :bound
1108+
end
10921109
else
1093-
break
1110+
vakind = :unbound
1111+
end
1112+
pn = unwrapva(pn)
1113+
end
1114+
if !(pn isa TypeVar || pn isa Type)
1115+
# prefer Tuple over NTuple if it contains something other than types
1116+
# (e.g. if the user has switched the N and T accidentally)
1117+
taillen = 0
1118+
elseif vakind === :none || vakind === :fixed
1119+
for i in (n-1):-1:1
1120+
if parameters[i] === pn
1121+
taillen += 1
1122+
else
1123+
break
1124+
end
10941125
end
10951126
end
1096-
if n == taillen > max_n
1097-
print(io, "NTuple{", n, ", ")
1098-
show(io, parameters[1])
1127+
1128+
# prefer NTuple over Tuple if it is a Vararg without a fixed length
1129+
# and prefer Tuple for short lists of elements
1130+
if (vakind == :bound && n == 1 == taillen) || (vakind === :fixed && taillen == fulln > max_n) ||
1131+
(vakind === :none && taillen == fulln > max_n)
1132+
print(io, "NTuple{")
1133+
vakind === :bound ? show(io, vaN) : print(io, fulln)
1134+
print(io, ", ")
1135+
show(io, pn)
10991136
print(io, "}")
11001137
else
11011138
print(io, "Tuple{")
1102-
for i = 1:(taillen > max_n ? n-taillen : n)
1139+
headlen = (taillen > max_n ? fulln - taillen : fulln)
1140+
for i = 1:headlen
11031141
i > 1 && print(io, ", ")
1104-
show(io, parameters[i])
1142+
show(io, vakind === :fixed && i >= n ? pn : parameters[i])
11051143
end
1106-
if taillen > max_n
1107-
print(io, ", Vararg{")
1108-
show(io, parameters[n])
1109-
print(io, ", ", taillen, "}")
1144+
if headlen < fulln
1145+
headlen > 0 && print(io, ", ")
1146+
print(io, "Vararg{")
1147+
show(io, pn)
1148+
print(io, ", ", fulln - headlen, "}")
11101149
end
11111150
print(io, "}")
11121151
end

src/codegen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6738,7 +6738,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con
67386738
sigt = NULL;
67396739
}
67406740
else {
6741-
sigt = jl_apply_tuple_type((jl_svec_t*)sigt);
6741+
sigt = jl_apply_tuple_type((jl_svec_t*)sigt, 1);
67426742
}
67436743
if (sigt && !(unionall_env && jl_has_typevar_from_unionall(rt, unionall_env))) {
67446744
unionall_env = NULL;
@@ -7242,7 +7242,7 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq)
72427242
}
72437243
jl_svecset(tupargs, i-nreq, argType);
72447244
}
7245-
jl_value_t *typ = jl_apply_tuple_type(tupargs);
7245+
jl_value_t *typ = jl_apply_tuple_type(tupargs, 1);
72467246
JL_GC_POP();
72477247
return (jl_datatype_t*)typ;
72487248
}

src/gf.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,7 +1266,7 @@ static jl_method_instance_t *cache_method(
12661266
intptr_t max_varargs = get_max_varargs(definition, kwmt, mt, NULL);
12671267
jl_compilation_sig(tt, sparams, definition, max_varargs, &newparams);
12681268
if (newparams) {
1269-
temp2 = jl_apply_tuple_type(newparams);
1269+
temp2 = jl_apply_tuple_type(newparams, 1);
12701270
// Now there may be a problem: the widened signature is more general
12711271
// than just the given arguments, so it might conflict with another
12721272
// definition that does not have cache instances yet. To fix this, we
@@ -1389,7 +1389,7 @@ static jl_method_instance_t *cache_method(
13891389
}
13901390
}
13911391
if (newparams) {
1392-
simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams);
1392+
simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams, 1);
13931393
temp2 = (jl_value_t*)simplett;
13941394
}
13951395

@@ -2579,7 +2579,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t
25792579
jl_compilation_sig(ti, env, m, max_varargs, &newparams);
25802580
int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple;
25812581
if (newparams) {
2582-
tt = (jl_datatype_t*)jl_apply_tuple_type(newparams);
2582+
tt = (jl_datatype_t*)jl_apply_tuple_type(newparams, 1);
25832583
if (!is_compileable) {
25842584
// compute new env, if used below
25852585
jl_value_t *ti = jl_type_intersection_env((jl_value_t*)tt, (jl_value_t*)m->sig, &newparams);
@@ -2834,7 +2834,7 @@ jl_value_t *jl_argtype_with_function_type(jl_value_t *ft JL_MAYBE_UNROOTED, jl_v
28342834
jl_svecset(tt, 0, ft);
28352835
for (size_t i = 0; i < l; i++)
28362836
jl_svecset(tt, i+1, jl_tparam(types,i));
2837-
tt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)tt);
2837+
tt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)tt, 1);
28382838
tt = jl_rewrap_unionall_(tt, types0);
28392839
JL_GC_POP();
28402840
return tt;

src/jltypes.c

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ JL_DLLEXPORT int jl_get_size(jl_value_t *val, size_t *pnt)
333333
if (jl_is_long(val)) {
334334
ssize_t slen = jl_unbox_long(val);
335335
if (slen < 0)
336-
jl_errorf("size or dimension is negative: %d", slen);
336+
jl_errorf("size or dimension is negative: %zd", slen);
337337
*pnt = slen;
338338
return 1;
339339
}
@@ -1435,17 +1435,6 @@ jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *ty)
14351435
return rettyp;
14361436
}
14371437

1438-
// used to expand an NTuple to a flat representation
1439-
static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v)
1440-
{
1441-
jl_value_t *p = NULL;
1442-
JL_GC_PUSH1(&p);
1443-
p = (jl_value_t*)jl_svec_fill(n, v);
1444-
p = jl_apply_tuple_type((jl_svec_t*)p);
1445-
JL_GC_POP();
1446-
return p;
1447-
}
1448-
14491438
JL_EXTENSION struct _jl_typestack_t {
14501439
jl_datatype_t *tt;
14511440
struct _jl_typestack_t *prev;
@@ -1796,13 +1785,13 @@ int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_c
17961785
// * `var` does not appear in invariant position
17971786
// * `var` appears at most once (in covariant position) and not in a `Vararg`
17981787
// unless the upper bound is concrete (diagonal rule)
1799-
int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT
1788+
static int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT
18001789
{
18011790
int cov_count = 0;
18021791
return _may_substitute_ub(v, var, 0, &cov_count);
18031792
}
18041793

1805-
jl_value_t *normalize_unionalls(jl_value_t *t)
1794+
static jl_value_t *normalize_unionalls(jl_value_t *t)
18061795
{
18071796
if (jl_is_uniontype(t)) {
18081797
jl_uniontype_t *u = (jl_uniontype_t*)t;
@@ -1840,6 +1829,29 @@ jl_value_t *normalize_unionalls(jl_value_t *t)
18401829
return t;
18411830
}
18421831

1832+
// used to expand an NTuple to a flat representation
1833+
static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *t, int check)
1834+
{
1835+
if (check) {
1836+
// Since we are skipping making the Vararg and skipping checks later,
1837+
// we inline the checks from jl_wrap_vararg here now
1838+
if (!jl_valid_type_param(t))
1839+
jl_type_error_rt("Vararg", "type", (jl_value_t*)jl_type_type, t);
1840+
// jl_wrap_vararg sometimes simplifies the type, so we only do this 1 time, instead of for each n later
1841+
t = normalize_unionalls(t);
1842+
jl_value_t *tw = extract_wrapper(t);
1843+
if (tw && t != tw && jl_types_equal(t, tw))
1844+
t = tw;
1845+
check = 0; // remember that checks are already done now
1846+
}
1847+
jl_value_t *p = NULL;
1848+
JL_GC_PUSH1(&p);
1849+
p = (jl_value_t*)jl_svec_fill(n, t);
1850+
p = jl_apply_tuple_type((jl_svec_t*)p, check);
1851+
JL_GC_POP();
1852+
return p;
1853+
}
1854+
18431855
static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev, jl_typestack_t *stack);
18441856

18451857
static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp,
@@ -1962,7 +1974,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
19621974
if (nt == 0 || !jl_has_free_typevars(va0)) {
19631975
if (ntp == 1) {
19641976
JL_GC_POP();
1965-
return jl_tupletype_fill(nt, va0);
1977+
return jl_tupletype_fill(nt, va0, 0);
19661978
}
19671979
size_t i, l;
19681980
p = jl_alloc_svec(ntp - 1 + nt);
@@ -1971,7 +1983,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
19711983
l = ntp - 1 + nt;
19721984
for (; i < l; i++)
19731985
jl_svecset(p, i, va0);
1974-
jl_value_t *ndt = jl_apply_tuple_type(p);
1986+
jl_value_t *ndt = jl_apply_tuple_type(p, check);
19751987
JL_GC_POP();
19761988
return ndt;
19771989
}
@@ -2136,19 +2148,19 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
21362148
return (jl_value_t*)ndt;
21372149
}
21382150

2139-
static jl_value_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params)
2151+
static jl_value_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params, int check)
21402152
{
2141-
return inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, 1);
2153+
return inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL, check);
21422154
}
21432155

2144-
JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params)
2156+
JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params, int check)
21452157
{
2146-
return jl_apply_tuple_type_v_(jl_svec_data(params), jl_svec_len(params), params);
2158+
return jl_apply_tuple_type_v_(jl_svec_data(params), jl_svec_len(params), params, check);
21472159
}
21482160

21492161
JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np)
21502162
{
2151-
return jl_apply_tuple_type_v_(p, np, NULL);
2163+
return jl_apply_tuple_type_v_(p, np, NULL, 1);
21522164
}
21532165

21542166
jl_tupletype_t *jl_lookup_arg_tuple_type(jl_value_t *arg1, jl_value_t **args, size_t nargs, int leaf)
@@ -2211,13 +2223,15 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_
22112223
jl_datatype_t *tt = (jl_datatype_t*)t;
22122224
jl_svec_t *tp = tt->parameters;
22132225
size_t ntp = jl_svec_len(tp);
2214-
// Instantiate NTuple{3,Int}
2226+
// Instantiate Tuple{Vararg{T,N}} where T is fixed and N is known, such as Dims{3}
2227+
// And avoiding allocating the intermediate steps
22152228
// Note this does not instantiate Tuple{Vararg{Int,3}}; that's done in inst_datatype_inner
2229+
// Note this does not instantiate NTuple{N,T}, since it is unnecessary and inefficient to expand that now
22162230
if (jl_is_va_tuple(tt) && ntp == 1) {
2217-
// If this is a Tuple{Vararg{T,N}} with known N, expand it to
2231+
// If this is a Tuple{Vararg{T,N}} with known N and T, expand it to
22182232
// a fixed-length tuple
22192233
jl_value_t *T=NULL, *N=NULL;
2220-
jl_value_t *va = jl_unwrap_unionall(jl_tparam0(tt));
2234+
jl_value_t *va = jl_tparam0(tt);
22212235
jl_value_t *ttT = jl_unwrap_vararg(va);
22222236
jl_value_t *ttN = jl_unwrap_vararg_num(va);
22232237
jl_typeenv_t *e = env;
@@ -2228,11 +2242,12 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_
22282242
N = e->val;
22292243
e = e->prev;
22302244
}
2231-
if (T != NULL && N != NULL && jl_is_long(N)) {
2245+
if (T != NULL && N != NULL && jl_is_long(N)) { // TODO: && !jl_has_free_typevars(T) to match inst_datatype_inner, or even && jl_is_concrete_type(T)
2246+
// Since this is skipping jl_wrap_vararg, we inline the checks from it here
22322247
ssize_t nt = jl_unbox_long(N);
22332248
if (nt < 0)
2234-
jl_errorf("size or dimension is negative: %zd", nt);
2235-
return jl_tupletype_fill(nt, T);
2249+
jl_errorf("Vararg length is negative: %zd", nt);
2250+
return jl_tupletype_fill(nt, T, check);
22362251
}
22372252
}
22382253
jl_value_t **iparams;
@@ -2428,9 +2443,8 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n, int check)
24282443
}
24292444
}
24302445
if (t) {
2431-
if (!jl_valid_type_param(t)) {
2446+
if (!jl_valid_type_param(t))
24322447
jl_type_error_rt("Vararg", "type", (jl_value_t*)jl_type_type, t);
2433-
}
24342448
t = normalize_unionalls(t);
24352449
jl_value_t *tw = extract_wrapper(t);
24362450
if (tw && t != tw && jl_types_equal(t, tw))
@@ -2729,7 +2743,7 @@ void jl_init_types(void) JL_GC_DISABLED
27292743
jl_anytuple_type->layout = NULL;
27302744

27312745
jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type);
2732-
jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec);
2746+
jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec, 0);
27332747
jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type);
27342748
jl_emptytuple_type->instance = jl_emptytuple;
27352749

src/julia.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1563,7 +1563,7 @@ JL_DLLEXPORT jl_value_t *jl_apply_type1(jl_value_t *tc, jl_value_t *p1);
15631563
JL_DLLEXPORT jl_value_t *jl_apply_type2(jl_value_t *tc, jl_value_t *p1, jl_value_t *p2);
15641564
JL_DLLEXPORT jl_datatype_t *jl_apply_modify_type(jl_value_t *dt);
15651565
JL_DLLEXPORT jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *dt);
1566-
JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params);
1566+
JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params, int check); // if uncertain, set check=1
15671567
JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np);
15681568
JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name,
15691569
jl_module_t *module,

src/method.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata,
998998
JL_GC_PUSH3(&f, &m, &argtype);
999999
size_t i, na = jl_svec_len(atypes);
10001000

1001-
argtype = jl_apply_tuple_type(atypes);
1001+
argtype = jl_apply_tuple_type(atypes, 1);
10021002
if (!jl_is_datatype(argtype))
10031003
jl_error("invalid type in method definition (Union{})");
10041004

src/precompile_utils.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ static void _compile_all_union(jl_value_t *sig)
120120
jl_svecset(p, i, ty);
121121
}
122122
}
123-
methsig = jl_apply_tuple_type(p);
123+
methsig = jl_apply_tuple_type(p, 1);
124124
methsig = jl_rewrap_unionall(methsig, sig);
125125
_compile_all_tvar_union(methsig);
126126
}

src/subtype.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,7 +3393,7 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten
33933393
else if (isy)
33943394
res = (jl_value_t*)yd;
33953395
else if (p)
3396-
res = jl_apply_tuple_type(p);
3396+
res = jl_apply_tuple_type(p, 1);
33973397
else
33983398
res = jl_apply_tuple_type_v(params, np);
33993399
}
@@ -4130,7 +4130,7 @@ static jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b)
41304130
ts[1] = jl_tparam(b, i);
41314131
jl_svecset(vec, i, jl_type_union(ts, 2));
41324132
}
4133-
jl_value_t *ans = jl_apply_tuple_type(vec);
4133+
jl_value_t *ans = jl_apply_tuple_type(vec, 1);
41344134
JL_GC_POP();
41354135
return ans;
41364136
}

test/show.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,9 @@ test_repr("(:).a")
13611361
@test repr(Tuple{Float32, Float32, Float32}) == "Tuple{Float32, Float32, Float32}"
13621362
@test repr(Tuple{String, Int64, Int64, Int64}) == "Tuple{String, Int64, Int64, Int64}"
13631363
@test repr(Tuple{String, Int64, Int64, Int64, Int64}) == "Tuple{String, Vararg{Int64, 4}}"
1364+
@test repr(NTuple) == "NTuple{N, T} where {N, T}"
1365+
@test repr(Tuple{NTuple{N}, Vararg{NTuple{N}, 4}} where N) == "NTuple{5, NTuple{N, T} where T} where N"
1366+
@test repr(Tuple{Float64, NTuple{N}, Vararg{NTuple{N}, 4}} where N) == "Tuple{Float64, Vararg{NTuple{N, T} where T, 5}} where N"
13641367

13651368
# Test printing of NamedTuples using the macro syntax
13661369
@test repr(@NamedTuple{kw::Int64}) == "@NamedTuple{kw::Int64}"
@@ -1373,17 +1376,20 @@ test_repr("(:).a")
13731376
@test repr(@Kwargs{init::Int}) == "Base.Pairs{Symbol, $Int, Tuple{Symbol}, @NamedTuple{init::$Int}}"
13741377

13751378
@testset "issue #42931" begin
1376-
@test repr(NTuple{4, :A}) == "NTuple{4, :A}"
1379+
@test repr(NTuple{4, :A}) == "Tuple{:A, :A, :A, :A}"
13771380
@test repr(NTuple{3, :A}) == "Tuple{:A, :A, :A}"
13781381
@test repr(NTuple{2, :A}) == "Tuple{:A, :A}"
13791382
@test repr(NTuple{1, :A}) == "Tuple{:A}"
13801383
@test repr(NTuple{0, :A}) == "Tuple{}"
13811384

13821385
@test repr(Tuple{:A, :A, :A, :B}) == "Tuple{:A, :A, :A, :B}"
1383-
@test repr(Tuple{:A, :A, :A, :A}) == "NTuple{4, :A}"
1386+
@test repr(Tuple{:A, :A, :A, :A}) == "Tuple{:A, :A, :A, :A}"
13841387
@test repr(Tuple{:A, :A, :A}) == "Tuple{:A, :A, :A}"
13851388
@test repr(Tuple{:A}) == "Tuple{:A}"
13861389
@test repr(Tuple{}) == "Tuple{}"
1390+
1391+
@test repr(Tuple{Vararg{N, 10}} where N) == "NTuple{10, N} where N"
1392+
@test repr(Tuple{Vararg{10, N}} where N) == "Tuple{Vararg{10, N}} where N"
13871393
end
13881394

13891395
# Test that REPL/mime display of invalid UTF-8 data doesn't throw an exception:

0 commit comments

Comments
 (0)