From e62b6e3137c577834f408bae7eab6d4c3a0f050c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 22 Mar 2023 10:07:05 -0400 Subject: [PATCH] make Tuple{Union{}} unconstructable Type intersection assumed it was equal to Union{}, so this makes it unconstructable so that holds true. This is similar to what the NamedTuple constructor does. Secondarily, this fixes an inference bug where it would create Vararg{Union{}} and then incorrectly handle that fieldtype. - Fixes #32392 - Addresses part of the concerns discussed in https://github.com/JuliaLang/julia/issues/24614#issuecomment-349679271 - Addresses part of the issues presented in https://github.com/JuliaLang/julia/issues/26175 - May allow improving jl_type_equality_is_identity (https://github.com/JuliaLang/julia/pull/49017/files#diff-882927c6e612596e22406ae0d06adcee88a9ec05e8b61ad81b48942e2cb266e9R986) - May allow improving intersection (finish_unionall can be more aggressive at computing varval for any typevars that appears in covariant position and has lb=Union{} and ub=leaf type) --- base/compiler/abstractinterpretation.jl | 25 +++++++-- base/compiler/inferenceresult.jl | 4 +- base/compiler/ssair/inlining.jl | 2 +- base/compiler/tfuncs.jl | 10 ++++ base/compiler/typeutils.jl | 1 + base/iterators.jl | 14 +++-- src/builtins.c | 2 +- src/codegen.cpp | 16 +++--- src/gf.c | 6 +- src/intrinsics.cpp | 2 +- src/jl_exported_funcs.inc | 1 - src/jltypes.c | 50 ++++++++++++----- src/julia.h | 5 +- src/method.c | 74 +++++++++++++------------ src/opaque_closure.c | 3 +- src/precompile.c | 2 +- src/precompile_utils.c | 2 +- src/runtime_intrinsics.c | 2 +- src/subtype.c | 10 ++-- stdlib/REPL/src/REPLCompletions.jl | 8 ++- test/compiler/codegen.jl | 7 +-- test/compiler/contextual.jl | 21 +++---- test/compiler/effects.jl | 4 +- test/compiler/inference.jl | 24 ++++++-- test/reflection.jl | 1 - test/subtype.jl | 21 +++++-- test/tuple.jl | 8 +++ 27 files changed, 205 insertions(+), 120 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 4980f051d1459..7ec75be26a182 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -546,6 +546,8 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp add_remark!(interp, sv, "Refusing to infer into `depwarn`") return MethodCallResult(Any, false, false, nothing, Effects()) end + sigtuple = unwrap_unionall(sig) + sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects()) # Limit argument type tuple growth of functions: # look through the parents list to see if there's a call to the same method @@ -581,7 +583,6 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp washardlimit = hardlimit if topmost !== nothing - sigtuple = unwrap_unionall(sig)::DataType msig = unwrap_unionall(method.sig)::DataType spec_len = length(msig.parameters) + 1 ls = length(sigtuple.parameters) @@ -1376,7 +1377,11 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) va = isvarargtype(last) elts = Any[ fieldtype(tti0, i) for i = 1:len ] if va - elts[len] = Vararg{elts[len]} + if elts[len] === Union{} + pop!(elts) + else + elts[len] = Vararg{elts[len]} + end end return AbstractIterationResult(elts, nothing) end @@ -1385,6 +1390,9 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) elseif tti0 === Any return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) elseif tti0 <: Array + if eltype(tti0) === Union{} + return AbstractIterationResult(Any[], nothing) + end return AbstractIterationResult(Any[Vararg{eltype(tti0)}], nothing) else return abstract_iteration(interp, itft, typ, sv) @@ -2098,7 +2106,7 @@ end function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) isref = false - if T === Bottom + if unwrapva(T) === Bottom return Bottom elseif isa(T, Type) if isa(T, DataType) && (T::DataType).name === _REF_NAME @@ -2135,8 +2143,13 @@ end function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::VarTable, sv::InferenceState) f = abstract_eval_value(interp, e.args[2], vtypes, sv) # rt = sp_type_rewrap(e.args[3], sv.linfo, true) - at = Any[ sp_type_rewrap(argt, sv.linfo, false) for argt in e.args[4]::SimpleVector ] - pushfirst!(at, f) + atv = e.args[4]::SimpleVector + at = Vector{Any}(undef, length(atv) + 1) + at[1] = f + for i = 1:length(atv) + at[i + 1] = sp_type_rewrap(at[i], sv.linfo, false) + at[i + 1] === Bottom && return + end # this may be the wrong world for the call, # but some of the result is likely to be valid anyways # and that may help generate better codegen @@ -2339,7 +2352,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp let t = t, at = at; all(i::Int->getfield(at.val::Tuple, i) isa fieldtype(t, i), 1:n); end nothrow = isexact t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val)) - elseif isa(at, PartialStruct) && at ⊑ᵢ Tuple && n == length(at.fields::Vector{Any}) && + elseif isa(at, PartialStruct) && at ⊑ᵢ Tuple && n > 0 && n == length(at.fields::Vector{Any}) && !isvarargtype(at.fields[end]) && let t = t, at = at; all(i::Int->(at.fields::Vector{Any})[i] ⊑ᵢ fieldtype(t, i), 1:n); end nothrow = isexact t = PartialStruct(t, at.fields::Vector{Any}) diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl index c079553fca06a..85f543943384d 100644 --- a/base/compiler/inferenceresult.jl +++ b/base/compiler/inferenceresult.jl @@ -119,9 +119,9 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe # to the appropriate `Tuple` type or `PartialStruct` instance. if !toplevel && isva if specTypes::Type == Tuple + linfo_argtypes = Any[Any for i = 1:nargs] if nargs > 1 - linfo_argtypes = Any[Any for i = 1:nargs] - linfo_argtypes[end] = Vararg{Any} + linfo_argtypes[end] = Tuple end vargtype = Tuple else diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index b91652e478636..3b1cb2c46ce6e 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1170,7 +1170,7 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}}, if isa(result, ConstPropResult) mi = result.result.linfo validate_sparams(mi.sparam_vals) || return nothing - if argtypes_to_type(argtypes) <: mi.def.sig + if Union{} !== argtypes_to_type(argtypes) <: mi.def.sig item = resolve_todo(mi, result.result, argtypes, info, flag, state; invokesig) handle_single_case!(todo, ir, idx, stmt, item, OptimizationParams(state.interp), true) return nothing diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index b23d1b17efd42..1f2d1a2f38466 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1872,7 +1872,15 @@ add_tfunc(apply_type, 1, INT_INF, apply_type_tfunc, 10) # convert the dispatch tuple type argtype to the real (concrete) type of # the tuple of those values function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any}) + isempty(argtypes) && return Const(()) argtypes = anymap(widenslotwrapper, argtypes) + if isvarargtype(argtypes[end]) && unwrapva(argtypes[end]) === Union{} + # Drop the Vararg in Tuple{...,Vararg{Union{}}} since it must be length 0. + # If there is a Vararg num also, it must be a TypeVar, and it must be + # zero, but that generally shouldn't show up here, since it implies a + # UnionAll context is missing around this. + pop!(argtypes) + end all_are_const = true for i in 1:length(argtypes) if !isa(argtypes[i], Const) @@ -1915,6 +1923,8 @@ function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any}) params[i] = x elseif !isvarargtype(x) && hasintersect(x, Type) params[i] = Union{x, Type} + elseif x === Union{} + return Bottom # argtypes is malformed, but try not to crash else params[i] = x end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 293ef5797888b..dbaf677558f0c 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -187,6 +187,7 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I bp = b.parameters[i] (isvarargtype(ap) || isvarargtype(bp)) && return a ta[i] = typesubtract(ap, bp, min(2, max_union_splitting)) + ta[i] === Union{} && return Union{} return Tuple{ta...} end end diff --git a/base/iterators.jl b/base/iterators.jl index f2a9f23c9d094..d97338aeee6cd 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -41,6 +41,12 @@ if Base !== Core.Compiler export partition end +function TupleOrBottom(tt...) + any(p -> p === Union{}, tt) && return Union{} + return Tuple{tt...} +end + + """ Iterators.map(f, iterators...) @@ -209,7 +215,7 @@ size(e::Enumerate) = size(e.itr) end last(e::Enumerate) = (length(e.itr), e.itr[end]) -eltype(::Type{Enumerate{I}}) where {I} = Tuple{Int, eltype(I)} +eltype(::Type{Enumerate{I}}) where {I} = TupleOrBottom(Int, eltype(I)) IteratorSize(::Type{Enumerate{I}}) where {I} = IteratorSize(I) IteratorEltype(::Type{Enumerate{I}}) where {I} = IteratorEltype(I) @@ -394,7 +400,7 @@ _promote_tuple_shape((m,)::Tuple{Integer}, (n,)::Tuple{Integer}) = (min(m, n),) _promote_tuple_shape(a, b) = promote_shape(a, b) _promote_tuple_shape(a, b...) = _promote_tuple_shape(a, _promote_tuple_shape(b...)) _promote_tuple_shape(a) = a -eltype(::Type{Zip{Is}}) where {Is<:Tuple} = Tuple{map(eltype, fieldtypes(Is))...} +eltype(::Type{Zip{Is}}) where {Is<:Tuple} = TupleOrBottom(map(eltype, fieldtypes(Is))...) #eltype(::Type{Zip{Tuple{}}}) = Tuple{} #eltype(::Type{Zip{Tuple{A}}}) where {A} = Tuple{eltype(A)} #eltype(::Type{Zip{Tuple{A, B}}}) where {A, B} = Tuple{eltype(A), eltype(B)} @@ -1072,8 +1078,7 @@ end eltype(::Type{ProductIterator{I}}) where {I} = _prod_eltype(I) _prod_eltype(::Type{Tuple{}}) = Tuple{} -_prod_eltype(::Type{I}) where {I<:Tuple} = - Tuple{ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...} +_prod_eltype(::Type{I}) where {I<:Tuple} = TupleOrBottom(ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...) iterate(::ProductIterator{Tuple{}}) = (), true iterate(::ProductIterator{Tuple{}}, state) = nothing @@ -1442,6 +1447,7 @@ end function _approx_iter_type(itrT::Type, vstate::Type) vstate <: Union{Nothing, Tuple{Any, Any}} || return Any vstate <: Union{} && return Union{} + itrT <: Union{} && return Union{} nextvstate = Base._return_type(doiterate, Tuple{itrT, vstate}) return (nextvstate <: vstate ? vstate : Any) end diff --git a/src/builtins.c b/src/builtins.c index 471b06e559dc5..a8efa2ef44d13 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1318,7 +1318,7 @@ JL_CALLABLE(jl_f_apply_type) jl_type_error_rt("Tuple", "parameter", (jl_value_t*)jl_type_type, pi); } } - return (jl_value_t*)jl_apply_tuple_type_v(&args[1], nargs-1); + return jl_apply_tuple_type_v(&args[1], nargs-1); } else if (args[0] == (jl_value_t*)jl_uniontype_type) { // Union{} has extra restrictions, so it needs to be checked after diff --git a/src/codegen.cpp b/src/codegen.cpp index b7d1bae7411a6..7cf108167d755 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5146,7 +5146,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met for (size_t i = 0; i < jl_svec_len(argt_typ->parameters); ++i) { jl_svecset(sig_args, 1+i, jl_svecref(argt_typ->parameters, i)); } - sigtype = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); + sigtype = jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); jl_method_instance_t *mi = jl_specializations_get_linfo(closure_method, sigtype, jl_emptysvec); jl_code_instance_t *ci = (jl_code_instance_t*)jl_rettype_inferred(mi, ctx.world, ctx.world); @@ -5469,7 +5469,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ if (can_optimize) { jl_value_t *closure_t = NULL; - jl_tupletype_t *env_t = NULL; + jl_value_t *env_t = NULL; JL_GC_PUSH2(&closure_t, &env_t); jl_value_t **env_component_ts = (jl_value_t**)alloca(sizeof(jl_value_t*) * (nargs-4)); @@ -5479,10 +5479,10 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ env_t = jl_apply_tuple_type_v(env_component_ts, nargs-4); // we need to know the full env type to look up the right specialization - if (jl_is_concrete_type((jl_value_t*)env_t)) { + if (jl_is_concrete_type(env_t)) { jl_tupletype_t *argt_typ = (jl_tupletype_t*)argt.constant; Function *F, *specF; - std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, env_t, argt_typ, ub.constant); + std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, (jl_datatype_t*)env_t, argt_typ, ub.constant); if (F) { jl_cgval_t jlcall_ptr = mark_julia_type(ctx, F, false, jl_voidpointer_type); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe); @@ -5495,7 +5495,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ fptr = mark_julia_type(ctx, (llvm::Value*)Constant::getNullValue(ctx.types().T_size), false, jl_voidpointer_type); // TODO: Inline the env at the end of the opaque closure and generate a descriptor for GC - jl_cgval_t env = emit_new_struct(ctx, (jl_value_t*)env_t, nargs-4, &argv.data()[4]); + jl_cgval_t env = emit_new_struct(ctx, env_t, nargs-4, &argv.data()[4]); jl_cgval_t closure_fields[5] = { env, @@ -6441,7 +6441,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con sigt = NULL; } else { - sigt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)sigt); + sigt = jl_apply_tuple_type((jl_svec_t*)sigt); } if (sigt && !(unionall_env && jl_has_typevar_from_unionall(rt, unionall_env))) { unionall_env = NULL; @@ -6891,9 +6891,9 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) } jl_svecset(tupargs, i-nreq, argType); } - jl_datatype_t *typ = jl_apply_tuple_type(tupargs); + jl_value_t *typ = jl_apply_tuple_type(tupargs); JL_GC_POP(); - return typ; + return (jl_datatype_t*)typ; } diff --git a/src/gf.c b/src/gf.c index 0b01f5e8e6ee2..24e9171614fd0 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1227,7 +1227,7 @@ static jl_method_instance_t *cache_method( intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? definition->nargs + 1 : jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt)); jl_compilation_sig(tt, sparams, definition, nspec, &newparams); if (newparams) { - temp2 = (jl_value_t*)jl_apply_tuple_type(newparams); + temp2 = jl_apply_tuple_type(newparams); // Now there may be a problem: the widened signature is more general // than just the given arguments, so it might conflict with another // definition that does not have cache instances yet. To fix this, we @@ -1350,7 +1350,7 @@ static jl_method_instance_t *cache_method( } } if (newparams) { - simplett = jl_apply_tuple_type(newparams); + simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams); temp2 = (jl_value_t*)simplett; } @@ -2513,7 +2513,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t jl_compilation_sig(ti, env, m, nspec, &newparams); int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple; if (newparams) { - tt = jl_apply_tuple_type(newparams); + tt = (jl_datatype_t*)jl_apply_tuple_type(newparams); if (!is_compileable) { // compute new env, if used below jl_value_t *ti = jl_type_intersection_env((jl_value_t*)tt, (jl_value_t*)m->sig, &newparams); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 8aec4a4990e6f..77ff7221649ff 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -1409,7 +1409,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg jl_value_t *params[2]; params[0] = xtyp; params[1] = (jl_value_t*)jl_bool_type; - jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2); + jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); *newtyp = tuptyp; Value *tupval; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 863f5d5686fb7..02355d7003605 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -478,7 +478,6 @@ XX(jl_try_substrtod) \ XX(jl_try_substrtof) \ XX(jl_tty_set_mode) \ - XX(jl_tupletype_fill) \ XX(jl_typeassert) \ XX(jl_typeinf_lock_begin) \ XX(jl_typeinf_lock_end) \ diff --git a/src/jltypes.c b/src/jltypes.c index 0439c8d034e6b..a390f6f0be531 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1033,7 +1033,7 @@ static jl_value_t *inst_datatype_env(jl_value_t *dt, jl_svec_t *p, jl_value_t ** jl_value_t *jl_apply_type(jl_value_t *tc, jl_value_t **params, size_t n) { if (tc == (jl_value_t*)jl_anytuple_type) - return (jl_value_t*)jl_apply_tuple_type_v(params, n); + return jl_apply_tuple_type_v(params, n); if (tc == (jl_value_t*)jl_uniontype_type) return (jl_value_t*)jl_type_union(params, n); size_t i; @@ -1122,20 +1122,20 @@ jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *dt) } params[0] = dt; params[1] = (jl_value_t*)jl_bool_type; - jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2); + jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); JL_GC_PROMISE_ROOTED(tuptyp); // (JL_ALWAYS_LEAFTYPE) jl_datatype_t *rettyp = (jl_datatype_t*)jl_apply_type2((jl_value_t*)jl_namedtuple_type, names, (jl_value_t*)tuptyp); JL_GC_PROMISE_ROOTED(rettyp); // (JL_ALWAYS_LEAFTYPE) return rettyp; } -JL_DLLEXPORT jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v) +// used to expand an NTuple to a flat representation +static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v) { - // TODO: replace with just using NTuple jl_value_t *p = NULL; JL_GC_PUSH1(&p); p = (jl_value_t*)jl_svec_fill(n, v); - p = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)p); + p = jl_apply_tuple_type((jl_svec_t*)p); JL_GC_POP(); return p; } @@ -1519,9 +1519,31 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value int cacheable = 1; if (istuple) { size_t i; - for (i = 0; cacheable && i < ntp; i++) - if (!jl_is_concrete_type(iparams[i]) && iparams[i] != jl_bottom_type) + for (i = 0; i < ntp; i++) { + jl_value_t *pi = iparams[i]; + if (jl_is_vararg(pi) && jl_unwrap_vararg(pi) == jl_bottom_type) { + jl_value_t *va1 = jl_unwrap_vararg_num(pi); + if (va1 && jl_is_long(va1)) { + ssize_t nt = jl_unbox_long(va1); + if (nt == 0) + va1 = NULL; + else + pi = jl_bottom_type; // trigger errorf below + } + // This imposes an implicit constraint that va1==0, + // so we keep the Vararg if it has a TypeVar + if (va1 == NULL) { + p = NULL; + ntp -= 1; + assert(i == ntp); + break; + } + } + if (pi == jl_bottom_type) + jl_errorf("Tuple field type cannot be Union{}"); + if (cacheable && !jl_is_concrete_type(pi)) cacheable = 0; + } } else { size_t i; @@ -1603,7 +1625,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value l = ntp - 1 + nt; for (; i < l; i++) jl_svecset(p, i, va0); - jl_value_t *ndt = (jl_value_t*)jl_apply_tuple_type(p); + jl_value_t *ndt = jl_apply_tuple_type(p); JL_GC_POP(); return ndt; } @@ -1732,17 +1754,17 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value return (jl_value_t*)ndt; } -static jl_tupletype_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) +static jl_value_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) { - return (jl_datatype_t*)inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL); + return inst_datatype_inner(jl_anytuple_type, params, p, np, NULL, NULL); } -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type(jl_svec_t *params) +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params) { return jl_apply_tuple_type_v_(jl_svec_data(params), jl_svec_len(params), params); } -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np) +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np) { return jl_apply_tuple_type_v_(p, np, NULL); } @@ -1828,7 +1850,7 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ ssize_t nt = jl_unbox_long(N); if (nt < 0) jl_errorf("size or dimension is negative: %zd", nt); - return (jl_value_t*)jl_tupletype_fill(nt, T); + return jl_tupletype_fill(nt, T); } } jl_value_t **iparams; @@ -2293,7 +2315,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_anytuple_type->layout = NULL; jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type); - jl_emptytuple_type = jl_apply_tuple_type(jl_emptysvec); + jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec); jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type); jl_emptytuple_type->instance = jl_emptytuple; diff --git a/src/julia.h b/src/julia.h index 2186ee346418d..4dba04a61770a 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1451,8 +1451,8 @@ JL_DLLEXPORT jl_value_t *jl_apply_type1(jl_value_t *tc, jl_value_t *p1); JL_DLLEXPORT jl_value_t *jl_apply_type2(jl_value_t *tc, jl_value_t *p1, jl_value_t *p2); JL_DLLEXPORT jl_datatype_t *jl_apply_modify_type(jl_value_t *dt); JL_DLLEXPORT jl_datatype_t *jl_apply_cmpswap_type(jl_value_t *dt); -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type(jl_svec_t *params); -JL_DLLEXPORT jl_tupletype_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np); +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type(jl_svec_t *params); +JL_DLLEXPORT jl_value_t *jl_apply_tuple_type_v(jl_value_t **p, size_t np); JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *super, @@ -1486,7 +1486,6 @@ JL_DLLEXPORT jl_svec_t *jl_alloc_svec(size_t n); JL_DLLEXPORT jl_svec_t *jl_alloc_svec_uninit(size_t n); JL_DLLEXPORT jl_svec_t *jl_svec_copy(jl_svec_t *a); JL_DLLEXPORT jl_svec_t *jl_svec_fill(size_t n, jl_value_t *x); -JL_DLLEXPORT jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *v); JL_DLLEXPORT jl_sym_t *jl_symbol(const char *str) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_symbol_lookup(const char *str) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_sym_t *jl_symbol_n(const char *str, size_t len) JL_NOTSAFEPOINT; diff --git a/src/method.c b/src/method.c index ee333bebccedf..8b4c87a46ecd9 100644 --- a/src/method.c +++ b/src/method.c @@ -989,7 +989,9 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, JL_GC_PUSH3(&f, &m, &argtype); size_t i, na = jl_svec_len(atypes); - argtype = (jl_value_t*)jl_apply_tuple_type(atypes); + argtype = jl_apply_tuple_type(atypes); + if (!jl_is_datatype(argtype)) + jl_error("invalid type in method definition (Union{})"); jl_methtable_t *external_mt = mt; if (!mt) @@ -1024,49 +1026,19 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, } } - for (i = jl_svec_len(tvars); i > 0; i--) { - jl_value_t *tv = jl_svecref(tvars, i - 1); - if (!jl_is_typevar(tv)) - jl_type_error("method signature", (jl_value_t*)jl_tvar_type, tv); - if (!jl_has_typevar(argtype, (jl_tvar_t*)tv)) // deprecate this to an error in v2 - jl_printf(JL_STDERR, - "WARNING: method definition for %s at %s:%d declares type variable %s but does not use it.\n", - jl_symbol_name(name), - jl_symbol_name(file), - line, - jl_symbol_name(((jl_tvar_t*)tv)->name)); - argtype = jl_new_struct(jl_unionall_type, tv, argtype); - } - if (jl_has_free_typevars(argtype)) { - jl_exceptionf(jl_argumenterror_type, - "method definition for %s at %s:%d has free type variables", - jl_symbol_name(name), - jl_symbol_name(file), - line); - } - - if (!jl_is_code_info(f)) { // this occurs when there is a closure being added to an out-of-scope function // the user should only do this at the toplevel // the result is that the closure variables get interpolated directly into the IR f = jl_new_code_info_from_ir((jl_expr_t*)f); } - m = jl_new_method_uninit(module); - m->external_mt = (jl_value_t*)external_mt; - if (external_mt) - jl_gc_wb(m, external_mt); - m->sig = argtype; - m->name = name; - m->isva = isva; - m->nargs = nargs; - m->file = file; - m->line = line; - jl_method_set_source(m, f); for (i = 0; i < na; i++) { jl_value_t *elt = jl_svecref(atypes, i); - if (!jl_is_type(elt) && !jl_is_typevar(elt) && !jl_is_vararg(elt)) { + int isvalid = jl_is_type(elt) || jl_is_typevar(elt) || jl_is_vararg(elt); + if (elt == jl_bottom_type || (jl_is_vararg(elt) && jl_unwrap_vararg(elt) == jl_bottom_type)) + isvalid = 0; + if (!isvalid) { jl_sym_t *argname = (jl_sym_t*)jl_array_ptr_ref(f->slotnames, i); if (argname == jl_unused_sym) jl_exceptionf(jl_argumenterror_type, @@ -1090,6 +1062,38 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, jl_symbol_name(file), line); } + for (i = jl_svec_len(tvars); i > 0; i--) { + jl_value_t *tv = jl_svecref(tvars, i - 1); + if (!jl_is_typevar(tv)) + jl_type_error("method signature", (jl_value_t*)jl_tvar_type, tv); + if (!jl_has_typevar(argtype, (jl_tvar_t*)tv)) // deprecate this to an error in v2 + jl_printf(JL_STDERR, + "WARNING: method definition for %s at %s:%d declares type variable %s but does not use it.\n", + jl_symbol_name(name), + jl_symbol_name(file), + line, + jl_symbol_name(((jl_tvar_t*)tv)->name)); + argtype = jl_new_struct(jl_unionall_type, tv, argtype); + } + if (jl_has_free_typevars(argtype)) { + jl_exceptionf(jl_argumenterror_type, + "method definition for %s at %s:%d has free type variables", + jl_symbol_name(name), + jl_symbol_name(file), + line); + } + + m = jl_new_method_uninit(module); + m->external_mt = (jl_value_t*)external_mt; + if (external_mt) + jl_gc_wb(m, external_mt); + m->sig = argtype; + m->name = name; + m->isva = isva; + m->nargs = nargs; + m->file = file; + m->line = line; + jl_method_set_source(m, f); #ifdef RECORD_METHOD_ORDER if (jl_all_methods == NULL) diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 1cc7bd23b6c1a..07a17ac3e8bec 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -22,6 +22,7 @@ JL_DLLEXPORT int jl_is_valid_oc_argtype(jl_tupletype_t *argt, jl_method_t *sourc return 1; } +// TODO: merge this with jl_argtype_with_function static jl_value_t *prepend_type(jl_value_t *t0, jl_tupletype_t *t) { jl_svec_t *sig_args = NULL; @@ -32,7 +33,7 @@ static jl_value_t *prepend_type(jl_value_t *t0, jl_tupletype_t *t) for (size_t i = 0; i < nsig-1; ++i) { jl_svecset(sig_args, 1+i, jl_tparam(t, i)); } - jl_value_t *sigtype = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig); + jl_value_t *sigtype = jl_apply_tuple_type(sig_args); JL_GC_POP(); return sigtype; } diff --git a/src/precompile.c b/src/precompile.c index 75970a20237c2..4aac28ff9a790 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -99,7 +99,7 @@ JL_DLLEXPORT void jl_write_compiler_output(void) // since it's a slightly duplication of effort jl_value_t *tt = jl_is_type(f) ? (jl_value_t*)jl_wrap_Type(f) : jl_typeof(f); JL_GC_PUSH1(&tt); - tt = (jl_value_t*)jl_apply_tuple_type_v(&tt, 1); + tt = jl_apply_tuple_type_v(&tt, 1); jl_compile_hint((jl_tupletype_t*)tt); JL_GC_POP(); } diff --git a/src/precompile_utils.c b/src/precompile_utils.c index 5b0c231b04345..9e513a1cfed3a 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -120,7 +120,7 @@ static void _compile_all_union(jl_value_t *sig) jl_svecset(p, i, ty); } } - methsig = (jl_value_t*)jl_apply_tuple_type(p); + methsig = jl_apply_tuple_type(p); methsig = jl_rewrap_unionall(methsig, sig); _compile_all_tvar_union(methsig); } diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 9536ed1f02fb3..f456fc9039415 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -1035,7 +1035,7 @@ static inline jl_value_t *jl_intrinsiclambda_checked(jl_value_t *ty, void *pa, v jl_value_t *params[2]; params[0] = ty; params[1] = (jl_value_t*)jl_bool_type; - jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2); + jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2); JL_GC_PROMISE_ROOTED(tuptyp); // (JL_ALWAYS_LEAFTYPE) jl_task_t *ct = jl_current_task; jl_value_t *newv = jl_gc_alloc(ct->ptls, jl_datatype_size(tuptyp), tuptyp); diff --git a/src/subtype.c b/src/subtype.c index 8760fa94e351d..2f05b778cea19 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -3000,9 +3000,9 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten if (xi == NULL || yi == NULL) { res = jl_bottom_type; if (vx && intersect_vararg_length(xi, ly+1-lx, e, 0)) - res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), j); + res = jl_apply_tuple_type_v(jl_svec_data(params), j); if (vy && intersect_vararg_length(yi, lx+1-ly, e, 1)) - res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), i); + res = jl_apply_tuple_type_v(jl_svec_data(params), i); break; } jl_value_t *ii = NULL; @@ -3036,7 +3036,7 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten else { if (xb) set_var_to_const(xb, jl_box_long(len-lx+1), yb); if (yb) set_var_to_const(yb, jl_box_long(len-ly+1), xb); - res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), len); + res = jl_apply_tuple_type_v(jl_svec_data(params), len); } } else { @@ -3052,7 +3052,7 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten } // TODO: handle Vararg with explicit integer length parameter if (res == NULL) - res = (jl_value_t*)jl_apply_tuple_type(params); + res = jl_apply_tuple_type(params); JL_GC_POP(); return res; } @@ -3682,7 +3682,7 @@ static jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b) ts[1] = jl_tparam(b, i); jl_svecset(vec, i, jl_type_union(ts, 2)); } - jl_value_t *ans = (jl_value_t*)jl_apply_tuple_type(vec); + jl_value_t *ans = jl_apply_tuple_type(vec); JL_GC_POP(); return ans; } diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 34ce7ad9928fb..a1a033ec5d2cd 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -448,7 +448,13 @@ function get_type_call(expr::Expr, fn::Module) args = Any[] for i in 2:length(expr.args) # Find the type of the function arguments typ, found = get_type(expr.args[i], fn) - found ? push!(args, typ) : push!(args, Any) + if typ === Union{} || !found + # if we didn't find a specific type, or proved it could not exist, + # try putting Any here and let the compiler attempt to infer something + # with the other types + typ = Any + end + push!(args, typ) end world = Base.get_world_counter() return_type = Core.Compiler.return_type(Tuple{f, args...}, world) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 2279856cf141c..8a3949212ea16 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -649,7 +649,7 @@ end # issue #41157 f41157(a, b) = a[1] = b[1] -@test_throws BoundsError f41157(Tuple{Int}[], Tuple{Union{}}[]) +@test_throws BoundsError f41157(Tuple{Int}[], (NTuple{N,Union{}} where N)[]) # issue #41096 struct Modulate41096{M<:Union{Function, Val{true}, Val{false}}, id} @@ -786,11 +786,6 @@ f_isa_type(@nospecialize(x)) = isa(x, Type) f47247(a::Ref{Int}, b::Nothing) = setfield!(a, :x, b) @test_throws TypeError f47247(Ref(5), nothing) -@testset "regression in generic_bitcast: should support Union{} values" begin - f(x) = Core.bitcast(UInt64, x) - @test occursin("llvm.trap", get_llvm(f, Tuple{Union{}})) -end - f48085(@nospecialize x...) = length(x) @test Core.Compiler.get_compileable_sig(which(f48085, (Vararg{Any},)), Tuple{typeof(f48085), Vararg{Int}}, Core.svec()) === nothing @test Core.Compiler.get_compileable_sig(which(f48085, (Vararg{Any},)), Tuple{typeof(f48085), Int, Vararg{Int}}, Core.svec()) === Tuple{typeof(f48085), Any, Vararg{Any}} diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 9db7ae1aeaa5d..16332555a0c3a 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -9,7 +9,7 @@ module MiniCassette using Core.Compiler: retrieve_code_info, CodeInfo, MethodInstance, SSAValue, GotoNode, GotoIfNot, ReturnNode, SlotNumber, quoted, - signature_type + signature_type, anymap using Base: _methods_by_ftype using Base.Meta: isexpr using Test @@ -19,10 +19,11 @@ module MiniCassette struct Ctx; end # A no-op cassette-like transform - function transform_expr(expr, map_slot_number, map_ssa_value, sparams) - transform(expr) = transform_expr(expr, map_slot_number, map_ssa_value, sparams) + function transform_expr(expr, map_slot_number, map_ssa_value, sparams::Core.SimpleVector) + @nospecialize expr + transform(@nospecialize expr) = transform_expr(expr, map_slot_number, map_ssa_value, sparams) if isexpr(expr, :call) - return Expr(:call, overdub, SlotNumber(2), map(transform, expr.args)...) + return Expr(:call, overdub, SlotNumber(2), anymap(transform, expr.args)...) elseif isa(expr, GotoIfNot) return GotoIfNot(transform(expr.cond), map_ssa_value(SSAValue(expr.dest)).id) elseif isexpr(expr, :static_parameter) @@ -30,7 +31,7 @@ module MiniCassette elseif isa(expr, ReturnNode) return ReturnNode(transform(expr.val)) elseif isa(expr, Expr) - return Expr(expr.head, map(transform, expr.args)...) + return Expr(expr.head, anymap(transform, expr.args)...) elseif isa(expr, GotoNode) return GotoNode(map_ssa_value(SSAValue(expr.label)).id) elseif isa(expr, SlotNumber) @@ -42,16 +43,16 @@ module MiniCassette end end - function transform!(ci, nargs, sparams) + function transform!(ci::CodeInfo, nargs::Int, sparams::Core.SimpleVector) code = ci.code ci.slotnames = Symbol[Symbol("#self#"), :ctx, :f, :args, ci.slotnames[nargs+1:end]...] ci.slotflags = UInt8[(0x00 for i = 1:4)..., ci.slotflags[nargs+1:end]...] # Insert one SSAValue for every argument statement - prepend!(code, [Expr(:call, getfield, SlotNumber(4), i) for i = 1:nargs]) - prepend!(ci.codelocs, [0 for i = 1:nargs]) - prepend!(ci.ssaflags, [0x00 for i = 1:nargs]) + prepend!(code, Any[Expr(:call, getfield, SlotNumber(4), i) for i = 1:nargs]) + prepend!(ci.codelocs, fill(0, nargs)) + prepend!(ci.ssaflags, fill(0x00, nargs)) ci.ssavaluetypes += nargs - function map_slot_number(slot) + function map_slot_number(slot::Int) if slot == 1 # self in the original function is now `f` return SlotNumber(3) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 03f60062a9ebe..13215addca54a 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -357,9 +357,9 @@ function f_boundscheck_elim(n) # Inbounds here assumes that this is only ever called with `n==0`, but of # course the compiler has no way of knowing that, so it must not attempt # to run the `@inbounds getfield(sin, 1)` that `ntuple` generates. - ntuple(x->(@inbounds getfield(sin, x)), n) + ntuple(x->(@inbounds ()[x]), n) end -@test !Core.Compiler.is_consistent(Base.infer_effects(f_boundscheck_elim, (Int,))) +@test_broken !Core.Compiler.is_consistent(Base.infer_effects(f_boundscheck_elim, (Int,))) @test Tuple{} <: only(Base.return_types(f_boundscheck_elim, (Int,))) # Test that purity modeling doesn't accidentally introduce new world age issues diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 12fedf2792a61..b322118e1d2d5 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -682,6 +682,7 @@ end # inference of `fieldtype` mutable struct UndefField__ x::Union{} + UndefField__() = new() end f_infer_undef_field() = fieldtype(UndefField__, :x) @test Base.return_types(f_infer_undef_field, ()) == Any[Type{Union{}}] @@ -1020,7 +1021,7 @@ end g21771(T) = T f21771(::Val{U}) where {U} = Tuple{g21771(U)} @test @inferred(f21771(Val{Int}())) === Tuple{Int} -@test @inferred(f21771(Val{Union{}}())) === Tuple{Union{}} +@test_throws ErrorException @inferred(f21771(Val{Union{}}())) @test @inferred(f21771(Val{Integer}())) === Tuple{Integer} # PR #28284, check that constants propagate through calls to new @@ -4394,18 +4395,18 @@ end init = Base.ImmutableDict{Number,Number}() a = Const(init) - b = Core.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) + b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) c = Core.Compiler.tmerge(a, b) @test ⊑(a, c) && ⊑(b, c) @test c === typeof(init) - a = Core.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + a = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) c = Core.Compiler.tmerge(a, b) @test ⊑(a, c) && ⊑(b, c) @test c.fields[2] === Any # or Number @test c.fields[3] === ComplexF64 - b = Core.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) + b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) c = Core.Compiler.tmerge(a, b) @test ⊑(a, c) @test ⊑(b, c) @@ -4447,13 +4448,24 @@ end Core.Compiler.return_type(+, NTuple{2, Rational}) end == Rational -# vararg-tuple comparison within `PartialStruct` +# vararg-tuple comparison within `Compiler.PartialStruct` # https://github.com/JuliaLang/julia/issues/44965 let 𝕃ᵢ = Core.Compiler.fallback_lattice - t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Core.Const(42), Vararg{Any}]) + t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Const(42), Vararg{Any}]) @test Core.Compiler.issimplertype(𝕃ᵢ, t, t) + + t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Const(42), Vararg{Union{}}]) + @test t === Const((42,)) + t = Core.Compiler.tuple_tfunc(𝕃ᵢ, Any[Const(42), Int, Vararg{Union{}}]) + @test t.typ === Tuple{Int, Int} + @test t.fields == Any[Const(42), Int] end +foo_empty_vararg(i...) = i[2] +bar_empty_vararg(i) = foo_empty_vararg(10, 20, 30, i...) +@test bar_empty_vararg(Union{}[]) === 20 + + # check the inference convergence with an empty vartable: # the inference state for the toplevel chunk below will have an empty vartable, # and so we may fail to terminate (or optimize) it if we don't update vartables correctly diff --git a/test/reflection.jl b/test/reflection.jl index 95965bf1725a7..353b0f52abcd3 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -84,7 +84,6 @@ end # module ReflectionTest @test isconcretetype(DataType) @test isconcretetype(Union) @test !isconcretetype(Union{}) -@test isconcretetype(Tuple{Union{}}) @test !isconcretetype(Complex) @test !isconcretetype(Complex.body) @test !isconcretetype(AbstractArray{Int,1}) diff --git a/test/subtype.jl b/test/subtype.jl index e2bb49a6e0123..3ac05aea16975 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -588,7 +588,7 @@ function test_old() end const easy_menagerie = - Any[Bottom, Any, Int, Int8, Integer, Real, + Any[Any, Int, Int8, Integer, Real, Array{Int,1}, AbstractArray{Int,1}, Tuple{Int,Vararg{Integer}}, Tuple{Integer,Vararg{Int}}, Tuple{}, Union{Int,Int8}, @@ -627,6 +627,10 @@ end add_variants!(easy_menagerie) add_variants!(hard_menagerie) +push!(easy_menagerie, Bottom) +push!(easy_menagerie, Ref{Bottom}) +push!(easy_menagerie, @UnionAll N NTuple{N,Bottom}) +push!(easy_menagerie, @UnionAll S<:Bottom Ref{S}) const menagerie = [easy_menagerie; hard_menagerie] @@ -673,9 +677,11 @@ function test_properties() @test isequal_type(T, S) == isequal_type(Ref{T}, Ref{S}) # covariance - @test issubTS == issub(Tuple{T}, Tuple{S}) - @test issubTS == issub(Tuple{Vararg{T}}, Tuple{Vararg{S}}) - @test issubTS == issub(Tuple{T}, Tuple{Vararg{S}}) + if T !== Bottom && S !== Bottom + @test issubTS == issub(Tuple{T}, Tuple{S}) + @test issubTS == issub(Tuple{Vararg{T}}, Tuple{Vararg{S}}) + @test issubTS == issub(Tuple{T}, Tuple{Vararg{S}}) + end # pseudo-contravariance @test issubTS == issub(¬S, ¬T) @@ -1874,8 +1880,11 @@ s26065 = Ref{Tuple{T,Ref{Union{Ref{Tuple{Ref{Union{Ref{Ref{Tuple{Ref{Tuple{Union Tuple{Type{Tuple{Vararg{V}}}, Tuple{Vararg{V}}} where V) # issue 36100 -@test NamedTuple{(:a, :b), Tuple{Missing, Union{}}} == NamedTuple{(:a, :b), Tuple{Missing, Union{}}} -@test Val{Tuple{Missing, Union{}}} === Val{Tuple{Missing, Union{}}} +@test Pair{(:a, :b), Tuple{Missing, Vararg{Union{},N}} where N} === + Pair{(:a, :b), Tuple{Missing, Vararg{Union{},N}} where N} != + Pair{(:a, :b), Tuple{Missing, Vararg{Union{}}}} === Pair{(:a, :b), Tuple{Missing}} +@test Val{Tuple{Missing, Vararg{Union{},N}} where N} === Val{Tuple{Missing, Vararg{Union{},N}} where N} != + Val{Tuple{Missing, Vararg{Union{}}}} === Val{Tuple{Missing}} # issue #36869 struct F36869{T, V} <: AbstractArray{Union{T, V}, 1} diff --git a/test/tuple.jl b/test/tuple.jl index ae764bd05481b..9238c6cd6d7a6 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -783,3 +783,11 @@ namedtup = (;a=1, b=2, c=3) # Make sure that tuple iteration is foldable @test Core.Compiler.is_foldable(Base.infer_effects(iterate, Tuple{NTuple{4, Float64}, Int})) @test Core.Compiler.is_foldable(Base.infer_effects(eltype, Tuple{Tuple})) + +# some basic equivalence handling tests for Union{} appearing in Tuple Vararg parameters +@test Tuple{} <: Tuple{Vararg{Union{}}} +@test Tuple{Int} <: Tuple{Int, Vararg{Union{}}} +@test_throws ErrorException("Tuple field type cannot be Union{}") Tuple{Int, Vararg{Union{},1}} +@test_throws ErrorException("Tuple field type cannot be Union{}") Tuple{Vararg{Union{},1}} +@test Tuple{} <: Tuple{Vararg{Union{},N}} where N +@test !(Tuple{} >: Tuple{Vararg{Union{},N}} where N)