diff --git a/src/subtype.c b/src/subtype.c index c01ee54e683be..0ad491cab15ce 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, @@ -2165,13 +2169,24 @@ 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 (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; + // Here we always return the shorter `Vararg`'s length. + if (offset > 0) + return jl_box_long(iv); + } + else + return jl_bottom_type; } 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; + // 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 +2201,17 @@ 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); + } + if (bb->offset > 0) { + bb->intvalued = 2; + return NULL; } return (jl_value_t*)tv; } @@ -2280,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); @@ -2334,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(); @@ -2443,6 +2489,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); @@ -2635,7 +2685,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; @@ -2670,7 +2720,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 +2738,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) { + if (xb->intvalued == 0) 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) { + if (yb->intvalued == 0) 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 +2766,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 +2806,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 +2822,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)) { @@ -2877,6 +2927,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) { @@ -2894,13 +2949,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(); @@ -3035,19 +3087,24 @@ 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); } JL_GC_POP(); + // Here we always return the shorter `Vararg`'s length. + if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) { + if (yy) yy->intvalued = 2; + return x; + } return y; } record_var_occurrence(xx, e, param); diff --git a/test/subtype.jl b/test/subtype.jl index b13dcdfa7a83a..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) @@ -2020,3 +2025,112 @@ 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 + +#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})