From 01a4a30a680a21d41151dd7066f0dd6b8b890045 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 24 Aug 2022 23:25:48 +0800 Subject: [PATCH 1/9] Avoid setting `offset` when we intersect `Vararg`'s eltype. `var->offset` is used to recode the length difference of 2 `Vararg`s. But `Vararg`'s length might also be used in the type field, e.g. `Tuple{Vararg{Val{N}, N}} where {N}`, where we should ignore `offset` when we intersect `Val{N}`. This commit move the offset setting/erasing into `intersect_varargs`. --- src/subtype.c | 63 ++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index c01ee54e683be..d723c6f008b90 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2670,7 +2670,7 @@ static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8 } static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); -static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, jl_stenv_t *e, int param) +static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t offset, jl_stenv_t *e, int param) { // Vararg: covariant in first parameter, invariant in second jl_value_t *xp1=jl_unwrap_vararg(vmx), *xp2=jl_unwrap_vararg_num(vmx), @@ -2688,19 +2688,24 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, jl_sten JL_GC_POP(); return ii; } + jl_varbinding_t *xb = NULL, *yb = NULL; if (xp2 && jl_is_typevar(xp2)) { - jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)xp2); - if (xb) xb->intvalued = 1; - if (!yp2) { - i2 = bound_var_below((jl_tvar_t*)xp2, xb, e); + xb = lookup(e, (jl_tvar_t*)xp2); + if (xb) { + xb->intvalued = 1; + xb->offset = offset; } + if (!yp2) + i2 = bound_var_below((jl_tvar_t*)xp2, xb, e); } if (yp2 && jl_is_typevar(yp2)) { - jl_varbinding_t *yb = lookup(e, (jl_tvar_t*)yp2); - if (yb) yb->intvalued = 1; - if (!xp2) { - i2 = bound_var_below((jl_tvar_t*)yp2, yb, e); + yb = lookup(e, (jl_tvar_t*)yp2); + if (yb) { + yb->intvalued = 1; + yb->offset = -offset; } + if (!xp2) + i2 = bound_var_below((jl_tvar_t*)yp2, yb, e); } if (xp2 && yp2) { // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 @@ -2711,6 +2716,8 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, jl_sten i2 = jl_bottom_type; } } + if (xb) xb->offset = 0; + if (yb) yb->offset = 0; ii = i2 == jl_bottom_type ? (jl_value_t*)jl_bottom_type : (jl_value_t*)jl_wrap_vararg(ii, i2); JL_GC_POP(); return ii; @@ -2749,28 +2756,14 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), i); break; } - jl_varbinding_t *xb=NULL, *yb=NULL; jl_value_t *ii = NULL; - if (vx && vy) { - // {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}} = {(A∩S)^n...,Vararg{T∩S,N}} plus N = M-n - jl_value_t *xlen = jl_unwrap_vararg_num(xi); - if (xlen && jl_is_typevar(xlen)) { - xb = lookup(e, (jl_tvar_t*)xlen); - if (xb) - xb->offset = ly-lx; - } - jl_value_t *ylen = jl_unwrap_vararg_num(yi); - if (ylen && jl_is_typevar(ylen)) { - yb = lookup(e, (jl_tvar_t*)ylen); - if (yb) - yb->offset = lx-ly; - } + if (vx && vy) ii = intersect_varargs((jl_vararg_t*)xi, (jl_vararg_t*)yi, + ly - lx, // xi's offset: {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}} + // {(A∩S)^n...,Vararg{T∩S,N}} plus N = M-n e, param); - if (xb) xb->offset = 0; - if (yb) yb->offset = 0; - } else { + else { if (vx) xi = jl_unwrap_vararg(xi); if (vy) @@ -2779,6 +2772,13 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten } if (ii == jl_bottom_type) { if (vx && vy) { + jl_varbinding_t *xb=NULL, *yb=NULL; + jl_value_t *xlen = jl_unwrap_vararg_num(xi); + if (xlen && jl_is_typevar(xlen)) + xb = lookup(e, (jl_tvar_t*)xlen); + jl_value_t *ylen = jl_unwrap_vararg_num(yi); + if (ylen && jl_is_typevar(ylen)) + yb = lookup(e, (jl_tvar_t*)ylen); int len = i > j ? i : j; if ((xb && jl_is_long(xb->lb) && lx-1+jl_unbox_long(xb->lb) != len) || (yb && jl_is_long(yb->lb) && ly-1+jl_unbox_long(yb->lb) != len)) { @@ -2894,13 +2894,10 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t jl_savedenv_t se; JL_GC_PUSH2(&ii, &root); save_env(e, &root, &se); - if (!subtype_in_env_existential(x, y, e, 0, e->invdepth)) { + if (!subtype_in_env_existential(x, y, e, 0, e->invdepth)) + ii = NULL; + else if (!subtype_in_env_existential(y, x, e, 0, e->invdepth)) ii = NULL; - } - else { - if (!subtype_in_env_existential(y, x, e, 0, e->invdepth)) - ii = NULL; - } restore_env(e, root, &se); free_env(&se); JL_GC_POP(); From e6d2624adb4b20ec0bc57c82e87cf7328c45fd3a Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Thu, 1 Sep 2022 00:08:40 +0800 Subject: [PATCH 2/9] Skip subtype check if `intersect_invariant` calls `set_vat_to_const`. Co-Authored-By: Jameson Nash --- src/subtype.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/subtype.c b/src/subtype.c index d723c6f008b90..23a3ebc438ab5 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2877,6 +2877,11 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t jl_value_t *ii = intersect(x, y, e, 2); e->invdepth--; e->Rinvdepth--; + // Skip the following subtype check if `ii` was returned from `set_vat_to_const`. + // As `var_gt`/`var_lt` might not handle `Vararg` length offset correctly. + // TODO: fix this on subtype side and remove this branch. + if (jl_is_long(ii) && ((jl_is_typevar(x) && jl_is_long(y)) || (jl_is_typevar(y) && jl_is_long(x)))) + return ii; if (jl_is_typevar(x) && jl_is_typevar(y) && (jl_is_typevar(ii) || !jl_is_type(ii))) return ii; if (ii == jl_bottom_type) { From f29a013366cc4a58b468b4137e98694710f1b8ed Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 23 Aug 2022 21:58:30 +0800 Subject: [PATCH 3/9] Add tuple length offset when we intersect 2 `Vararg`'s length. --- src/subtype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subtype.c b/src/subtype.c index 23a3ebc438ab5..15772cae6ed7b 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2170,7 +2170,7 @@ static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_ bb->lb = bb->ub = v; } else if (jl_is_long(v) && jl_is_long(bb->lb)) { - if (jl_unbox_long(v) != jl_unbox_long(bb->lb)) + if (jl_unbox_long(v) + offset != jl_unbox_long(bb->lb)) return jl_bottom_type; } else if (!jl_egal(v, bb->lb)) { From 38829a0ac4eb4cc96ce39401ce7d37cd7509ca10 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Tue, 23 Aug 2022 21:14:13 +0800 Subject: [PATCH 4/9] Always return the shorter `Vararg` length. The type `var` might be switched during intersection. Thus previous result looks order dependent. When we intersect 2 `Vararg`s' length, we should always return the shorter one. As we has consumed the extra elements in `intersect_tuple`. Also fix #37257 Co-Authored-By: Jameson Nash --- src/subtype.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 15772cae6ed7b..87f3a0f4f650f 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2165,13 +2165,22 @@ static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_ offset = -othervar->offset; assert(!othervar || othervar->offset == -offset); if (bb->lb == jl_bottom_type && bb->ub == (jl_value_t*)jl_any_type) { - if (jl_is_long(v)) - v = jl_box_long(jl_unbox_long(v) + offset); - bb->lb = bb->ub = v; + if (jl_is_long(v)) { + size_t iv = jl_unbox_long(v); + v = jl_box_long(iv + offset); + bb->lb = bb->ub = v; + // Here we always return the shorter `Vararg`'s length. + if (offset > 0) + return jl_box_long(iv); + } + else + bb->lb = bb->ub = v; } else if (jl_is_long(v) && jl_is_long(bb->lb)) { if (jl_unbox_long(v) + offset != jl_unbox_long(bb->lb)) return jl_bottom_type; + // Here we always return the shorter `Vararg`'s length. + if (offset < 0) return bb->lb; } else if (!jl_egal(v, bb->lb)) { return jl_bottom_type; @@ -2186,11 +2195,13 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_ return jl_bottom_type; record_var_occurrence(bb, e, 2); if (jl_is_long(bb->lb)) { - if (bb->offset == 0) - return bb->lb; - if (jl_unbox_long(bb->lb) < bb->offset) + ssize_t blb = jl_unbox_long(bb->lb); + if ((blb < bb->offset) || (blb < 0)) return jl_bottom_type; - return jl_box_long(jl_unbox_long(bb->lb) - bb->offset); + // Here we always return the shorter `Vararg`'s length. + if (bb->offset <= 0) + return bb->lb; + return jl_box_long(blb - bb->offset); } return (jl_value_t*)tv; } @@ -3050,6 +3061,9 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa assert(xx->ub != x); } JL_GC_POP(); + // Here we always return the shorter `Vararg`'s length. + if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) + return x; return y; } record_var_occurrence(xx, e, param); From b7f47d5394ba7593b90be68d7203f1f055f6c4f9 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 24 Aug 2022 00:04:21 +0800 Subject: [PATCH 5/9] Avoid set `var`'s bounds if `offset != 0` if `offset != 0`, then these 2 var should have different value, thus it's invalid to set bounds. --- src/subtype.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 87f3a0f4f650f..13f92c51e57cf 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2165,7 +2165,9 @@ static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_ offset = -othervar->offset; assert(!othervar || othervar->offset == -offset); if (bb->lb == jl_bottom_type && bb->ub == (jl_value_t*)jl_any_type) { - if (jl_is_long(v)) { + if (offset == 0) + bb->lb = bb->ub = v; + else if (jl_is_long(v)) { size_t iv = jl_unbox_long(v); v = jl_box_long(iv + offset); bb->lb = bb->ub = v; @@ -2174,7 +2176,7 @@ static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_ return jl_box_long(iv); } else - bb->lb = bb->ub = v; + return jl_bottom_type; } else if (jl_is_long(v) && jl_is_long(bb->lb)) { if (jl_unbox_long(v) + offset != jl_unbox_long(bb->lb)) @@ -3048,14 +3050,14 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa lb = ylb; else lb = simple_join(xlb, ylb); - if (yy) { + if (yy && yy->offset == 0) { yy->lb = lb; if (!reachable_var(ub, (jl_tvar_t*)y, e)) yy->ub = ub; assert(yy->ub != y); assert(yy->lb != y); } - if (xx && !reachable_var(y, (jl_tvar_t*)x, e)) { + if (xx && xx->offset == 0 && !reachable_var(y, (jl_tvar_t*)x, e)) { xx->lb = y; xx->ub = y; assert(xx->ub != x); From a6a5f0094cb318387c40473facc83f1dc1d19a0b Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 24 Aug 2022 00:04:33 +0800 Subject: [PATCH 6/9] subtype: add tests for `Vararg` expansion. Ref #36443 Ref #37257 --- test/subtype.jl | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/subtype.jl b/test/subtype.jl index b13dcdfa7a83a..be3cfdb3f9a72 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2020,3 +2020,63 @@ end #issue #43082 struct X43082{A, I, B<:Union{Ref{I},I}}; end @testintersect(Tuple{X43082{T}, Int} where T, Tuple{X43082{Int}, Any}, Tuple{X43082{Int}, Int}) + +#issue #36443 +let C = Tuple{Val{3},Int,Int,Int}, + As = (Tuple{Val{N},Vararg{T,N}} where {T,N}, + Tuple{Val{N},Vararg{T,N}} where {N,T}), + Bs = (Tuple{Val{3},Int,Vararg{T,N}} where {T,N}, + Tuple{Val{3},Int,Vararg{T,N}} where {N,T}, + Tuple{Val{3},Int,Vararg{T}} where {T}, + Tuple{Val{3},Int,Vararg{T,2}} where {T}) + for A in As, B in Bs + @testintersect(A, B, C) + end +end + +let A = Tuple{Type{Val{N}},Tuple{Vararg{T,N}} where T} where N, + C = Tuple{Type{Val{2}},Tuple{T,T} where T} + @testintersect(A, Tuple{Type{Val{2}},Tuple{Vararg{T,N}} where T} where N, C) + @testintersect(A, Tuple{Type{Val{2}},Tuple{T,Vararg{T,N}} where T} where N, C) + @testintersect(A, Tuple{Type{Val{2}},Tuple{T,T,Vararg{T,N}} where T} where N, C) +end + +let f36443(::NTuple{N}=[(f36443,),(1,2)][2],::Val{N}=Val(2)) where{N} = 0 + @test f36443() == 0; +end + +let C = Tuple{Val{3},Int,Int,Int,Int}, + As = (Tuple{Val{N},Int,Vararg{T,N}} where {T,N}, + Tuple{Val{N},Int,Vararg{T,N}} where {N,T}), + Bs = (Tuple{Val{3},Vararg{T,N}} where {T,N}, + Tuple{Val{3},Vararg{T,N}} where {N,T}, + Tuple{Val{3},Vararg{T}} where {T}) + for A in As, B in Bs + @testintersect(A, B, C) + end +end + +#issue #37257 +let T = Tuple{Val{N}, Any, Any, Vararg{Any,N}} where N, + C = Tuple{Val{1}, Any, Any, Any} + @testintersect(T, Tuple{Val{1}, Vararg{Any}}, C) + @testintersect(T, Tuple{Val{1}, Any, Vararg{Any}}, C) + @testintersect(T, Tuple{Val{1}, Any, Any, Vararg{Any}}, C) + @testintersect(T, Tuple{Val{1}, Any, Any, Any, Vararg{Any}}, C) + @testintersect(T, Tuple{Val{1}, Any, Any, Any, Any, Vararg{Any}}, Union{}) +end + +let A = Tuple{NTuple{N,Any},Val{N}} where {N}, + C = Tuple{NTuple{4,Any},Val{4}} + @testintersect(A, Tuple{Tuple{Vararg{Any,N}},Val{4}} where {N}, C) + @testintersect(A, Tuple{Tuple{Vararg{Any}},Val{4}}, C) + @testintersect(A, Tuple{Tuple{Vararg{Any,N}} where {N},Val{4}}, C) + + @testintersect(A, Tuple{Tuple{Any,Vararg{Any,N}},Val{4}} where {N}, C) + @testintersect(A, Tuple{Tuple{Any,Vararg{Any}},Val{4}}, C) + @testintersect(A, Tuple{Tuple{Any,Vararg{Any,N}} where {N},Val{4}}, C) + + @testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any,N}},Val{4}} where {N}, Union{}) + @testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any}},Val{4}}, Union{}) + @testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any,N}} where {N},Val{4}}, Union{}) +end From 1268582291c0818533e22d0f4fd5a7260514ac34 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Mon, 29 Aug 2022 11:06:14 +0800 Subject: [PATCH 7/9] Make `bound_var_below` return `NULL` if the input typevar is not valid. If offset > 0, the correct result is `var - offset` if expressible. So an unbounded typevar should not be returned in this case as it might be a diagonal var. Since the result could be improved if `N` get fixed, set `intvalued` to 2 as a re-intersection hint. --- src/subtype.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 13f92c51e57cf..3a703c59f26c8 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -74,7 +74,11 @@ typedef struct jl_varbinding_t { // 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} + // intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N} + // 0: No restriction + // 1: must be unbounded/ or fixed to a `Int`/typevar + // 2: we have some imprecise vararg length intersection that can be improved if this var is const valued. + int8_t intvalued; 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, @@ -2205,6 +2209,10 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_ return bb->lb; return jl_box_long(blb - bb->offset); } + if (bb->offset > 0) { + bb->intvalued = 2; + return NULL; + } return (jl_value_t*)tv; } @@ -2456,6 +2464,10 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind } } + // vb is still unbounded. + if (vb->intvalued == 2 && !(varval && jl_is_long(varval))) + vb->intvalued = 1; + // 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); @@ -2648,7 +2660,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ 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)) { + if (vb.concrete || vb.occurs_inv>1 || vb.intvalued > 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 = vb.concrete ? 1 : 2; @@ -2705,7 +2717,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t if (xp2 && jl_is_typevar(xp2)) { xb = lookup(e, (jl_tvar_t*)xp2); if (xb) { - xb->intvalued = 1; + if (xb->intvalued == 0) xb->intvalued = 1; xb->offset = offset; } if (!yp2) @@ -2714,7 +2726,7 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t if (yp2 && jl_is_typevar(yp2)) { yb = lookup(e, (jl_tvar_t*)yp2); if (yb) { - yb->intvalued = 1; + if (yb->intvalued == 0) yb->intvalued = 1; yb->offset = -offset; } if (!xp2) @@ -3064,8 +3076,10 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa } JL_GC_POP(); // Here we always return the shorter `Vararg`'s length. - if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) + if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) { + if (yy) yy->intvalued = 2; return x; + } return y; } record_var_occurrence(xx, e, param); From 2abedf6d4db01bba03cef3b60fad735b1db1b90e Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Mon, 29 Aug 2022 11:05:38 +0800 Subject: [PATCH 8/9] Avoid set var's lb if intersect return a Vararg with free length. (Null or a local type var) But `check_unsat_bound` should not be skipped. Co-Authored-By: Jameson Nash --- src/subtype.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/subtype.c b/src/subtype.c index 3a703c59f26c8..0ad491cab15ce 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2301,6 +2301,26 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS return 0; } +static int has_free_vararg_length(jl_value_t *a, jl_stenv_t *e) { + if (jl_is_unionall(a)) + a = jl_unwrap_unionall(a); + if (jl_is_datatype(a) && jl_is_tuple_type((jl_datatype_t *)a)) { + size_t lx = jl_nparams((jl_datatype_t *)a); + if (lx > 0) { + jl_value_t *la = jl_tparam((jl_datatype_t *)a, lx-1); + if (jl_is_vararg(la)) { + jl_value_t *len = jl_unwrap_vararg_num((jl_vararg_t *)la); + // return 1 if we meet a vararg with Null length + if (!len) return 1; + // or a typevar not in the current env. + if (jl_is_typevar(len)) + return lookup(e, (jl_tvar_t *)len) == NULL; + } + } + } + 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); @@ -2355,6 +2375,11 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } } bb->ub = ub; + // We get a imprecise Tuple here. Don't change `lb` and return the typevar directly. + if (has_free_vararg_length(ub, e) && !has_free_vararg_length(a, e)) { + JL_GC_POP(); + return (jl_value_t*)b; + } bb->lb = ub; } JL_GC_POP(); From 4654cfa10b373c60886c7c815efe68e5e2c9b946 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Mon, 29 Aug 2022 11:06:17 +0800 Subject: [PATCH 9/9] subtyping: add tests for various Vararg bounds bugs Refs #39088 Refs #39098 --- test/subtype.jl | 56 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/test/subtype.jl b/test/subtype.jl index be3cfdb3f9a72..3e8f2ccad6bbf 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1043,7 +1043,12 @@ function test_intersection() Type{Tuple{Int,T}} where T<:Integer) @testintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N, - Type{Tuple{Int,Vararg{Int,N}}} where N) + !Union{}) + + @test typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) != Type{Tuple{Int,Vararg{Int}}} + @test_broken typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) == Type{Tuple{Int,Vararg{Int,N}}} where N + @test_broken typeintersect(Type{<:Tuple{Any,Vararg{Any}}}, Type{Tuple{Vararg{Int,N}}} where N) != Type{<:Tuple{Int,Vararg{Int}}} + @testintersect(Type{<:Array}, Type{AbstractArray{T}} where T, Bottom) @@ -2080,3 +2085,52 @@ let A = Tuple{NTuple{N,Any},Val{N}} where {N}, @testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any}},Val{4}}, Union{}) @testintersect(A, Tuple{Tuple{Any,Any,Any,Any,Any,Vararg{Any,N}} where {N},Val{4}}, Union{}) end + +#issue #39088 +let + a() = c((1,), (1,1,1,1)) + c(d::NTuple{T}, ::NTuple{T}) where T = d + c(d::NTuple{f}, b) where f = c((d..., f), b) + j(h::NTuple{T}, ::NTuple{T} = a()) where T = nothing + @test j((1,1,1,1)) === nothing +end + +let A = Tuple{NTuple{N, Int}, NTuple{N, Int}} where N, + C = Tuple{NTuple{4, Int}, NTuple{4, Int}} + @testintersect(A, Tuple{Tuple{Int, Vararg{Any}}, NTuple{4, Int}}, C) + @testintersect(A, Tuple{Tuple{Int, Vararg{Any, N}} where {N}, NTuple{4, Int}}, C) + @testintersect(A, Tuple{Tuple{Int, Vararg{Any, N}}, NTuple{4, Int}} where {N}, C) + + Bs = (Tuple{Tuple{Int, Vararg{Any}}, Tuple{Int, Int, Vararg{Any}}}, + Tuple{Tuple{Int, Vararg{Any,N1}}, Tuple{Int, Int, Vararg{Any,N2}}} where {N1,N2}, + Tuple{Tuple{Int, Vararg{Any,N}} where {N}, Tuple{Int, Int, Vararg{Any,N}} where {N}}) + Cerr = Tuple{Tuple{Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N} + for B in Bs + C = typeintersect(A, B) + @test C == typeintersect(B, A) != Union{} + @test C != Cerr + # TODO: The ideal result is Tuple{Tuple{Int, Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N} + @test_broken C != Tuple{Tuple{Int, Vararg{Int}}, Tuple{Int, Int, Vararg{Int}}} + end +end + +let A = Pair{NTuple{N, Int}, NTuple{N, Int}} where N, + C = Pair{NTuple{4, Int}, NTuple{4, Int}} + @testintersect(A, Pair{<:Tuple{Int, Vararg{Any}}, NTuple{4, Int}}, C) + @testintersect(A, Pair{<:Tuple{Int, Vararg{Any, N}} where {N}, NTuple{4, Int}}, C) + @testintersect(A, Pair{<:Tuple{Int, Vararg{Any, N}}, NTuple{4, Int}} where {N}, C) + + Bs = (Pair{<:Tuple{Int, Vararg{Int}}, <:Tuple{Int, Int, Vararg{Int}}}, + Pair{Tuple{Int, Vararg{Int,N1}}, Tuple{Int, Int, Vararg{Int,N2}}} where {N1,N2}, + Pair{<:Tuple{Int, Vararg{Int,N}} where {N}, <:Tuple{Int, Int, Vararg{Int,N}} where {N}}) + Cerr = Pair{Tuple{Int, Vararg{Int, N}}, Tuple{Int, Int, Vararg{Int, N}}} where {N} + for B in Bs + C = typeintersect(A, B) + @test C == typeintersect(B, A) != Union{} + @test C != Cerr + @test_broken C != B + end +end + +# Example from pr#39098 +@testintersect(NTuple, Tuple{Any,Vararg}, Tuple{T, Vararg{T}} where {T})