Skip to content

Commit 126677b

Browse files
committed
Avoid invalid typevar setting in bound_var_below.
The added check failed to cover all cases. It should be much safer to always return a `Vararg` with unknown length. We only need to make sure that `typeintersect(Val{<:Tuple{Any,Vararg{Any}}}, Val{Tuple{Vararg{Any,N}} where {N}})` doesn't gives `Val{Tuple{Any,Vararg{Any}}`.
1 parent 851662d commit 126677b

File tree

2 files changed

+57
-20
lines changed

2 files changed

+57
-20
lines changed

src/subtype.c

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,7 @@ typedef struct jl_varbinding_t {
7777
// intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N}
7878
// 0: No restriction
7979
// 1: must be unbounded/ or fixed to a `Int`/typevar
80-
// 2: This var has been commandeered once.
81-
// 3: This var has been commandeered more than once.
82-
// 4: This var has been commandeered more than once, and it has no fixed ub/lb.
80+
// 2: we have some imprecise vararg length intersection that can be improved if this var is const valued.
8381
int8_t intvalued;
8482
int8_t limited;
8583
int16_t depth0; // # of invariant constructors nested around the UnionAll type for this var
@@ -2209,16 +2207,10 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_
22092207
return bb->lb;
22102208
return jl_box_long(jl_unbox_long(bb->lb) - bb->offset);
22112209
}
2212-
if (bb->intvalued == 1 && bb->offset > 0)
2213-
// We really should return `tv` here, but some subtype tests need it.
2214-
// As a temparay fix, increase `intvalued` to 2.
2210+
if (bb->offset > 0) {
22152211
bb->intvalued = 2;
2216-
else if (bb->intvalued == 2)
2217-
// If bb has been commandeered, we should always increase `intvalued`.
2218-
bb->intvalued = 3;
2219-
else if (bb->intvalued == 4 && bb->offset > 0)
2220-
// bb has no fixed bounds, return `NULL` if `tv` is not correct.
22212212
return NULL;
2213+
}
22222214
return (jl_value_t*)tv;
22232215
}
22242216

@@ -2307,6 +2299,26 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS
23072299
return 0;
23082300
}
23092301

2302+
static int has_null_vararg_length(jl_value_t *a) {
2303+
int is_unionall = 0;
2304+
while (jl_is_unionall(a)) {
2305+
a = ((jl_unionall_t *)a)->body;
2306+
is_unionall = 1;
2307+
}
2308+
if (jl_is_datatype(a) && jl_is_tuple_type((jl_datatype_t *)a)) {
2309+
size_t lx = jl_nparams((jl_datatype_t *)a);
2310+
if (lx > 0) {
2311+
jl_value_t *la = jl_tparam((jl_datatype_t *)a, lx-1);
2312+
if (jl_is_vararg(la)) {
2313+
jl_value_t *len = jl_unwrap_vararg_num((jl_vararg_t *)la);
2314+
if (!len) return 1;
2315+
if (!jl_is_long(len)) return is_unionall;
2316+
}
2317+
}
2318+
}
2319+
return 0;
2320+
}
2321+
23102322
static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param)
23112323
{
23122324
jl_varbinding_t *bb = lookup(e, b);
@@ -2353,6 +2365,10 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int
23532365
ub = (jl_value_t*)b;
23542366
}
23552367
}
2368+
if (has_null_vararg_length(ub) && !has_null_vararg_length(a)) {
2369+
bb->ub = ub;
2370+
ub = (jl_value_t*)b;
2371+
}
23562372
if (ub != (jl_value_t*)b) {
23572373
if (jl_has_free_typevars(ub)) {
23582374
if (check_unsat_bound(ub, b, e)) {
@@ -2470,12 +2486,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
24702486
}
24712487
}
24722488

2473-
// This typevar has been commandeered by the other side once. Which is allowed if it's unbounded.
2474-
if (vb->intvalued == 2 && vb->lb == jl_bottom_type && vb->ub == (jl_value_t*)jl_any_type)
2489+
// vb is still unbounded.
2490+
if (vb->intvalued == 2 && !(varval && jl_is_long(varval)))
24752491
vb->intvalued = 1;
2476-
// This typevar has been commandeered more than once. Set it to 4 if its not fixed.
2477-
if (vb->intvalued == 3 && !(varval && jl_is_long(varval)))
2478-
vb->intvalued = 4;
24792492

24802493
// TODO: this can prevent us from matching typevar identities later
24812494
if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub))
@@ -3080,10 +3093,10 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa
30803093
}
30813094
JL_GC_POP();
30823095
// Here we always return the shorter `Vararg`'s length.
3083-
if ((xx && xx->offset < 0) || (yy && yy->offset > 0))
3096+
if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) {
3097+
if (yy) yy->intvalued = 2;
30843098
return x;
3085-
if ((xx && xx->offset > 0) || (yy && yy->offset < 0))
3086-
return y;
3099+
}
30873100
return y;
30883101
}
30893102
record_var_occurrence(xx, e, param);

test/subtype.jl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,12 @@ function test_intersection()
10431043
Type{Tuple{Int,T}} where T<:Integer)
10441044
@testintersect(Type{<:Tuple{Any,Vararg{Any}}},
10451045
Type{Tuple{Vararg{Int,N}}} where N,
1046-
Type{Tuple{Int,Vararg{Int,N}}} where N)
1046+
!Union{})
1047+
1048+
@test typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) != Type{Tuple{Int,Vararg{Int}}}
1049+
@test_broken typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) == Type{Tuple{Int,Vararg{Int,N}}} where N
1050+
@test_broken typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) != Type{<:Tuple{Int,Vararg{Int}}}
1051+
10471052
@testintersect(Type{<:Array},
10481053
Type{AbstractArray{T}} where T,
10491054
Bottom)
@@ -2109,5 +2114,24 @@ let A = Tuple{NTuple{N, Int}, NTuple{N, Int}} where N,
21092114
end
21102115
end
21112116

2117+
struct __Vals{A,B} end
2118+
let A = __Vals{NTuple{N, Int}, NTuple{N, Int}} where N,
2119+
C = __Vals{NTuple{4, Int}, NTuple{4, Int}}
2120+
@testintersect(A, __Vals{<:Tuple{Int, Vararg{Any}}, NTuple{4, Int}}, C)
2121+
@testintersect(A, __Vals{<:Tuple{Int, Vararg{Any, N}} where {N}, NTuple{4, Int}}, C)
2122+
@testintersect(A, __Vals{<:Tuple{Int, Vararg{Any, N}}, NTuple{4, Int}} where {N}, C)
2123+
2124+
Bs = (__Vals{<:Tuple{Int, Vararg{Int}}, <:Tuple{Int, Int, Vararg{Int}}},
2125+
__Vals{Tuple{Int, Vararg{Int,N1}}, Tuple{Int, Int, Vararg{Int,N2}}} where {N1,N2},
2126+
__Vals{<:Tuple{Int, Vararg{Int,N}} where {N}, <:Tuple{Int, Int, Vararg{Int,N}} where {N}})
2127+
Cerr = __Vals{Tuple{Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N}
2128+
for B in Bs
2129+
C = typeintersect(A, B)
2130+
@test C == typeintersect(B, A) != Union{}
2131+
@test C != Cerr
2132+
@test_broken C != B
2133+
end
2134+
end
2135+
21122136
# Example from pr#39098
21132137
@testintersect(NTuple, Tuple{Any,Vararg}, Tuple{T, Vararg{T}} where {T})

0 commit comments

Comments
 (0)