Skip to content

Commit 6f8e1d3

Browse files
fix #40048, stack overflows in type intersection (#41687) (#41772)
(cherry picked from commit ca0331f) Co-authored-by: Jeff Bezanson <jeff.bezanson@gmail.com>
1 parent 13d8a10 commit 6f8e1d3

File tree

2 files changed

+34
-14
lines changed

2 files changed

+34
-14
lines changed

src/subtype.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,13 +2210,29 @@ static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *
22102210
return issub;
22112211
}
22122212

2213+
// See if var y is reachable from x via bounds; used to avoid cycles.
2214+
static int reachable_var(jl_value_t *x, jl_tvar_t *y, jl_stenv_t *e)
2215+
{
2216+
if (in_union(x, (jl_value_t*)y))
2217+
return 1;
2218+
if (!jl_is_typevar(x))
2219+
return 0;
2220+
jl_varbinding_t *xv = lookup(e, (jl_tvar_t*)x);
2221+
if (xv == NULL)
2222+
return 0;
2223+
return reachable_var(xv->ub, y, e) || reachable_var(xv->lb, y, e);
2224+
}
2225+
22132226
static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param)
22142227
{
22152228
jl_varbinding_t *bb = lookup(e, b);
22162229
if (bb == NULL)
22172230
return R ? intersect_aside(a, b->ub, e, 1, 0) : intersect_aside(b->ub, a, e, 0, 0);
2218-
if (bb->lb == bb->ub && jl_is_typevar(bb->lb) && bb->lb != (jl_value_t*)b)
2231+
if (reachable_var(bb->lb, b, e) || reachable_var(bb->ub, b, e))
2232+
return a;
2233+
if (bb->lb == bb->ub && jl_is_typevar(bb->lb)) {
22192234
return intersect(a, bb->lb, e, param);
2235+
}
22202236
if (!jl_is_type(a) && !jl_is_typevar(a))
22212237
return set_var_to_const(bb, a, NULL);
22222238
int d = bb->depth0;
@@ -2521,7 +2537,11 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv
25212537
// if the var for this unionall (based on identity) already appears somewhere
25222538
// in the environment, rename to get a fresh var.
25232539
// TODO: might need to look inside types in btemp->lb and btemp->ub
2540+
int envsize = 0;
25242541
while (btemp != NULL) {
2542+
envsize++;
2543+
if (envsize > 150)
2544+
return t;
25252545
if (btemp->var == u->var || btemp->lb == (jl_value_t*)u->var ||
25262546
btemp->ub == (jl_value_t*)u->var) {
25272547
u = rename_unionall(u);
@@ -2923,19 +2943,6 @@ static int subtype_by_bounds(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
29232943
return compareto_var(x, (jl_tvar_t*)y, e, -1) || compareto_var(y, (jl_tvar_t*)x, e, 1);
29242944
}
29252945

2926-
// See if var y is reachable from x via bounds; used to avoid cycles.
2927-
static int reachable_var(jl_value_t *x, jl_tvar_t *y, jl_stenv_t *e)
2928-
{
2929-
if (x == (jl_value_t*)y)
2930-
return 1;
2931-
if (!jl_is_typevar(x))
2932-
return 0;
2933-
jl_varbinding_t *xv = lookup(e, (jl_tvar_t*)x);
2934-
if (xv == NULL)
2935-
return 0;
2936-
return reachable_var(xv->ub, y, e) || reachable_var(xv->lb, y, e);
2937-
}
2938-
29392946
// `param` means we are currently looking at a parameter of a type constructor
29402947
// (as opposed to being outside any type constructor, or comparing variable bounds).
29412948
// this is used to record the positions where type variables occur for the

test/subtype.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,3 +1914,16 @@ end
19141914
f18985(x::T, y...) where {T<:Union{Int32,Int64}} = (length(y), f18985(y[1], y[2:end]...)...)
19151915
f18985(x::T) where {T<:Union{Int32,Int64}} = 100
19161916
@test f18985(1, 2, 3) == (2, 1, 100)
1917+
1918+
# issue #40048
1919+
let A = Tuple{Ref{T}, Vararg{T}} where T,
1920+
B = Tuple{Ref{U}, Union{Ref{S}, Ref{U}, Int}, Union{Ref{S}, S}} where S where U,
1921+
C = Tuple{Ref{U}, Union{Ref{S}, Ref{U}, Ref{W}}, Union{Ref{S}, W, V}} where V<:AbstractArray where W where S where U
1922+
I = typeintersect(A, B)
1923+
@test I != Union{}
1924+
@test I <: A
1925+
@test I <: B
1926+
# avoid stack overflow
1927+
J = typeintersect(A, C)
1928+
@test_broken J != Union{}
1929+
end

0 commit comments

Comments
 (0)