Skip to content

Commit c22b1c1

Browse files
authored
typeintersect: also record chained innervars (#50551)
On master, we only record direct `innervars` (`T` -> `S<:Val{T}`). And chained `innervars` might be ignored (`T` -> `S<:Val{V<:T}`. Before #48228, those chained `innervars` would have been wrapped into an `UnionAll`, thus we just need to check outer vars' lb/ub. Test added. ~Note: this only fix #50456 (comment), the other MWE still get stackoverflow.~
2 parents 18d18dc + cd74337 commit c22b1c1

File tree

2 files changed

+128
-39
lines changed

2 files changed

+128
-39
lines changed

src/subtype.c

Lines changed: 122 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2721,32 +2721,33 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t)
27212721
jl_tvar_t *var = ((jl_unionall_t *)u)->var;
27222722
jl_value_t *ub = var->ub, *body = ((jl_unionall_t *)u)->body;
27232723
assert(var != t);
2724-
if (!jl_has_typevar(var->lb, t)) {
2725-
JL_GC_PUSH3(&ub, &body, &var);
2726-
body = omit_bad_union(body, t);
2727-
if (!jl_has_typevar(body, var)) {
2728-
res = body;
2724+
JL_GC_PUSH3(&ub, &body, &var);
2725+
body = omit_bad_union(body, t);
2726+
if (!jl_has_typevar(body, var)) {
2727+
res = body;
2728+
}
2729+
else if (jl_has_typevar(var->lb, t)) {
2730+
res = jl_bottom_type;
2731+
}
2732+
else {
2733+
ub = omit_bad_union(ub, t);
2734+
if (ub == jl_bottom_type && var->lb != ub) {
2735+
res = jl_bottom_type;
27292736
}
2730-
else {
2731-
ub = omit_bad_union(ub, t);
2732-
if (ub == jl_bottom_type && var->lb != ub) {
2733-
res = jl_bottom_type;
2737+
else if (obviously_egal(var->lb, ub)) {
2738+
JL_TRY {
2739+
res = jl_substitute_var(body, var, ub);
27342740
}
2735-
else if (obviously_egal(var->lb, ub)) {
2736-
JL_TRY {
2737-
res = jl_substitute_var(body, var, ub);
2738-
}
2739-
JL_CATCH {
2740-
res = jl_bottom_type;
2741-
}
2741+
JL_CATCH {
2742+
res = jl_bottom_type;
27422743
}
2743-
else {
2744-
if (ub != var->ub) {
2745-
var = jl_new_typevar(var->name, var->lb, ub);
2746-
body = jl_substitute_var(body, ((jl_unionall_t *)u)->var, (jl_value_t *)var);
2747-
}
2748-
res = jl_new_struct(jl_unionall_type, var, body);
2744+
}
2745+
else {
2746+
if (ub != var->ub) {
2747+
var = jl_new_typevar(var->name, var->lb, ub);
2748+
body = jl_substitute_var(body, ((jl_unionall_t *)u)->var, (jl_value_t *)var);
27492749
}
2750+
res = jl_new_struct(jl_unionall_type, var, body);
27502751
}
27512752
}
27522753
JL_GC_POP();
@@ -2770,9 +2771,9 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t)
27702771
// Caller might not have rooted `res`
27712772
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)
27722773
{
2773-
jl_value_t *varval = NULL;
2774+
jl_value_t *varval = NULL, *ilb = NULL, *iub = NULL, *nivar = NULL;
27742775
jl_tvar_t *newvar = vb->var;
2775-
JL_GC_PUSH2(&res, &newvar);
2776+
JL_GC_PUSH5(&res, &newvar, &ilb, &iub, &nivar);
27762777
// try to reduce var to a single value
27772778
if (jl_is_long(vb->ub) && jl_is_typevar(vb->lb)) {
27782779
varval = vb->ub;
@@ -2806,19 +2807,99 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
28062807
newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub);
28072808

28082809
// remove/replace/rewrap free occurrences of this var in the environment
2810+
2811+
// I. Handle indirect innervars (make them behave like direct innervars).
2812+
// 1) record if btemp->lb/ub has indirect innervars.
2813+
// 2) subtitute `vb->var` with `varval`/`varval`
2814+
// note: We only store the innervar in the outmost `varbinding`,
2815+
// thus we must check all inner env to ensure the recording/subtitution
2816+
// is complete
2817+
int len = current_env_length(e);
2818+
int8_t *blinding_has_innerdep = (int8_t *)alloca(len);
2819+
memset(blinding_has_innerdep, 0, len);
2820+
for (jl_varbinding_t *btemp = e->vars; btemp != NULL; btemp = btemp->prev) {
2821+
if (btemp->innervars != NULL) {
2822+
for (size_t i = 0; i < jl_array_len(btemp->innervars); i++) {
2823+
jl_tvar_t *ivar = (jl_tvar_t*)jl_array_ptr_ref(btemp->innervars, i);
2824+
ilb = ivar->lb; iub = ivar->ub;
2825+
int has_innerdep = 0;
2826+
if (jl_has_typevar(ilb, vb->var)) {
2827+
has_innerdep = 1;
2828+
if (varval) {
2829+
JL_TRY {
2830+
ilb = jl_substitute_var(ilb, vb->var, varval);
2831+
}
2832+
JL_CATCH {
2833+
res = jl_bottom_type;
2834+
}
2835+
}
2836+
else if (newvar != vb->var) {
2837+
ilb = jl_substitute_var(ilb, vb->var, (jl_value_t*)newvar);
2838+
}
2839+
}
2840+
if (jl_has_typevar(iub, vb->var)) {
2841+
has_innerdep = 1;
2842+
if (varval) {
2843+
JL_TRY {
2844+
iub = jl_substitute_var(iub, vb->var, varval);
2845+
}
2846+
JL_CATCH {
2847+
res = jl_bottom_type;
2848+
}
2849+
}
2850+
else if (newvar != vb->var) {
2851+
iub = jl_substitute_var(iub, vb->var, (jl_value_t*)newvar);
2852+
}
2853+
}
2854+
if (!has_innerdep) continue;
2855+
int need_subtitution = 0;
2856+
if (ilb != ivar->lb || iub != ivar->ub) {
2857+
need_subtitution = 1;
2858+
nivar = (jl_value_t *)jl_new_typevar(ivar->name, ilb, iub);
2859+
jl_array_ptr_set(btemp->innervars, i, nivar);
2860+
if (jl_has_typevar(res, ivar))
2861+
res = jl_substitute_var(res, ivar, nivar);
2862+
}
2863+
int envind = 0;
2864+
for (jl_varbinding_t *btemp2 = e->vars; btemp2 != btemp->prev; btemp2 = btemp2->prev) {
2865+
if (jl_has_typevar(btemp2->lb, ivar)) {
2866+
if (need_subtitution)
2867+
btemp2->lb = jl_substitute_var(btemp2->lb, ivar, nivar);
2868+
blinding_has_innerdep[envind] |= 1;
2869+
}
2870+
if (jl_has_typevar(btemp2->ub, ivar)) {
2871+
if (need_subtitution)
2872+
btemp2->ub = jl_substitute_var(btemp2->ub, ivar, nivar);
2873+
blinding_has_innerdep[envind] |= 2;
2874+
}
2875+
envind++;
2876+
}
2877+
}
2878+
}
2879+
}
2880+
// II. Handle direct innervars.
28092881
jl_varbinding_t *wrap = NULL;
2882+
int envind = 0;
28102883
for (jl_varbinding_t *btemp = e->vars; btemp != NULL; btemp = btemp->prev) {
2811-
if (jl_has_typevar(btemp->lb, vb->var)) {
2884+
int has_innerdep = blinding_has_innerdep[envind++];
2885+
int lb_has_innerdep = has_innerdep & 1;
2886+
int ub_has_innerdep = has_innerdep & 2;
2887+
assert(!has_innerdep || btemp->depth0 == vb->depth0);
2888+
int lb_has_dep = jl_has_typevar(btemp->lb, vb->var);
2889+
int ub_has_dep = jl_has_typevar(btemp->ub, vb->var);
2890+
if (lb_has_innerdep || lb_has_dep) {
28122891
if (vb->lb == (jl_value_t*)btemp->var) {
28132892
JL_GC_POP();
28142893
return jl_bottom_type;
28152894
}
28162895
if (varval) {
2817-
JL_TRY {
2818-
btemp->lb = jl_substitute_var(btemp->lb, vb->var, varval);
2819-
}
2820-
JL_CATCH {
2821-
res = jl_bottom_type;
2896+
if (lb_has_dep) { // inner substitution has been handled
2897+
JL_TRY {
2898+
btemp->lb = jl_substitute_var(btemp->lb, vb->var, varval);
2899+
}
2900+
JL_CATCH {
2901+
res = jl_bottom_type;
2902+
}
28222903
}
28232904
}
28242905
else if (btemp->lb == (jl_value_t*)vb->var) {
@@ -2827,7 +2908,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
28272908
else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var)) {
28282909
// if our variable is T, and some outer variable has constraint S = Ref{T},
28292910
// move the `where T` outside `where S` instead of putting it here. issue #21243.
2830-
if (newvar != vb->var)
2911+
if (newvar != vb->var && lb_has_dep) // inner substitution has been handled
28312912
btemp->lb = jl_substitute_var(btemp->lb, vb->var, (jl_value_t*)newvar);
28322913
wrap = btemp;
28332914
}
@@ -2836,20 +2917,23 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
28362917
}
28372918
assert((jl_value_t*)btemp->var != btemp->lb);
28382919
}
2839-
if (jl_has_typevar(btemp->ub, vb->var)) {
2920+
if (ub_has_innerdep || ub_has_dep) {
28402921
if (vb->ub == (jl_value_t*)btemp->var) {
2922+
// TODO: handle `omit_bad_union` correctly if `ub_has_innerdep`
28412923
btemp->ub = omit_bad_union(btemp->ub, vb->var);
28422924
if (btemp->ub == jl_bottom_type && btemp->ub != btemp->lb) {
28432925
JL_GC_POP();
28442926
return jl_bottom_type;
28452927
}
28462928
}
28472929
if (varval) {
2848-
JL_TRY {
2849-
btemp->ub = jl_substitute_var(btemp->ub, vb->var, varval);
2850-
}
2851-
JL_CATCH {
2852-
res = jl_bottom_type;
2930+
if (ub_has_dep) { // inner substitution has been handled
2931+
JL_TRY {
2932+
btemp->ub = jl_substitute_var(btemp->ub, vb->var, varval);
2933+
}
2934+
JL_CATCH {
2935+
res = jl_bottom_type;
2936+
}
28532937
}
28542938
}
28552939
else if (btemp->ub == (jl_value_t*)vb->var) {
@@ -2860,7 +2944,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
28602944
btemp->ub = vb->ub;
28612945
}
28622946
else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var)) {
2863-
if (newvar != vb->var)
2947+
if (newvar != vb->var && ub_has_dep) // inner substitution has been handled
28642948
btemp->ub = jl_substitute_var(btemp->ub, vb->var, (jl_value_t*)newvar);
28652949
wrap = btemp;
28662950
}

test/subtype.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2552,8 +2552,13 @@ end
25522552
@test !<:(Type{Vector{Union{Base.BitInteger, Base.IEEEFloat, StridedArray, Missing, Nothing, Val{T}}}} where {T}, Type{Array{T}} where {T})
25532553

25542554
#issue 50195
2555-
T50195{S} = Pair{S,Set{S}}
25562555
let a = Tuple{Type{X} where X<:Union{Nothing, Val{X1} where {X4, X1<:(Pair{X2, Val{X2}} where X2<:Val{X4})}}},
25572556
b = Tuple{Type{Y} where Y<:(Val{Y1} where {Y4<:Src, Y1<:(Pair{Y2, Val{Y2}} where Y2<:Union{Val{Y4}, Y4})})} where Src
25582557
@test typeintersect(a, b) <: Any
25592558
end
2559+
2560+
#issue 50195
2561+
let a = Tuple{Union{Nothing, Type{Pair{T1}} where T1}}
2562+
b = Tuple{Type{X2} where X2<:(Pair{T2, Y2} where {Src, Z2<:Src, Y2<:Union{Val{Z2}, Z2}})} where T2
2563+
@test !Base.has_free_typevars(typeintersect(a, b))
2564+
end

0 commit comments

Comments
 (0)