diff --git a/NEWS.md b/NEWS.md index d4f46aa40f6c7..17ffa4b858168 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,9 @@ New language features Language changes ---------------- +* Converting arbitrary tuples to `NTuple`, e.g. `convert(NTuple, (1, ""))` now gives an error, + where it used to be incorrectly allowed. This is because `NTuple` refers only to homogeneous + tuples (this meaning has not changed) ([#34272]). Multi-threading changes ----------------------- diff --git a/base/essentials.jl b/base/essentials.jl index ec4e562f2fe4f..9baf47566b2b8 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -333,9 +333,7 @@ convert(T::Type{Tuple{Vararg{V}}}, x::Tuple) where {V} = #convert(_::Type{Tuple{Vararg{S, N}}}, # x::Tuple{Vararg{Any, N}}) where # {S, N} = cnvt_all(S, x...) -# TODO: These currently can't be used since -# Type{NTuple} <: (Type{Tuple{Vararg{S}}} where S) is true -# even though the value S doesn't exist +# TODO: These are similar to the methods we currently use but cnvt_all might work better: #convert(_::Type{Tuple{Vararg{S}}}, # x::Tuple{Any, Vararg{Any}}) where # {S} = cnvt_all(S, x...) diff --git a/doc/src/devdocs/types.md b/doc/src/devdocs/types.md index d18ddad7e7f00..739ee0567f82f 100644 --- a/doc/src/devdocs/types.md +++ b/doc/src/devdocs/types.md @@ -403,17 +403,38 @@ f(nothing, 2.0) These examples are telling us something: when `x` is `nothing::Nothing`, there are no extra constraints on `y`. It is as if the method signature had `y::Any`. -This means that whether a variable is diagonal is not a static property based on -where it appears in a type. -Rather, it depends on where a variable appears when the subtyping algorithm *uses* it. -When `x` has type `Nothing`, we don't need to use the `T` in `Union{Nothing,T}`, so `T` -does not "occur". Indeed, we have the following type equivalence: ```julia (Tuple{Union{Nothing,T},T} where T) == Union{Tuple{Nothing,Any}, Tuple{T,T} where T} ``` +The general rule is: a concrete variable in covariant position acts like it's +not concrete if the subtyping algorithm only *uses* it once. +When `x` has type `Nothing`, we don't need to use the `T` in `Union{Nothing,T}`; +we only use it in the second slot. +This arises naturally from the observation that in `Tuple{T} where T` restricting +`T` to concrete types makes no difference; the type is equal to `Tuple{Any}` either way. + +However, appearing in *invariant* position disqualifies a variable from being concrete +whether that appearance of the variable is used or not. +Otherwise types can behave differently depending on which other types +they are compared to, making subtyping not transitive. For example, consider + +Tuple{Int,Int8,Vector{Integer}} <: Tuple{T,T,Vector{Union{Integer,T}}} where T + +If the `T` inside the Union is ignored, then `T` is concrete and the answer is "false" +since the first two types aren't the same. +But consider instead + +Tuple{Int,Int8,Vector{Any}} <: Tuple{T,T,Vector{Union{Integer,T}}} where T + +Now we cannot ignore the `T` in the Union (we must have T == Any), so `T` is not +concrete and the answer is "true". +That would make the concreteness of `T` depend on the other type, which is not +acceptable since a type must have a clear meaning on its own. +Therefore the appearance of `T` inside `Vector` is considered in both cases. + ## Subtyping diagonal variables The subtyping algorithm for diagonal variables has two components: diff --git a/src/subtype.c b/src/subtype.c index f251f7839f309..ddc269a1115b1 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -57,10 +57,6 @@ typedef struct jl_varbinding_t { jl_value_t *lb; jl_value_t *ub; int8_t right; // whether this variable came from the right side of `A <: B` - // if another var that this one depends on is found to be concrete, store it - // here for reference in case that var is popped from the environment before this one. - // TODO: generalize this to multiple variables - jl_tvar_t *concretevar; 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 @@ -516,7 +512,7 @@ static int subtype_ccheck(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) return sub; } -static int subtype_left_var(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) +static int subtype_left_var(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) { if (x == y) return 1; @@ -528,7 +524,7 @@ static int subtype_left_var(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) return 1; if (x == (jl_value_t*)jl_any_type && jl_is_datatype(y)) return 0; - return subtype(x, y, e, 0); + return subtype(x, y, e, param); } // use the current context to record where a variable occurred, for the purpose @@ -566,10 +562,10 @@ static int var_lt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) { jl_varbinding_t *bb = lookup(e, b); if (bb == NULL) - return e->ignore_free || subtype_left_var(b->ub, a, e); + return e->ignore_free || subtype_left_var(b->ub, a, e, param); record_var_occurrence(bb, e, param); if (!bb->right) // check ∀b . b<:a - return subtype_left_var(bb->ub, a, e); + return subtype_left_var(bb->ub, a, e, param); if (bb->ub == a) return 1; if (!((bb->lb == jl_bottom_type && !jl_is_type(a) && !jl_is_typevar(a)) || subtype_ccheck(bb->lb, a, e))) @@ -590,7 +586,7 @@ static int var_lt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) if (aa && !aa->right && in_union(bb->lb, a) && bb->depth0 != aa->depth0 && var_outside(e, b, (jl_tvar_t*)a)) { // an "exists" var cannot equal a "forall" var inside it unless the forall // var has equal bounds. - return subtype_left_var(aa->ub, aa->lb, e); + return subtype_left_var(aa->ub, aa->lb, e, param); } } return 1; @@ -601,10 +597,10 @@ static int var_gt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) { jl_varbinding_t *bb = lookup(e, b); if (bb == NULL) - return e->ignore_free || subtype_left_var(a, b->lb, e); + return e->ignore_free || subtype_left_var(a, b->lb, e, param); record_var_occurrence(bb, e, param); if (!bb->right) // check ∀b . b>:a - return subtype_left_var(a, bb->lb, e); + return subtype_left_var(a, bb->lb, e, param); if (bb->lb == bb->ub) { if (jl_is_typevar(bb->lb) && !jl_is_type(a) && !jl_is_typevar(a)) return var_gt((jl_tvar_t*)bb->lb, a, e, param); @@ -618,7 +614,7 @@ static int var_gt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) if (jl_is_typevar(a)) { jl_varbinding_t *aa = lookup(e, (jl_tvar_t*)a); if (aa && !aa->right && bb->depth0 != aa->depth0 && param == 2 && var_outside(e, b, (jl_tvar_t*)a)) - return subtype_left_var(aa->ub, aa->lb, e); + return subtype_left_var(aa->ub, aa->lb, e, param); } return 1; } @@ -690,9 +686,14 @@ static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want typedef int (*tvar_callback)(void*, int8_t, jl_stenv_t *, int); +static int var_occurs_invariant(jl_value_t *v, jl_tvar_t *var, int inv) JL_NOTSAFEPOINT +{ + return var_occurs_inside(v, var, 0, 1); +} + static int with_tvar(tvar_callback callback, void *context, jl_unionall_t *u, int8_t R, jl_stenv_t *e, int param) { - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, NULL, 0, 0, 0, 0, + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, R ? e->Rinvdepth : e->invdepth, 0, NULL, 0, e->vars }; JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); e->vars = &vb; @@ -738,7 +739,7 @@ static int with_tvar(tvar_callback callback, void *context, jl_unionall_t *u, in // ( Tuple{Int, Int} <: Tuple{T, T} where T) but // !( Tuple{Int, String} <: Tuple{T, T} where T) // Then check concreteness by checking that the lower bound is not an abstract type. - int diagonal = !vb.occurs_inv && vb.occurs_cov > 1; + int diagonal = vb.occurs_cov > 1 && !var_occurs_invariant(u->body, u->var, 0); if (ans && (vb.concrete || (diagonal && is_leaf_typevar(u->var)))) { if (vb.concrete && !diagonal && !is_leaf_bound(vb.ub)) { // a non-diagonal var can only be a subtype of a diagonal var if its @@ -750,24 +751,10 @@ static int with_tvar(tvar_callback callback, void *context, jl_unionall_t *u, in jl_varbinding_t *vlb = lookup(e, v); if (vlb) vlb->concrete = 1; - //else // TODO handle multiple variables in vb.concretevar - // ans = (v == vb.concretevar); } else if (!is_leaf_bound(vb.lb)) { ans = 0; } - if (ans) { - // if we occur as another var's lower bound, record the fact that we - // were concrete so that subtype can return true for that var. - /* - btemp = vb.prev; - while (btemp != NULL) { - if (btemp->lb == (jl_value_t*)u->var) - btemp->concretevar = u->var; - btemp = btemp->prev; - } - */ - } } e->vars = vb.prev; @@ -948,7 +935,7 @@ static int subtype_tuple_varargs(struct subtype_tuple_env *env, jl_stenv_t *e, i // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to // simulate the possibility of multiple arguments, which is needed // to implement the diagonal rule correctly. - if (!subtype(xp0, yp0, e, 1)) return 0; + if (!subtype(xp0, yp0, e, param)) return 0; if (!subtype(xp0, yp0, e, 1)) return 0; constrain_length: @@ -1176,7 +1163,7 @@ static int subtype_naked_vararg(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to // simulate the possibility of multiple arguments, which is needed // to implement the diagonal rule correctly. - if (!subtype(xp1, yp1, e, 1)) return 0; + if (!subtype(xp1, yp1, e, param)) return 0; if (!subtype(xp1, yp1, e, 1)) return 0; e->invdepth++; e->Rinvdepth++; @@ -1518,12 +1505,39 @@ static int concrete_min(jl_value_t *t) return 2; // up to infinite } +static jl_value_t *find_var_body(jl_value_t *t, jl_tvar_t *v) +{ + if (jl_is_unionall(t)) { + if (((jl_unionall_t*)t)->var == v) + return ((jl_unionall_t*)t)->body; + jl_value_t *b = find_var_body(((jl_unionall_t*)t)->var->lb, v); + if (b) return b; + b = find_var_body(((jl_unionall_t*)t)->var->ub, v); + if (b) return b; + return find_var_body(((jl_unionall_t*)t)->body, v); + } + else if (jl_is_uniontype(t)) { + jl_value_t *b = find_var_body(((jl_uniontype_t*)t)->a, v); + if (b) return b; + return find_var_body(((jl_uniontype_t*)t)->b, v); + } + else if (jl_is_datatype(t)) { + size_t i; + for (i=0; i < jl_nparams(t); i++) { + jl_value_t *b = find_var_body(jl_tparam(t, i), v); + if (b) + return b; + } + } + return NULL; +} + // quickly compute if x seems like a possible subtype of y // especially optimized for x isa concrete type // returns true if it could be easily determined, with the result in subtype // the approximation widens typevar bounds under the assumption they are bound // in the immediate caller--the caller must be conservative in handling the result -JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) +static int obvious_subtype(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *subtype) { if (x == y || y == (jl_value_t*)jl_any_type) { *subtype = 1; @@ -1544,14 +1558,14 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) if (jl_is_typevar(x)) { if (((jl_tvar_t*)x)->lb != (jl_value_t*)jl_bottom_type) return 0; - if (jl_obvious_subtype(((jl_tvar_t*)x)->ub, y, subtype) && *subtype) + if (obvious_subtype(((jl_tvar_t*)x)->ub, y, y0, subtype) && *subtype) return 1; return 0; } if (jl_is_typevar(y)) { if (((jl_tvar_t*)y)->lb != (jl_value_t*)jl_bottom_type) return 0; - if (jl_obvious_subtype(x, ((jl_tvar_t*)y)->ub, subtype) && !*subtype) + if (obvious_subtype(x, ((jl_tvar_t*)y)->ub, y0, subtype) && !*subtype) return 1; return 0; } @@ -1569,30 +1583,30 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) } if (jl_is_uniontype(x)) { // TODO: consider handling more LHS unions, being wary of covariance - if (jl_obvious_subtype(((jl_uniontype_t*)x)->a, y, subtype) && *subtype) { - if (jl_obvious_subtype(((jl_uniontype_t*)x)->b, y, subtype) && *subtype) + if (obvious_subtype(((jl_uniontype_t*)x)->a, y, y0, subtype) && *subtype) { + if (obvious_subtype(((jl_uniontype_t*)x)->b, y, y0, subtype) && *subtype) return 1; } - //if (jl_obvious_subtype(((jl_uniontype_t*)x)->a, y, subtype)) { + //if (obvious_subtype(((jl_uniontype_t*)x)->a, y, y0, subtype)) { // if (!*subtype) // return 1; - // if (jl_obvious_subtype(((jl_uniontype_t*)x)->b, y, subtype)) + // if (obvious_subtype(((jl_uniontype_t*)x)->b, y, y0, subtype)) // return 1; //} - //else if (jl_obvious_subtype(((jl_uniontype_t*)x)->b, y, subtype)) { + //else if (obvious_subtype(((jl_uniontype_t*)x)->b, y, y0, subtype)) { // if (!*subtype) // return 1; //} return 0; } if (jl_is_uniontype(y)) { - if (jl_obvious_subtype(x, ((jl_uniontype_t*)y)->a, subtype)) { + if (obvious_subtype(x, ((jl_uniontype_t*)y)->a, y0, subtype)) { if (*subtype) return 1; - if (jl_obvious_subtype(x, ((jl_uniontype_t*)y)->b, subtype)) + if (obvious_subtype(x, ((jl_uniontype_t*)y)->b, y0, subtype)) return 1; } - else if (jl_obvious_subtype(x, ((jl_uniontype_t*)y)->b, subtype)) { + else if (obvious_subtype(x, ((jl_uniontype_t*)y)->b, y0, subtype)) { if (*subtype) return 1; } @@ -1627,13 +1641,13 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) return 1; } } - if (jl_obvious_subtype((jl_value_t*)temp, y, subtype) && *subtype) + if (obvious_subtype((jl_value_t*)temp, y, y0, subtype) && *subtype) return 1; return 0; } if (!iscov && !((jl_datatype_t*)x)->hasfreetypevars) { // by transitivity, if `wrapper <: y`, then `x <: y` if x is a leaf type of its name - if (jl_obvious_subtype(((jl_datatype_t*)x)->name->wrapper, y, subtype) && *subtype) + if (obvious_subtype(((jl_datatype_t*)x)->name->wrapper, y, y0, subtype) && *subtype) return 1; } int i, npx = jl_nparams(x), npy = jl_nparams(y); @@ -1689,7 +1703,7 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) jl_value_t *a = i >= (npx - (vx == JL_VARARG_NONE ? 0 : 1)) ? vxt : jl_tparam(x, i); jl_value_t *b = jl_tparam(y, i); if (iscov || jl_is_typevar(b)) { - if (jl_obvious_subtype(a, b, subtype)) { + if (obvious_subtype(a, b, y0, subtype)) { if (!*subtype) return 1; if (jl_has_free_typevars(b)) // b is actually more constrained that this @@ -1701,7 +1715,7 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) } else { if (!obviously_egal(a, b)) { - if (jl_obvious_subtype(a, b, subtype)) { + if (obvious_subtype(a, b, y0, subtype)) { if (!*subtype) return 1; if (jl_has_free_typevars(b)) // b is actually more constrained that this @@ -1710,7 +1724,7 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) else { uncertain = 1; } - if (!jl_has_free_typevars(b) && jl_obvious_subtype(b, a, subtype)) { + if (!jl_has_free_typevars(b) && obvious_subtype(b, a, y0, subtype)) { if (!*subtype) return 1; if (jl_has_free_typevars(a)) // a is actually more constrained that this @@ -1727,8 +1741,13 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) assert(vy != JL_VARARG_NONE && istuple && iscov); jl_value_t *a1 = (vx != JL_VARARG_NONE && i >= npx - 1) ? vxt : jl_tparam(x, i); jl_value_t *b = jl_unwrap_vararg(jl_tparam(y, i)); - if (jl_is_typevar(b) && var_occurs_inside(y, (jl_tvar_t*)b, 0, 1)) - return 0; + if (jl_is_typevar(b)) { + jl_value_t *body = find_var_body(y0, (jl_tvar_t*)b); + if (body == NULL) + body = y0; + if (var_occurs_invariant(body, (jl_tvar_t*)b, 0)) + return 0; + } if (nparams_expanded_x > npy && jl_is_typevar(b) && concrete_min(a1) > 1) { // diagonal rule for 2 or more elements: they must all be concrete on the LHS *subtype = 0; @@ -1747,7 +1766,7 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) a2 = jl_typeof(jl_tparam0(a)); } if (!obviously_egal(a1, a2)) { - if (jl_obvious_subtype(a2, a1, subtype)) { + if (obvious_subtype(a2, a1, y0, subtype)) { if (!*subtype) return 1; if (jl_has_free_typevars(a1)) // a1 is actually more constrained that this @@ -1756,7 +1775,7 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) else { uncertain = 1; } - if (jl_obvious_subtype(a1, a2, subtype)) { + if (obvious_subtype(a1, a2, y0, subtype)) { if (!*subtype) return 1; if (jl_has_free_typevars(a2)) // a2 is actually more constrained that this @@ -1767,7 +1786,7 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) } } } - if (jl_obvious_subtype(a, b, subtype)) { + if (obvious_subtype(a, b, y0, subtype)) { if (!*subtype) return 1; if (jl_has_free_typevars(b)) // b is actually more constrained that this @@ -1787,6 +1806,11 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) return 0; } +JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) +{ + return obvious_subtype(x, y, y, subtype); +} + // `env` is NULL if no typevar information is requested, or otherwise // points to a rooted array of length `jl_subtype_env_size(y)`. // This will be populated with the values of variables from unionall @@ -2318,9 +2342,12 @@ static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want } else if (jl_is_datatype(v)) { size_t i; - int ins = inside || !want_inv || !jl_is_tuple_type(v); + int istuple = jl_is_tuple_type(v); + int isva = jl_is_vararg_type(v); for (i=0; i < jl_nparams(v); i++) { - if (var_occurs_inside(jl_tparam(v,i), var, ins, want_inv)) + int invar = isva ? i == 1 : !istuple; + int ins_i = inside || !want_inv || invar; + if (var_occurs_inside(jl_tparam(v,i), var, ins_i, want_inv)) return 1; } } @@ -2494,7 +2521,8 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv else { res = intersect(u->body, t, e, param); } - vb->concrete |= (!vb->occurs_inv && vb->occurs_cov > 1 && is_leaf_typevar(u->var)); + vb->concrete |= (vb->occurs_cov > 1 && is_leaf_typevar(u->var) && + !var_occurs_invariant(u->body, u->var, 0)); // handle the "diagonal dispatch" rule, which says that a type var occurring more // than once, and only in covariant position, is constrained to concrete types. E.g. @@ -2532,7 +2560,7 @@ 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, NULL, 0, 0, 0, 0, + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, 0, 0, 0, 0, R ? e->Rinvdepth : e->invdepth, 0, NULL, 0, e->vars }; JL_GC_PUSH6(&res, &save2, &vb.lb, &vb.ub, &save, &vb.innervars); save_env(e, &save, &se); @@ -2967,14 +2995,6 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa jl_value_t *i2=NULL, *ii = intersect(xp1, yp1, e, 1); if (ii == jl_bottom_type) return jl_bottom_type; JL_GC_PUSH2(&ii, &i2); - if (jl_is_typevar(xp1)) { - jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)xp1); - if (xb && is_leaf_typevar(xb->var)) xb->concrete = 1; - } - if (jl_is_typevar(yp1)) { - jl_varbinding_t *yb = lookup(e, (jl_tvar_t*)yp1); - if (yb && is_leaf_typevar(yb->var)) yb->concrete = 1; - } if (jl_is_typevar(xp2)) { jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)xp2); if (xb) xb->intvalued = 1; diff --git a/test/subtype.jl b/test/subtype.jl index 093d1ad096299..1f656a5e07893 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -136,6 +136,35 @@ function test_diagonal() @test issub(Tuple{Tuple{T, T}} where T>:Int, Tuple{Tuple{T, T} where T>:Int}) @test issub(Vector{Tuple{T, T} where Number<:T<:Number}, Vector{Tuple{Number, Number}}) + + @test !issub(Type{Tuple{T,Any} where T}, Type{Tuple{T,T}} where T) + @test !issub(Type{Tuple{T,Any,T} where T}, Type{Tuple{T,T,T}} where T) + @test_broken issub(Type{Tuple{T} where T}, Type{Tuple{T}} where T) + @test_broken issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T) + @test !issub(Type{Tuple{T,T} where T}, Type{Tuple{T,T}} where T) + @test !issub(Type{Tuple{T,T,T} where T}, Type{Tuple{T,T,T}} where T) + @test isequal_type(Ref{Tuple{T, T} where Int<:T<:Int}, + Ref{Tuple{S, S}} where Int<:S<:Int) + + let A = Tuple{Int,Int8,Vector{Integer}}, + B = Tuple{T,T,Vector{T}} where T>:Integer, + C = Tuple{T,T,Vector{Union{Integer,T}}} where T + @test A <: B + @test B == C + @test A <: C + @test Tuple{Int,Int8,Vector{Any}} <: C + end + + # #26108 + @test !issub((Tuple{T, T, Array{T, 1}} where T), Tuple{T, T, Any} where T) + + # #26716 + @test !issub((Union{Tuple{Int,Bool}, Tuple{P,Bool}} where P), Tuple{Union{T,Int}, T} where T) + + @test issub_strict(Tuple{Ref{Tuple{N,N}}, Ref{N}} where N, + Tuple{Ref{Tuple{N1,N1}}, Ref{N2}} where {N1, N2}) + @test !issub(Tuple{Type{Tuple{Vararg{T}} where T <: Integer}, Tuple{Float64, Int}}, + Tuple{Type{Tuple{Vararg{T}}}, Tuple{Vararg{T}}} where T) end # level 3: UnionAll @@ -881,7 +910,9 @@ function test_intersection() # since TypeofBottom is a singleton we can deduce its intersection with Type{...} @testintersect(Core.TypeofBottom, (Type{T} where T<:Tuple), Type{Union{}}) - @testintersect((Type{Tuple{Vararg{T}}} where T), Type{Tuple}, Bottom) + # since this T is inside the invariant ctor Type{}, we allow T == Any here + @testintersect((Type{Tuple{Vararg{T}}} where T), Type{Tuple}, Type{Tuple}) + @testintersect(Tuple{Type{S}, Tuple{Any, Vararg{Any}}} where S<:Tuple{Any, Vararg{Any}}, Tuple{Type{T}, T} where T, Tuple{Type{S},S} where S<:Tuple{Any,Vararg{Any,N} where N}) @@ -1005,6 +1036,13 @@ function test_intersection() @testintersect(Type{<:Array}, Type{AbstractArray{T}} where T, Bottom) + + @testintersect(Tuple{Type{Tuple{Vararg{Integer}}}, Tuple}, + Tuple{Type{Tuple{Vararg{V}}}, Tuple{Vararg{V}}} where {V}, + Tuple{Type{Tuple{Vararg{Integer,N} where N}},Tuple{Vararg{Integer,N} where N}}) + @testintersect(Tuple{Type{Tuple{Vararg{Union{Int,Symbol}}}}, Tuple}, + Tuple{Type{Tuple{Vararg{V}}}, Tuple{Vararg{V}}} where {V}, + Tuple{Type{Tuple{Vararg{Union{Int,Symbol},N} where N}},Tuple{Vararg{Union{Int,Symbol},N} where N}}) end function test_intersection_properties() @@ -1475,12 +1513,19 @@ let U = Tuple{Union{LT, LT1},Union{R, R1},Int} where LT1<:R1 where R1<:Tuple{Int end # issue #31082 and #30741 -@testintersect(Tuple{T, Ref{T}, T} where T, - Tuple{Ref{S}, S, S} where S, - Union{}) +@test typeintersect(Tuple{T, Ref{T}, T} where T, + Tuple{Ref{S}, S, S} where S) != Union{} @testintersect(Tuple{Pair{B,C},Union{C,Pair{B,C}},Union{B,Real}} where {B,C}, Tuple{Pair{B,C},C,C} where {B,C}, Tuple{Pair{B,C},C,C} where C<:Union{Real, B} where B) +f31082(::Pair{B, C}, ::Union{C, Pair{B, C}}, ::Union{B, Real}) where {B, C} = 0 +f31082(::Pair{B, C}, ::C, ::C) where {B, C} = 1 +@test f31082(""=>1, 2, 3) == 1 +@test f31082(""=>1, 2, "") == 0 +@test f31082(""=>1, 2, 3.0) == 0 +@test f31082(Pair{Any,Any}(1,2), 1, 2) == 1 +@test f31082(Pair{Any,Any}(1,2), Pair{Any,Any}(1,2), 2) == 1 +@test f31082(Pair{Any,Any}(1,2), 1=>2, 2.0) == 1 # issue #31115 @testintersect(Tuple{Ref{Z} where Z<:(Ref{Y} where Y<:Tuple{<:B}), Int} where B, diff --git a/test/tuple.jl b/test/tuple.jl index 2d1e525302a4d..06658819cb9c2 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -28,11 +28,10 @@ end @test convert(Tuple{Int, Int, Float64}, (1, 2, 3)) === (1, 2, 3.0) @test convert(Tuple{Float64, Int, UInt8}, (1.0, 2, 0x3)) === (1.0, 2, 0x3) - @test convert(NTuple, (1.0, 2, 0x3)) === (1.0, 2, 0x3) + @test convert(Tuple{Vararg{Real}}, (1.0, 2, 0x3)) === (1.0, 2, 0x3) + @test convert(Tuple{Vararg{Integer}}, (1.0, 2, 0x3)) === (1, 2, 0x3) @test convert(Tuple{Vararg{Int}}, (1.0, 2, 0x3)) === (1, 2, 3) @test convert(Tuple{Int, Vararg{Int}}, (1.0, 2, 0x3)) === (1, 2, 3) - @test convert(Tuple{Vararg{T}} where T<:Integer, (1.0, 2, 0x3)) === (1, 2, 0x3) - @test convert(Tuple{T, Vararg{T}} where T<:Integer, (1.0, 2, 0x3)) === (1, 2, 0x3) @test convert(NTuple{3, Int}, (1.0, 2, 0x3)) === (1, 2, 3) @test convert(Tuple{Int, Int, Float64}, (1.0, 2, 0x3)) === (1, 2, 3.0) @@ -53,6 +52,19 @@ end @test_throws MethodError convert(Tuple{Int, Int, Int}, (1, 2)) # issue #26589 @test_throws MethodError convert(NTuple{4}, (1.0,2.0,3.0,4.0,5.0)) + # issue #31824 + # there is no generic way to convert an arbitrary tuple to a homogeneous tuple + @test_throws MethodError(convert, (NTuple, (1, 1.0)), Base.get_world_counter()) convert(NTuple, (1, 1.0)) + let T = Tuple{Vararg{T}} where T<:Integer, v = (1.0, 2, 0x3) + @test_throws MethodError(convert, (T, (1.0, 2, 0x3)), Base.get_world_counter()) convert(T, v) + end + let T = Tuple{T, Vararg{T}} where T<:Integer, v = (1.0, 2, 0x3) + @test_throws MethodError(convert, (Tuple{Vararg{T}} where T<:Integer, (2, 0x3)), Base.get_world_counter()) convert(T, v) + end + function f31824(input...) + b::NTuple = input + end + @test f31824(1,2,3) === (1,2,3) # PR #15516 @test Tuple{Char,Char}("za") === ('z','a')