Skip to content

Commit 9e1e4ed

Browse files
committed
Avoid commandeering typevar from the other side if it's not valid.
Fix #39088.
1 parent 1ffe528 commit 9e1e4ed

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

src/subtype.c

Lines changed: 29 additions & 6 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,
@@ -2194,13 +2200,23 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_
21942200
return jl_bottom_type;
21952201
record_var_occurrence(bb, e, 2);
21962202
if (jl_is_long(bb->lb)) {
2203+
if (jl_unbox_long(bb->lb) < bb->offset || jl_unbox_long(bb->lb) < 0)
2204+
return jl_bottom_type;
21972205
// Here we always return the shorter `Vararg`'s length.
21982206
if (bb->offset <= 0)
21992207
return bb->lb;
2200-
if (jl_unbox_long(bb->lb) < bb->offset)
2201-
return jl_bottom_type;
22022208
return jl_box_long(jl_unbox_long(bb->lb) - bb->offset);
22032209
}
2210+
if (bb->intvalued == 1 && bb->offset > 0)
2211+
// We really should return `tv` here, but some subtype tests need it.
2212+
// As a temparay fix, increase `intvalued` to 2.
2213+
bb->intvalued = 2;
2214+
else if (bb->intvalued == 2)
2215+
// If bb has been commandeered, we should always increase `intvalued`.
2216+
bb->intvalued = 3;
2217+
else if (bb->intvalued == 4 && bb->offset > 0)
2218+
// bb has no fixed bounds, return `NULL` if `tv` is not correct.
2219+
return NULL;
22042220
return (jl_value_t*)tv;
22052221
}
22062222

@@ -2452,6 +2468,13 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
24522468
}
24532469
}
24542470

2471+
// This typevar has been commandeered by the other side once. Which is allowed if it's unbounded.
2472+
if (vb->intvalued == 2 && vb->lb == jl_bottom_type && vb->ub == (jl_value_t*)jl_any_type)
2473+
vb->intvalued = 1;
2474+
// This typevar has been commandeered more than once. Set it to 4 if its not fixed.
2475+
if (vb->intvalued == 3 && !(varval && jl_is_long(varval)))
2476+
vb->intvalued = 4;
2477+
24552478
// TODO: this can prevent us from matching typevar identities later
24562479
if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub))
24572480
newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub);
@@ -2644,7 +2667,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_
26442667
e->vars->limited = 1;
26452668
}
26462669
else if (res != jl_bottom_type) {
2647-
if (vb.concrete || vb.occurs_inv>1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) {
2670+
if (vb.concrete || vb.occurs_inv>1 || vb.intvalued > 1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) {
26482671
restore_env(e, NULL, &se);
26492672
vb.occurs_cov = vb.occurs_inv = 0;
26502673
vb.constraintkind = vb.concrete ? 1 : 2;
@@ -2701,7 +2724,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, size_t
27012724
if (xp2 && jl_is_typevar(xp2)) {
27022725
xb = lookup(e, (jl_tvar_t*)xp2);
27032726
if (xb) {
2704-
xb->intvalued = 1;
2727+
if (xb->intvalued == 0) xb->intvalued = 1;
27052728
xb->offset = offset;
27062729
}
27072730
if (!yp2)
@@ -2710,7 +2733,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, size_t
27102733
if (yp2 && jl_is_typevar(yp2)) {
27112734
yb = lookup(e, (jl_tvar_t*)yp2);
27122735
if (yb) {
2713-
yb->intvalued = 1;
2736+
if (yb->intvalued == 0) yb->intvalued = 1;
27142737
yb->offset = -offset;
27152738
}
27162739
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, Int64}}, C)
2096+
@testintersect(A, Tuple{Tuple{Int, Vararg{Any, N}} where {N}, NTuple{4, Int64}}, C)
2097+
@testintersect(A, Tuple{Tuple{Int, Vararg{Any, N}}, NTuple{4, Int64}} 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{Int64, N}}, Tuple{Int, Int, Vararg{Int64, 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{Int64, N}}, Tuple{Int, Int, Vararg{Int64, N}}} where {N}
2108+
@test_broken C != Tuple{Tuple{Int, Vararg{Int64}}, Tuple{Int, Int, Vararg{Int64}}}
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)