From 13bcdf124f7b8baa7fca517ec015ec4b6eb34faa Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 5 Aug 2021 18:03:39 -0400 Subject: [PATCH 1/2] simplify and improve type intersection algorithm a bit This removes some code and makes a class of results more conservative, fixing some potential cases of unsoundness. --- src/subtype.c | 128 +++++++++++++++++------------------------------- test/subtype.jl | 60 +++++++++++++++++------ 2 files changed, 92 insertions(+), 96 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 152d17daeaaaa..0a20b0db09b55 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -68,15 +68,14 @@ typedef struct jl_varbinding_t { int8_t occurs_inv; // occurs in invariant position int8_t occurs_cov; // # of occurrences in covariant position int8_t concrete; // 1 if another variable has a constraint forcing this one to be concrete - // in covariant position, we need to try constraining a variable in different ways: - // 0 - unconstrained - // 1 - less than - // 2 - greater than - // 3 - inexpressible - occurs when the var has non-trivial overlap with another type, - // and we would need to return `intersect(var,other)`. in this case - // we choose to over-estimate the intersection by returning the var. + // constraintkind: in covariant position, we try three different ways to compute var ∩ type: + // let ub = var.ub ∩ type + // 0 - var.ub <: type ? var : ub + // 1 - var.ub = ub; return var + // 2 - either (var.ub = ub; return var), or return ub int8_t constraintkind; int8_t intvalued; // must be integer-valued; i.e. occurs as N in Vararg{_,N} + int8_t limited; int16_t depth0; // # of invariant constructors nested around the UnionAll type for this var // when this variable's integer value is compared to that of another, // it equals `other + offset`. used by vararg length parameters. @@ -759,7 +758,7 @@ static jl_unionall_t *unalias_unionall(jl_unionall_t *u, jl_stenv_t *e) static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param) { u = unalias_unionall(u, e); - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, R ? e->Rinvdepth : e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); e->vars = &vb; @@ -2318,67 +2317,31 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int JL_GC_POP(); return ub; } - else if (bb->constraintkind == 0) { - if (!jl_is_typevar(bb->ub) && !jl_is_typevar(a)) { - if (try_subtype_in_env(bb->ub, a, e, 0, d)) - return (jl_value_t*)b; - } - return R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); - } - else if (bb->concrete || bb->constraintkind == 1) { - jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); - if (ub == jl_bottom_type) - return jl_bottom_type; - JL_GC_PUSH1(&ub); - if (!R && !subtype_bounds_in_env(bb->lb, a, e, 0, d)) { - // this fixes issue #30122. TODO: better fix for R flag. - JL_GC_POP(); - return jl_bottom_type; - } - JL_GC_POP(); - set_bound(&bb->ub, ub, b, e); - return (jl_value_t*)b; - } - else if (bb->constraintkind == 2) { - // TODO: removing this case fixes many test_brokens in test/subtype.jl - // but breaks other tests. - if (!subtype_bounds_in_env(a, bb->ub, e, 1, d)) { - // mark var as unsatisfiable by making it circular - bb->lb = (jl_value_t*)b; - return jl_bottom_type; - } - jl_value_t *lb = simple_join(bb->lb, a); - set_bound(&bb->lb, lb, b, e); - return a; - } - assert(bb->constraintkind == 3); jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); if (ub == jl_bottom_type) return jl_bottom_type; - if (jl_is_typevar(a)) - return (jl_value_t*)b; - if (ub == a) { - if (bb->lb == jl_bottom_type) { - set_bound(&bb->ub, a, b, e); + if (bb->constraintkind == 0) { + JL_GC_PUSH1(&ub); + if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e, 0, d)) { + JL_GC_POP(); return (jl_value_t*)b; } + JL_GC_POP(); return ub; } - else if (bb->ub == bb->lb) { - return ub; + else if (bb->constraintkind == 1) { + set_bound(&bb->ub, ub, b, e); + return (jl_value_t*)b; } - root = NULL; - JL_GC_PUSH2(&root, &ub); - save_env(e, &root, &se); - jl_value_t *ii = R ? intersect_aside(a, bb->lb, e, 1, d) : intersect_aside(bb->lb, a, e, 0, d); - if (ii == jl_bottom_type) { - restore_env(e, root, &se); - ii = (jl_value_t*)b; + assert(bb->constraintkind == 2); + if (!jl_is_typevar(a)) { + if (ub == a && bb->lb != jl_bottom_type) + return ub; + else if (jl_egal(bb->ub, bb->lb)) + return ub; set_bound(&bb->ub, ub, b, e); } - free_env(&se); - JL_GC_POP(); - return ii; + return (jl_value_t*)b; } // test whether `var` occurs inside constructors. `want_inv` tests only inside @@ -2422,7 +2385,7 @@ static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want } // Caller might not have rooted `res` -static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_stenv_t *e) +static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_unionall_t *u, jl_stenv_t *e) { jl_value_t *varval = NULL; jl_tvar_t *newvar = vb->var; @@ -2435,7 +2398,10 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // given x<:T<:x, substitute x for T varval = vb->ub; } - else if (!vb->occurs_inv && is_leaf_bound(vb->ub)) { + // TODO: `vb.occurs_cov == 1` here allows substituting Tuple{<:X} => Tuple{X}, + // which is valid but changes some ambiguity errors so we don't need to do it yet. + else if ((/*vb->occurs_cov == 1 || */is_leaf_bound(vb->ub)) && + !var_occurs_invariant(u->body, u->var, 0)) { // replace T<:x with x in covariant position when possible varval = vb->ub; } @@ -2453,9 +2419,8 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind } } - // prefer generating a fresh typevar, to avoid repeated renaming if the result - // is compared to one of the intersected types later. - if (!varval) + // TODO: this can prevent us from matching typevar identities later + if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub)) newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub); // remove/replace/rewrap free occurrences of this var in the environment @@ -2573,8 +2538,10 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv int envsize = 0; while (btemp != NULL) { envsize++; - if (envsize > 150) + if (envsize > 120) { + vb->limited = 1; return t; + } if (btemp->var == u->var || btemp->lb == (jl_value_t*)u->var || btemp->ub == (jl_value_t*)u->var) { u = rename_unionall(u); @@ -2624,7 +2591,7 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv } if (res != jl_bottom_type) // res is rooted by callee - res = finish_unionall(res, vb, e); + res = finish_unionall(res, vb, u, e); JL_GC_POP(); return res; } @@ -2633,16 +2600,21 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ { jl_value_t *res=NULL, *res2=NULL, *save=NULL, *save2=NULL; jl_savedenv_t se, se2; - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, R ? e->Rinvdepth : e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH6(&res, &save2, &vb.lb, &vb.ub, &save, &vb.innervars); save_env(e, &save, &se); res = intersect_unionall_(t, u, e, R, param, &vb); - if (res != jl_bottom_type) { + if (vb.limited) { + // if the environment got too big, avoid tree recursion and propagate the flag + if (e->vars) + e->vars->limited = 1; + } + else if (res != jl_bottom_type) { if (vb.concrete || vb.occurs_inv>1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) { restore_env(e, NULL, &se); vb.occurs_cov = vb.occurs_inv = 0; - vb.constraintkind = 3; + vb.constraintkind = vb.concrete ? 1 : 2; res = intersect_unionall_(t, u, e, R, param, &vb); } else if (vb.occurs_cov) { @@ -2652,17 +2624,10 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ vb.lb = u->var->lb; vb.ub = u->var->ub; vb.constraintkind = 1; res2 = intersect_unionall_(t, u, e, R, param, &vb); - if (res2 == jl_bottom_type) { - restore_env(e, save, &se); - vb.occurs_cov = vb.occurs_inv = 0; - vb.lb = u->var->lb; vb.ub = u->var->ub; - vb.constraintkind = 2; - res2 = intersect_unionall_(t, u, e, R, param, &vb); - if (res2 == jl_bottom_type) - restore_env(e, save2, &se2); - } if (res2 != jl_bottom_type) res = res2; + else + restore_env(e, save2, &se2); free_env(&se2); } } @@ -3049,14 +3014,13 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa jl_value_t *ub=NULL, *lb=NULL; JL_GC_PUSH2(&lb, &ub); ub = intersect_aside(xub, yub, e, 0, xx ? xx->depth0 : 0); - if (xlb == y) + if (reachable_var(xlb, (jl_tvar_t*)y, e)) lb = ylb; else lb = simple_join(xlb, ylb); if (yy) { - if (!subtype_by_bounds(lb, y, e)) - yy->lb = lb; - if (!subtype_by_bounds(y, ub, e)) + yy->lb = lb; + if (!reachable_var(ub, (jl_tvar_t*)y, e)) yy->ub = ub; assert(yy->ub != y); assert(yy->lb != y); diff --git a/test/subtype.jl b/test/subtype.jl index 17d2b491003fe..5b8ebc6744e72 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -587,7 +587,7 @@ function test_old() @test !(Type{Tuple{Nothing}} <: Tuple{Type{Nothing}}) end -const menagerie = +const easy_menagerie = Any[Bottom, Any, Int, Int8, Integer, Real, Array{Int,1}, AbstractArray{Int,1}, Tuple{Int,Vararg{Integer}}, Tuple{Integer,Vararg{Int}}, Tuple{}, @@ -607,12 +607,14 @@ const menagerie = Array{(@UnionAll T<:Int T), 1}, (@UnionAll T<:Real @UnionAll S<:AbstractArray{T,1} Tuple{T,S}), Union{Int,Ref{Union{Int,Int8}}}, - (@UnionAll T Union{Tuple{T,Array{T,1}}, Tuple{T,Array{Int,1}}}), ] -let new = Any[] - # add variants of each type - for T in menagerie +const hard_menagerie = + Any[(@UnionAll T Union{Tuple{T,Array{T,1}}, Tuple{T,Array{Int,1}}})] + +function add_variants!(types) + new = Any[] + for T in types push!(new, Ref{T}) push!(new, Tuple{T}) push!(new, Tuple{T,T}) @@ -620,9 +622,14 @@ let new = Any[] push!(new, @UnionAll S<:T S) push!(new, @UnionAll S<:T Ref{S}) end - append!(menagerie, new) + append!(types, new) end +add_variants!(easy_menagerie) +add_variants!(hard_menagerie) + +const menagerie = [easy_menagerie; hard_menagerie] + function test_properties() x→y = !x || y ¬T = @UnionAll X>:T Ref{X} @@ -1057,14 +1064,15 @@ function test_intersection() end function test_intersection_properties() - approx = Tuple{Vector{Vector{T}} where T, Vector{Vector{T}} where T} - for T in menagerie - for S in menagerie + for i in eachindex(menagerie) + T = menagerie[i] + for j in eachindex(menagerie) + S = menagerie[j] I = _type_intersect(T,S) I2 = _type_intersect(S,T) @test isequal_type(I, I2) - if I == approx - # TODO: some of these cases give a conservative answer + if i > length(easy_menagerie) || j > length(easy_menagerie) + # TODO: these cases give a conservative answer @test issub(I, T) || issub(I, S) else @test issub(I, T) && issub(I, S) @@ -1796,7 +1804,7 @@ let X1 = Tuple{AlmostLU, Vector{T}} where T, # TODO: the quality of this intersection is not great; for now just test that it # doesn't stack overflow @test I<:X1 || I<:X2 - actual = Tuple{AlmostLU{S, X} where X<:Matrix{S}, Vector{S}} where S<:Union{Float32, Float64} + actual = Tuple{Union{AlmostLU{S, X} where X<:Matrix{S}, AlmostLU{S, <:Matrix}}, Vector{S}} where S<:Union{Float32, Float64} @test I == actual end @@ -1898,8 +1906,8 @@ end # issue #39948 let A = Tuple{Array{Pair{T, JT} where JT<:Ref{T}, 1} where T, Vector}, I = typeintersect(A, Tuple{Vararg{Vector{T}}} where T) - @test_broken I <: A - @test_broken !Base.has_free_typevars(I) + @test I <: A + @test !Base.has_free_typevars(I) end # issue #8915 @@ -1927,3 +1935,27 @@ let A = Tuple{Ref{T}, Vararg{T}} where T, J = typeintersect(A, C) @test_broken J != Union{} end + +let A = Tuple{Dict{I,T}, I, T} where T where I, + B = Tuple{AbstractDict{I,T}, T, I} where T where I + # TODO: we should probably have I == T here + @test typeintersect(A, B) == Tuple{Dict{I,T}, I, T} where {I, T} +end + +let A = Tuple{UnionAll, Vector{Any}}, + B = Tuple{Type{T}, T} where T<:AbstractArray, + I = typeintersect(A, B) + @test !isconcretetype(I) + @test_broken I == Tuple{Type{T}, Vector{Any}} where T<:AbstractArray +end + +@testintersect(Tuple{Type{Vector{<:T}}, T} where {T<:Integer}, + Tuple{Type{T}, AbstractArray} where T<:Array, + Bottom) + +struct S40{_A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11, _Z12, _Z13, _Z14} +end + +@testintersect(Tuple{Type{S40{_A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11, _Z12, _Z13, _Z14}} where _Z14 where _Z13 where _Z12 where _Z11 where _Z10 where _Z9 where _Z8 where _Z7 where _Z6 where _Z5 where _Z4 where _Z3 where _Z2 where _Z1 where _Z where _Y where _X where _W where _V where _U where _T where _S where _R where _Q where _P where _O where _N where _M where _L where _K where _J where _I where _H where _G where _F where _E where _D where _C where _B where _A, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any}, + Tuple{Type{S40{A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40} where A40 where A39 where A38 where A37 where A36 where A35 where A34 where A33 where A32 where A31 where A30 where A29 where A28 where A27 where A26 where A25 where A24 where A23 where A22 where A21 where A20 where A19 where A18 where A17 where A16 where A15 where A14 where A13 where A12 where A11 where A10 where A9 where A8 where A7 where A6 where A5 where A4 where A3 where A2 where A1}, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40} where A40 where A39 where A38 where A37 where A36 where A35 where A34 where A33 where A32 where A31 where A30 where A29 where A28 where A27 where A26 where A25 where A24 where A23 where A22 where A21 where A20 where A19 where A18 where A17 where A16 where A15 where A14 where A13 where A12 where A11 where A10 where A9 where A8 where A7 where A6 where A5 where A4 where A3 where A2 where A1, + Bottom) From 71757cd618472d291b168671fc3f5e75611b4fd8 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 12 Aug 2021 13:04:53 -0400 Subject: [PATCH 2/2] give wider/safer intersection result for vars used in both invariant and covariant position fixes #41738 --- src/subtype.c | 62 ++++++++++++++++++++++++++----------------------- test/subtype.jl | 18 +++++++++++--- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 0a20b0db09b55..c3512eeb17dac 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -101,6 +101,7 @@ typedef struct jl_stenv_t { int ignore_free; // treat free vars as black boxes; used during intersection int intersection; // true iff subtype is being called from intersection int emptiness_only; // true iff intersection only needs to test for emptiness + int triangular; // when intersecting Ref{X} with Ref{<:Y} } jl_stenv_t; // state manipulation utilities @@ -1444,6 +1445,7 @@ static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz) e->ignore_free = 0; e->intersection = 0; e->emptiness_only = 0; + e->triangular = 0; e->Lunions.depth = 0; e->Runions.depth = 0; e->Lunions.more = 0; e->Runions.more = 0; e->Lunions.used = 0; e->Runions.used = 0; @@ -2203,7 +2205,7 @@ static void set_bound(jl_value_t **bound, jl_value_t *val, jl_tvar_t *v, jl_sten return; jl_varbinding_t *btemp = e->vars; while (btemp != NULL) { - if (btemp->lb == (jl_value_t*)v && btemp->ub == (jl_value_t*)v && + if ((btemp->lb == (jl_value_t*)v || btemp->ub == (jl_value_t*)v) && in_union(val, (jl_value_t*)btemp->var)) return; btemp = btemp->prev; @@ -2255,6 +2257,21 @@ static int reachable_var(jl_value_t *x, jl_tvar_t *y, jl_stenv_t *e) return reachable_var(xv->ub, y, e) || reachable_var(xv->lb, y, e); } +// check whether setting v == t implies v == SomeType{v}, which is unsatisfiable. +static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTSAFEPOINT +{ + if (var_occurs_inside(t, v, 0, 0)) + return 1; + jl_varbinding_t *btemp = e->vars; + while (btemp != NULL) { + if (btemp->lb == (jl_value_t*)v && btemp->ub == (jl_value_t*)v && + var_occurs_inside(t, btemp->var, 0, 0)) + return 1; + btemp = btemp->prev; + } + return 0; +} + static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param) { jl_varbinding_t *bb = lookup(e, b); @@ -2284,7 +2301,9 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int ub = a; } else { + e->triangular++; ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); + e->triangular--; save_env(e, &root, &se); int issub = subtype_in_env_existential(bb->lb, ub, e, 0, d); restore_env(e, root, &se); @@ -2296,20 +2315,10 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } if (ub != (jl_value_t*)b) { if (jl_has_free_typevars(ub)) { - // constraint X == Ref{X} is unsatisfiable. also check variables set equal to X. - if (var_occurs_inside(ub, b, 0, 0)) { + if (check_unsat_bound(ub, b, e)) { JL_GC_POP(); return jl_bottom_type; } - jl_varbinding_t *btemp = e->vars; - while (btemp != NULL) { - if (btemp->lb == (jl_value_t*)b && btemp->ub == (jl_value_t*)b && - var_occurs_inside(ub, btemp->var, 0, 0)) { - JL_GC_POP(); - return jl_bottom_type; - } - btemp = btemp->prev; - } } bb->ub = ub; bb->lb = ub; @@ -2320,7 +2329,13 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); if (ub == jl_bottom_type) return jl_bottom_type; - if (bb->constraintkind == 0) { + if (bb->constraintkind == 1 || e->triangular) { + if (e->triangular && check_unsat_bound(ub, b, e)) + return jl_bottom_type; + set_bound(&bb->ub, ub, b, e); + return (jl_value_t*)b; + } + else if (bb->constraintkind == 0) { JL_GC_PUSH1(&ub); if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e, 0, d)) { JL_GC_POP(); @@ -2329,10 +2344,6 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int JL_GC_POP(); return ub; } - else if (bb->constraintkind == 1) { - set_bound(&bb->ub, ub, b, e); - return (jl_value_t*)b; - } assert(bb->constraintkind == 2); if (!jl_is_typevar(a)) { if (ub == a && bb->lb != jl_bottom_type) @@ -2598,11 +2609,11 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param) { - jl_value_t *res=NULL, *res2=NULL, *save=NULL, *save2=NULL; - jl_savedenv_t se, se2; + jl_value_t *res=NULL, *save=NULL; + jl_savedenv_t se; jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, 0, 0, R ? e->Rinvdepth : e->invdepth, 0, NULL, e->vars }; - JL_GC_PUSH6(&res, &save2, &vb.lb, &vb.ub, &save, &vb.innervars); + JL_GC_PUSH5(&res, &vb.lb, &vb.ub, &save, &vb.innervars); save_env(e, &save, &se); res = intersect_unionall_(t, u, e, R, param, &vb); if (vb.limited) { @@ -2617,18 +2628,11 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ vb.constraintkind = vb.concrete ? 1 : 2; res = intersect_unionall_(t, u, e, R, param, &vb); } - else if (vb.occurs_cov) { - save_env(e, &save2, &se2); + else if (vb.occurs_cov && !var_occurs_invariant(u->body, u->var, 0)) { restore_env(e, save, &se); vb.occurs_cov = vb.occurs_inv = 0; - vb.lb = u->var->lb; vb.ub = u->var->ub; vb.constraintkind = 1; - res2 = intersect_unionall_(t, u, e, R, param, &vb); - if (res2 != jl_bottom_type) - res = res2; - else - restore_env(e, save2, &se2); - free_env(&se2); + res = intersect_unionall_(t, u, e, R, param, &vb); } } free_env(&se); diff --git a/test/subtype.jl b/test/subtype.jl index 5b8ebc6744e72..d403716646de0 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1577,7 +1577,7 @@ f31082(::Pair{B, C}, ::C, ::C) where {B, C} = 1 Tuple{Type{Val{T}},Int,T} where T) @testintersect(Tuple{Type{Val{T}},Integer,T} where T, Tuple{Type,Int,Integer}, - Tuple{Type{Val{T}},Int,T} where T<:Integer) + Tuple{Type{Val{T}},Int,Integer} where T) @testintersect(Tuple{Type{Val{T}},Integer,T} where T>:Integer, Tuple{Type,Int,Integer}, Tuple{Type{Val{T}},Int,Integer} where T>:Integer) @@ -1866,7 +1866,7 @@ let A = Tuple{Type{T} where T<:Ref, Ref, Union{T, Union{Ref{T}, T}} where T<:Ref I = typeintersect(A,B) # this was a case where <: disagreed with === (due to a badly-normalized type) @test I == typeintersect(A,B) - @test I == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref + @test I == Tuple{Type{T}, Ref{T}, Ref} where T<:Ref end # issue #39218 @@ -1946,7 +1946,7 @@ let A = Tuple{UnionAll, Vector{Any}}, B = Tuple{Type{T}, T} where T<:AbstractArray, I = typeintersect(A, B) @test !isconcretetype(I) - @test_broken I == Tuple{Type{T}, Vector{Any}} where T<:AbstractArray + @test I == Tuple{Type{T}, Vector{Any}} where T<:AbstractArray end @testintersect(Tuple{Type{Vector{<:T}}, T} where {T<:Integer}, @@ -1959,3 +1959,15 @@ end @testintersect(Tuple{Type{S40{_A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11, _Z12, _Z13, _Z14}} where _Z14 where _Z13 where _Z12 where _Z11 where _Z10 where _Z9 where _Z8 where _Z7 where _Z6 where _Z5 where _Z4 where _Z3 where _Z2 where _Z1 where _Z where _Y where _X where _W where _V where _U where _T where _S where _R where _Q where _P where _O where _N where _M where _L where _K where _J where _I where _H where _G where _F where _E where _D where _C where _B where _A, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any}, Tuple{Type{S40{A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40} where A40 where A39 where A38 where A37 where A36 where A35 where A34 where A33 where A32 where A31 where A30 where A29 where A28 where A27 where A26 where A25 where A24 where A23 where A22 where A21 where A20 where A19 where A18 where A17 where A16 where A15 where A14 where A13 where A12 where A11 where A10 where A9 where A8 where A7 where A6 where A5 where A4 where A3 where A2 where A1}, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40} where A40 where A39 where A38 where A37 where A36 where A35 where A34 where A33 where A32 where A31 where A30 where A29 where A28 where A27 where A26 where A25 where A24 where A23 where A22 where A21 where A20 where A19 where A18 where A17 where A16 where A15 where A14 where A13 where A12 where A11 where A10 where A9 where A8 where A7 where A6 where A5 where A4 where A3 where A2 where A1, Bottom) + +let A = Tuple{Any, Type{Ref{_A}} where _A}, + B = Tuple{Type{T}, Type{<:Union{Ref{T}, T}}} where T, + I = typeintersect(A, B) + @test I != Union{} + # TODO: this intersection result is still too narrow + @test_broken Tuple{Type{Ref{Integer}}, Type{Ref{Integer}}} <: I +end + +@testintersect(Tuple{Type{T}, T} where T<:(Tuple{Vararg{_A, _B}} where _B where _A), + Tuple{Type{Tuple{Vararg{_A, N}} where _A<:F}, Pair{N, F}} where F where N, + Bottom)