Skip to content

Commit 851662d

Browse files
committed
Avoid commandeering typevar from the other side if it's not valid.
Fix #39088.
1 parent 2a24a6e commit 851662d

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

src/subtype.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,13 @@ typedef struct jl_varbinding_t {
7474
// 1 - var.ub = ub; return var
7575
// 2 - either (var.ub = ub; return var), or return ub
7676
int8_t constraintkind;
77-
int8_t intvalued; // must be integer-valued; i.e. occurs as N in Vararg{_,N}
77+
// intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N}
78+
// 0: No restriction
79+
// 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.
83+
int8_t intvalued;
7884
int8_t limited;
7985
int16_t depth0; // # of invariant constructors nested around the UnionAll type for this var
8086
// when this variable's integer value is compared to that of another,
@@ -2196,12 +2202,23 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_
21962202
if (jl_is_long(bb->lb)) {
21972203
size_t blb = jl_unbox_long(bb->lb);
21982204
if (blb < bb->offset || blb < 0)
2205+
if (jl_unbox_long(bb->lb) < bb->offset || jl_unbox_long(bb->lb) < 0)
21992206
return jl_bottom_type;
22002207
// Here we always return the shorter `Vararg`'s length.
22012208
if (bb->offset <= 0)
22022209
return bb->lb;
22032210
return jl_box_long(jl_unbox_long(bb->lb) - bb->offset);
22042211
}
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.
2215+
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.
2221+
return NULL;
22052222
return (jl_value_t*)tv;
22062223
}
22072224

@@ -2453,6 +2470,13 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
24532470
}
24542471
}
24552472

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)
2475+
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;
2479+
24562480
// TODO: this can prevent us from matching typevar identities later
24572481
if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub))
24582482
newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub);
@@ -2645,7 +2669,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_
26452669
e->vars->limited = 1;
26462670
}
26472671
else if (res != jl_bottom_type) {
2648-
if (vb.concrete || vb.occurs_inv>1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) {
2672+
if (vb.concrete || vb.occurs_inv>1 || vb.intvalued > 1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) {
26492673
restore_env(e, NULL, &se);
26502674
vb.occurs_cov = vb.occurs_inv = 0;
26512675
vb.constraintkind = vb.concrete ? 1 : 2;
@@ -2702,7 +2726,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, size_t
27022726
if (xp2 && jl_is_typevar(xp2)) {
27032727
xb = lookup(e, (jl_tvar_t*)xp2);
27042728
if (xb) {
2705-
xb->intvalued = 1;
2729+
if (xb->intvalued == 0) xb->intvalued = 1;
27062730
xb->offset = offset;
27072731
}
27082732
if (!yp2)
@@ -2711,7 +2735,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, size_t
27112735
if (yp2 && jl_is_typevar(yp2)) {
27122736
yb = lookup(e, (jl_tvar_t*)yp2);
27132737
if (yb) {
2714-
yb->intvalued = 1;
2738+
if (yb->intvalued == 0) yb->intvalued = 1;
27152739
yb->offset = -offset;
27162740
}
27172741
if (!xp2)

test/subtype.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,3 +2080,34 @@ let A = Tuple{NTuple{N,Any},Val{N}} where {N},
20802080
@testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any}},Val{4}}, Union{})
20812081
@testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any,N}} where {N},Val{4}}, Union{})
20822082
end
2083+
2084+
#issue #39088
2085+
let
2086+
a() = c((1,), (1,1,1,1))
2087+
c(d::NTuple{T}, ::NTuple{T}) where T = d
2088+
c(d::NTuple{f}, b) where f = c((d..., f), b)
2089+
j(h::NTuple{T}, ::NTuple{T} = a()) where T = nothing
2090+
@test j((1,1,1,1)) === nothing
2091+
end
2092+
2093+
let A = Tuple{NTuple{N, Int}, NTuple{N, Int}} where N,
2094+
C = Tuple{NTuple{4, Int}, NTuple{4, Int}}
2095+
@testintersect(A, Tuple{Tuple{Int, Vararg{Any}}, NTuple{4, Int}}, C)
2096+
@testintersect(A, Tuple{Tuple{Int, Vararg{Any, N}} where {N}, NTuple{4, Int}}, C)
2097+
@testintersect(A, Tuple{Tuple{Int, Vararg{Any, N}}, NTuple{4, Int}} where {N}, C)
2098+
2099+
Bs = (Tuple{Tuple{Int, Vararg{Any}}, Tuple{Int, Int, Vararg{Any}}},
2100+
Tuple{Tuple{Int, Vararg{Any,N1}}, Tuple{Int, Int, Vararg{Any,N2}}} where {N1,N2},
2101+
Tuple{Tuple{Int, Vararg{Any,N}} where {N}, Tuple{Int, Int, Vararg{Any,N}} where {N}})
2102+
Cerr = Tuple{Tuple{Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N}
2103+
for B in Bs
2104+
C = typeintersect(A, B)
2105+
@test C == typeintersect(B, A) != Union{}
2106+
@test C != Cerr
2107+
# TODO: The idea result is Tuple{Tuple{Int, Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N}
2108+
@test_broken C != Tuple{Tuple{Int, Vararg{Int}}, Tuple{Int, Int, Vararg{Int}}}
2109+
end
2110+
end
2111+
2112+
# Example from pr#39098
2113+
@testintersect(NTuple, Tuple{Any,Vararg}, Tuple{T, Vararg{T}} where {T})

0 commit comments

Comments
 (0)