From 1a3e13462b2b965ec92b71f1c982925278406f04 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 21 Oct 2020 23:39:01 -0400 Subject: [PATCH] WIP: Make Vararg not a DataType Currently `Vararg` is a DataType, but is special cased in a bunch of places to give it special behavior (e.g. in subtyping and of course in tuple construction). However, unlike all other DataTypes, it cannot appear as a type parameter, which caused trouble in PR #38071. Having it be a DataType is a bit of a pun of convenience in the first place - it's a lot more similar to a tvar (which can be considered an implementation detail of UnionAll in the same way Vararg is an implementation detail of Tuple), which has its own non-type object. This PR does the same to Vararg, and moves it from being an abstract DataType with special cased behavior to its own custom type (called `Core.VarargMarker`). The user facing behavior should be mostly unchanged, since `boot.jl` now has: ``` const Vararg{T, N} = VarargMarker{T, N} ``` i.e. we have a handly UnionAll wrapper that looks just like it used to. The biggest difference is probably that VarargMarker does not have `.parameters`, so code that tries to reach into that explicitly will need to be adjusted. We could provide a compatibility `getproperty` method to adapt that, but I'd prefer to see how many packages need updating first, before going that route. This is essentially complete, but a few cleanup items remain. --- base/boot.jl | 21 +++-- base/compiler/tfuncs.jl | 4 +- base/compiler/typelimits.jl | 31 ++++--- base/essentials.jl | 7 +- base/promotion.jl | 2 +- base/show.jl | 8 ++ src/builtins.c | 7 +- src/dump.c | 18 +++- src/jltypes.c | 106 ++++++++++++++--------- src/julia.h | 9 +- src/julia_internal.h | 29 +++++-- src/method.c | 2 +- src/serialize.h | 3 +- src/staticdata.c | 4 +- src/subtype.c | 162 +++++++++++++++++++++--------------- src/typemap.c | 6 +- stdlib/Test/src/Test.jl | 2 +- 17 files changed, 263 insertions(+), 158 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 54d852ca96416..1b8a84e90a7d7 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -242,14 +242,23 @@ ccall(:jl_toplevel_eval_in, Any, (Any, Any), (f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x)) end) -# let the compiler assume that calling Union{} as a constructor does not need -# to be considered ever (which comes up often as Type{<:T}) -Union{}(a...) = throw(MethodError(Union{}, a)) macro nospecialize(x) _expr(:meta, :nospecialize, x) end +TypeVar(n::Symbol) = _typevar(n, Union{}, Any) +TypeVar(n::Symbol, @nospecialize(ub)) = _typevar(n, Union{}, ub) +TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub) + +UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) + +const Vararg{T, N} = VarargMarker{T, N} + +# let the compiler assume that calling Union{} as a constructor does not need +# to be considered ever (which comes up often as Type{<:T}) +Union{}(a...) = throw(MethodError(Union{}, a)) + Expr(@nospecialize args...) = _expr(args...) abstract type Exception end @@ -378,12 +387,6 @@ mutable struct WeakRef (Ptr{Cvoid}, Any), getptls(), v) end -TypeVar(n::Symbol) = _typevar(n, Union{}, Any) -TypeVar(n::Symbol, @nospecialize(ub)) = _typevar(n, Union{}, ub) -TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub) - -UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) - Tuple{}() = () struct VecElement{T} diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 7f5d3e42522d7..fa9ab3ecfabbe 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -449,7 +449,7 @@ function typebound_nothrow(b) if isType(b) b = unwrap_unionall(b.parameters[1]) b === Union{} && return true - return !isa(b, DataType) || b.name != _va_typename + return !isa(b, DataType) || !isa(b, Core.VarargMarker) end return false end @@ -490,7 +490,7 @@ function typeof_concrete_vararg(t::DataType) p = t.parameters[i] if i == np && isvarargtype(p) pp = unwrap_unionall(p) - if isconcretetype(pp.parameters[1]) && pp.parameters[2] isa TypeVar + if isconcretetype(pp.T) && pp.N isa TypeVar return rewrap_unionall(Type{Tuple{t.parameters[1:np-1]..., pp}}, p) end elseif !isconcretetype(p) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 22be265287fa5..5a9989689a8f4 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -46,6 +46,8 @@ function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) # see if it is derived from the body # also handle the var here, since this construct bounds the mindepth to the smallest possible value return is_derived_type(t, c.var.ub, mindepth) || is_derived_type(t, c.body, mindepth) + elseif isa(c, Core.VarargMarker) + return is_derived_type(t, c.T, mindepth) elseif isa(c, DataType) if mindepth > 0 mindepth -= 1 @@ -85,7 +87,7 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec return t # fast path: unparameterized are always simple else ut = unwrap_unionall(t) - if isa(ut, DataType) && ut.name !== _va_typename && isa(c, Type) && c !== Union{} && c <: t + if isa(ut, DataType) && isa(c, Type) && c !== Union{} && c <: t # TODO: need to check that the UnionAll bounds on t are limited enough too return t # t is already wider than the comparison in the type lattice elseif is_derived_type_from_any(ut, sources, depth) @@ -118,19 +120,20 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec b = _limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen) return Union{a, b} end + elseif isa(t, Core.VarargMarker) + isa(c, Core.VarargMarker) || return Vararg + VaT = _limit_type_size(t.T, c.T, sources, depth + 1, 0) + N = t.N + if isa(N, TypeVar) || N === c.N + return Vararg{VaT, N} + end + return Vararg{VaT} elseif isa(t, DataType) if isa(c, DataType) tP = t.parameters cP = c.parameters if t.name === c.name && !isempty(cP) - if isvarargtype(t) - VaT = _limit_type_size(tP[1], cP[1], sources, depth + 1, 0) - N = tP[2] - if isa(N, TypeVar) || N === cP[2] - return Vararg{VaT, N} - end - return Vararg{VaT} - elseif t.name === Tuple.name + if t.name === Tuple.name # for covariant datatypes (Tuple), # apply type-size limit element-wise ltP = length(tP) @@ -155,10 +158,10 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec end return Tuple{Q...} end - elseif isvarargtype(c) - # Tuple{Vararg{T}} --> Tuple{T} is OK - return _limit_type_size(t, cP[1], sources, depth, 0) end + elseif isa(c, Core.VarargMarker) + # Tuple{Vararg{T}} --> Tuple{T} is OK + return _limit_type_size(t, c.T, sources, depth, 0) end if isType(t) # allow taking typeof as Type{...}, but ensure it doesn't start nesting tt = unwrap_unionall(t.parameters[1]) @@ -166,10 +169,6 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec is_derived_type_from_any(tt, sources, depth) && return t end end - if isvarargtype(t) - # never replace Vararg with non-Vararg - return Vararg - end if allowed_tuplelen < 1 && t.name === Tuple.name return Any end diff --git a/base/essentials.jl b/base/essentials.jl index 31c746bab5d28..79d78bba233af 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -271,10 +271,9 @@ function rename_unionall(@nospecialize(u)) return UnionAll(nv, body{nv}) end -const _va_typename = Vararg.body.body.name function isvarargtype(@nospecialize(t)) t = unwrap_unionall(t) - return isa(t, DataType) && (t::DataType).name === _va_typename + return isa(t, Core.VarargMarker) end function isvatuple(@nospecialize(t)) @@ -289,14 +288,14 @@ end function unwrapva(@nospecialize(t)) # NOTE: this returns a related type, but it's NOT a subtype of the original tuple t2 = unwrap_unionall(t) - return isvarargtype(t2) ? rewrap_unionall(t2.parameters[1], t) : t + return isvarargtype(t2) ? rewrap_unionall(t2.T, t) : t end function unconstrain_vararg_length(@nospecialize(va)) # construct a new Vararg type where its length is unconstrained, # but its element type still captures any dependencies the input # element type may have had on the input length - T = unwrap_unionall(va).parameters[1] + T = unwrap_unionall(va).T return rewrap_unionall(Vararg{T}, va) end diff --git a/base/promotion.jl b/base/promotion.jl index 56ab533c18b59..47ec12fe5cb5a 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -157,7 +157,7 @@ function full_va_len(p) isempty(p) && return 0, true last = p[end] if isvarargtype(last) - N = unwrap_unionall(last).parameters[2] + N = unwrap_unionall(last).N if isa(N, Int) return length(p)::Int + N - 1, true end diff --git a/base/show.jl b/base/show.jl index 82cea00739833..ef8726db0db9d 100644 --- a/base/show.jl +++ b/base/show.jl @@ -2248,6 +2248,14 @@ function show(io::IO, tv::TypeVar) nothing end +function show(io::IO, vm::Core.VarargMarker) + print(io, "Vararg{") + show(io, vm.T) + print(io, ", ") + show(io, vm.N) + print(io, "}") +end + module IRShow const Compiler = Core.Compiler using Core.IR diff --git a/src/builtins.c b/src/builtins.c index 8cbce6c5b6188..78eecfb92b7a2 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1002,6 +1002,10 @@ JL_CALLABLE(jl_f_apply_type) // substituting typevars (a valid_type_param check here isn't sufficient). return (jl_value_t*)jl_type_union(&args[1], nargs-1); } + else if (args[0] == (jl_value_t*)jl_vararg_marker_type) { + JL_NARGS(apply_type, 3, 3); + return jl_wrap_vararg(args[1], args[2]); + } else if (jl_is_unionall(args[0])) { for(i=1; i < nargs; i++) { jl_value_t *pi = args[i]; @@ -1257,7 +1261,6 @@ static void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) if (!jl_is_datatype(super) || !jl_is_abstracttype(super) || tt->super != NULL || tt->name == ((jl_datatype_t*)super)->name || - jl_subtype(super, (jl_value_t*)jl_vararg_type) || jl_is_tuple_type(super) || jl_is_namedtuple_type(super) || jl_subtype(super, (jl_value_t*)jl_type_type) || @@ -1577,7 +1580,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("Union", (jl_value_t*)jl_uniontype_type); add_builtin("TypeofBottom", (jl_value_t*)jl_typeofbottom_type); add_builtin("Tuple", (jl_value_t*)jl_anytuple_type); - add_builtin("Vararg", (jl_value_t*)jl_vararg_type); + add_builtin("VarargMarker", (jl_value_t*)jl_vararg_marker_type); add_builtin("SimpleVector", (jl_value_t*)jl_simplevector_type); add_builtin("Module", (jl_value_t*)jl_module_type); diff --git a/src/dump.c b/src/dump.c index 4206f1a3d7d26..f307c51c8ef50 100644 --- a/src/dump.c +++ b/src/dump.c @@ -566,6 +566,11 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li jl_serialize_value(s, ((jl_tvar_t*)v)->lb); jl_serialize_value(s, ((jl_tvar_t*)v)->ub); } + else if (jl_is_vararg_marker(v)) { + write_uint8(s->s, TAG_VARARG); + jl_serialize_value(s, ((jl_vararg_marker_t*)v)->T); + jl_serialize_value(s, ((jl_vararg_marker_t*)v)->N); + } else if (jl_is_method(v)) { write_uint8(s->s, TAG_METHOD); jl_method_t *m = (jl_method_t*)v; @@ -1773,6 +1778,15 @@ static jl_value_t *jl_deserialize_value(jl_serializer_state *s, jl_value_t **loc tv->ub = jl_deserialize_value(s, &tv->ub); jl_gc_wb(tv, tv->ub); return (jl_value_t*)tv; + case TAG_VARARG: + v = jl_gc_alloc(s->ptls, sizeof(jl_vararg_marker_t), jl_vararg_marker_type); + jl_vararg_marker_t *vm = (jl_vararg_marker_t*)v; + arraylist_push(&backref_list, vm); + vm->T = (jl_sym_t*)jl_deserialize_value(s, NULL); + jl_gc_wb(vm, vm->T); + vm->N = jl_deserialize_value(s, &vm->N); + jl_gc_wb(vm, vm->N); + return (jl_value_t*)vm; case TAG_METHOD: return jl_deserialize_value_method(s, loc); case TAG_METHOD_INSTANCE: @@ -2607,7 +2621,8 @@ void jl_init_serializer(void) jl_bool_type, jl_linenumbernode_type, jl_pinode_type, jl_upsilonnode_type, jl_type_type, jl_bottom_type, jl_ref_type, - jl_pointer_type, jl_vararg_type, jl_abstractarray_type, jl_nothing_type, + jl_pointer_type, jl_abstractarray_type, jl_nothing_type, + jl_vararg_marker_type, jl_densearray_type, jl_function_type, jl_typename_type, jl_builtin_type, jl_task_type, jl_uniontype_type, jl_array_any_type, jl_intrinsic_type, @@ -2656,6 +2671,7 @@ void jl_init_serializer(void) deser_tag[TAG_GOTOIFNOT] = (jl_value_t*)jl_gotoifnot_type; deser_tag[TAG_RETURNNODE] = (jl_value_t*)jl_returnnode_type; deser_tag[TAG_ARGUMENT] = (jl_value_t*)jl_argument_type; + deser_tag[TAG_VARARG] = (jl_value_t*)jl_vararg_marker_type; intptr_t i = 0; while (vals[i] != NULL) { diff --git a/src/jltypes.c b/src/jltypes.c index 94eef9c50d9ce..70495b2e3d7b0 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -44,8 +44,7 @@ jl_datatype_t *jl_anytuple_type; jl_datatype_t *jl_emptytuple_type; jl_unionall_t *jl_anytuple_type_type; jl_typename_t *jl_vecelement_typename; -jl_unionall_t *jl_vararg_type; -jl_typename_t *jl_vararg_typename; +jl_datatype_t *jl_vararg_marker_type; jl_datatype_t *jl_tvar_type; jl_datatype_t *jl_uniontype_type; jl_datatype_t *jl_unionall_type; @@ -165,6 +164,9 @@ static int has_free_typevars(jl_value_t *v, jl_typeenv_t *env) JL_NOTSAFEPOINT if (jl_is_uniontype(v)) return has_free_typevars(((jl_uniontype_t*)v)->a, env) || has_free_typevars(((jl_uniontype_t*)v)->b, env); + if (jl_is_vararg_marker(v)) + return has_free_typevars(((jl_vararg_marker_t*)v)->T, env) || + has_free_typevars(((jl_vararg_marker_t*)v)->N, env); if (jl_is_unionall(v)) { jl_unionall_t *ua = (jl_unionall_t*)v; jl_typeenv_t newenv = { ua->var, NULL, env }; @@ -201,6 +203,10 @@ static void find_free_typevars(jl_value_t *v, jl_typeenv_t *env, jl_array_t *out find_free_typevars(((jl_uniontype_t*)v)->a, env, out); find_free_typevars(((jl_uniontype_t*)v)->b, env, out); } + else if (jl_is_vararg_marker(v)) { + find_free_typevars(((jl_vararg_marker_t*)v)->T, env, out); + find_free_typevars(((jl_vararg_marker_t*)v)->N, env, out); + } else if (jl_is_unionall(v)) { jl_unionall_t *ua = (jl_unionall_t*)v; jl_typeenv_t newenv = { ua->var, NULL, env }; @@ -234,6 +240,10 @@ static int jl_has_bound_typevars(jl_value_t *v, jl_typeenv_t *env) JL_NOTSAFEPOI if (jl_is_uniontype(v)) return jl_has_bound_typevars(((jl_uniontype_t*)v)->a, env) || jl_has_bound_typevars(((jl_uniontype_t*)v)->b, env); + if (jl_is_vararg_marker(v)) { + return jl_has_bound_typevars(((jl_vararg_marker_t*)v)->T, env) || + jl_has_bound_typevars(((jl_vararg_marker_t*)v)->N, env); + } if (jl_is_unionall(v)) { jl_unionall_t *ua = (jl_unionall_t*)v; if (jl_has_bound_typevars(ua->var->lb, env) || jl_has_bound_typevars(ua->var->ub, env)) @@ -529,7 +539,7 @@ JL_DLLEXPORT jl_value_t *jl_type_union(jl_value_t **ts, size_t n) JL_DLLEXPORT jl_value_t *jl_type_unionall(jl_tvar_t *v, jl_value_t *body) { - if (!jl_is_type(body) && !jl_is_typevar(body)) + if (!jl_is_type(body) && !jl_is_typevar(body) && !jl_is_vararg_marker(body)) jl_type_error("UnionAll", (jl_value_t*)jl_type_type, body); // normalize `T where T<:S` => S if (body == (jl_value_t*)v) @@ -1088,6 +1098,14 @@ static unsigned type_hash(jl_value_t *kj, int *failed) JL_NOTSAFEPOINT // ignore var and lb, since those might get normalized out in equality testing return type_hash(((jl_tvar_t*)uw)->ub, failed); } + else if (jl_is_vararg_marker(uw)) { + if (!*failed) { + *failed = 1; + return 0; + } + jl_vararg_marker_t *vm = (jl_vararg_marker_t *)uw; + return bitmix(type_hash(vm->T, failed), type_hash(vm->N, failed)); + } else if (jl_is_uniontype(uw)) { if (!*failed) { *failed = 1; @@ -1147,8 +1165,6 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) dt->hasfreetypevars = 0; dt->isconcretetype = !dt->abstract; dt->isdispatchtuple = istuple; - if (dt->name == jl_vararg_typename) - dt->isconcretetype = 0; size_t i, l = jl_nparams(dt); for (i = 0; i < l; i++) { jl_value_t *p = jl_tparam(dt, i); @@ -1228,7 +1244,7 @@ static jl_value_t *normalize_vararg(jl_value_t *va) jl_unionall_t *ua = (jl_unionall_t*)va; body = normalize_vararg(ua->body); jl_value_t *unw = jl_unwrap_unionall(body); - jl_value_t *va0 = jl_tparam0(unw), *va1 = jl_tparam1(unw); + jl_value_t *va0 = jl_unwrap_vararg(unw), *va1 = jl_unwrap_vararg_num(unw); if (jl_has_typevar(va1, ua->var)) { if (body != ua->body) va = jl_type_unionall(ua->var, body); @@ -1285,20 +1301,6 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value return stack_lkup; if (!istuple) { - if (jl_is_vararg_type((jl_value_t*)dt) && ntp == 2) { - jl_value_t *lenparam = iparams[1]; - if (jl_is_typevar(lenparam)) { - jl_tvar_t *N = (jl_tvar_t*)lenparam; - if (!(N->lb == jl_bottom_type && N->ub == (jl_value_t*)jl_any_type)) - jl_error("TypeVar in Vararg length must have bounds Union{} and Any"); - } - else if (!jl_is_long(lenparam)) { - jl_type_error_rt("Vararg", "count", (jl_value_t*)jl_long_type, lenparam); - } - else if (jl_unbox_long(lenparam) < 0) { - jl_errorf("Vararg length is negative: %zd", jl_unbox_long(lenparam)); - } - } // check parameters against bounds in type definition check_datatype_parameters(tn, iparams, ntp); } @@ -1316,12 +1318,11 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value isvatuple = 1; // normalize Tuple{..., Vararg{Int, 3}} to Tuple{..., Int, Int, Int} jl_value_t *va = jl_unwrap_unionall(last); - jl_value_t *va0 = jl_tparam0(va), *va1 = jl_tparam1(va); + jl_value_t *va0 = jl_unwrap_vararg(va), *va1 = jl_unwrap_vararg_num(va); // return same `Tuple` object for types equal to it if (ntp == 1 && - (last == (jl_value_t*)jl_vararg_type || // Tuple{Vararg} == Tuple (va0 == (jl_value_t*)jl_any_type && - jl_is_unionall(last) && va1 == (jl_value_t*)((jl_unionall_t*)last)->var))) { + jl_is_unionall(last) && va1 == (jl_value_t*)((jl_unionall_t*)last)->var)) { JL_GC_POP(); return (jl_value_t*)jl_anytuple_type; } @@ -1332,7 +1333,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value last = last2; did_normalize = 1; va = jl_unwrap_unionall(last); - va0 = jl_tparam0(va); va1 = jl_tparam1(va); + va0 = jl_unwrap_vararg(va); va1 = jl_unwrap_vararg_num(va); } if (jl_is_long(va1)) { ssize_t nt = jl_unbox_long(va1); @@ -1584,8 +1585,8 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ // a fixed-length tuple jl_value_t *T=NULL, *N=NULL; jl_value_t *va = jl_unwrap_unionall(jl_tparam0(tt)); - jl_value_t *ttT = jl_tparam0(va); - jl_value_t *ttN = jl_tparam1(va); + jl_value_t *ttT = jl_unwrap_vararg(va); + jl_value_t *ttN = jl_unwrap_vararg_num(va); jl_typeenv_t *e = env; while (e != NULL) { if ((jl_value_t*)e->var == ttT) @@ -1685,6 +1686,18 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t JL_GC_POP(); return t; } + if (jl_is_vararg_marker(t)) { + jl_vararg_marker_t *v = (jl_vararg_marker_t*)t; + jl_value_t *T = inst_type_w_(v->T, env, stack, check); + jl_value_t *N = NULL; + JL_GC_PUSH2(&T, &N); + N = inst_type_w_(v->N, env, stack, check); + if (T != v->T || N != v->N) { + t = jl_wrap_vararg(T, N); + } + JL_GC_POP(); + return t; + } if (!jl_is_datatype(t)) return t; jl_datatype_t *tt = (jl_datatype_t*)t; @@ -1757,17 +1770,28 @@ jl_datatype_t *jl_wrap_Type(jl_value_t *t) jl_value_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) { - if (t == NULL) { - assert(n == NULL); - return (jl_value_t*)jl_vararg_type; + assert(t); + if (!n) { + jl_tvar_t *N = jl_new_typevar(jl_symbol("N"), jl_bottom_type, jl_any_type); + jl_vararg_marker_t *vm; + JL_GC_PUSH2(&N, &vm); + vm = jl_wrap_vararg(t, N); + jl_value_t *ret = jl_type_unionall(N, vm); + JL_GC_POP(); + return ret; } - jl_value_t *vt = jl_instantiate_unionall(jl_vararg_type, t); - if (n == NULL) - return vt; - JL_GC_PUSH1(&vt); - jl_value_t *vn = jl_instantiate_unionall((jl_unionall_t*)vt, n); - JL_GC_POP(); - return vn; + if (jl_is_typevar(n)) { + jl_tvar_t *N = (jl_tvar_t*)n; + if (!(N->lb == jl_bottom_type && N->ub == (jl_value_t*)jl_any_type)) + jl_error("TypeVar in Vararg length must have bounds Union{} and Any"); + } + else if (!jl_is_long(n)) { + jl_type_error_rt("Vararg", "count", (jl_value_t*)jl_long_type, n); + } + else if (jl_unbox_long(n) < 0) { + jl_errorf("Vararg length is negative: %zd", jl_unbox_long(n)); + } + return jl_new_struct(jl_vararg_marker_type, t, n); } JL_DLLEXPORT jl_svec_t *jl_compute_fieldtypes(jl_datatype_t *st JL_PROPAGATES_ROOT, void *stack) @@ -2011,10 +2035,10 @@ void jl_init_types(void) JL_GC_DISABLED jl_svec(2, jl_tvar_type, jl_any_type), 0, 0, 2); - jl_svec_t *tv; - tv = jl_svec2(tvar("T"),tvar("N")); - jl_vararg_type = (jl_unionall_t*)jl_new_abstracttype((jl_value_t*)jl_symbol("Vararg"), core, jl_any_type, tv)->name->wrapper; - jl_vararg_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_vararg_type))->name; + jl_vararg_marker_type = jl_new_datatype(jl_symbol("VarargMarker"), core, jl_any_type, jl_emptysvec, + jl_perm_symsvec(2, "T", "N"), + jl_svec(2, jl_any_type, jl_any_type), + 0, 0, 2); jl_svec_t *anytuple_params = jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type, (jl_value_t*)NULL)); jl_anytuple_type = jl_new_datatype(jl_symbol("Tuple"), core, jl_any_type, anytuple_params, @@ -2136,7 +2160,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_function_type->name->mt = NULL; // subtypes of Function have independent method tables jl_builtin_type->name->mt = NULL; // so they don't share the Any type table - tv = jl_svec2(tvar("T"), tvar("N")); + jl_tvar_t *tv = jl_svec2(tvar("T"), tvar("N")); jl_abstractarray_type = (jl_unionall_t*) jl_new_abstracttype((jl_value_t*)jl_symbol("AbstractArray"), core, jl_any_type, tv)->name->wrapper; diff --git a/src/julia.h b/src/julia.h index 7560886301840..0139aa48eb00d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -480,6 +480,12 @@ typedef struct _jl_datatype_t { uint8_t cached_by_hash; // stored in hash-based set cache (instead of linear cache) } jl_datatype_t; +typedef struct _jl_vararg_marker_t { + JL_DATA_TYPE + jl_value_t *T; + jl_value_t *N; +} jl_vararg_marker_t; + typedef struct { JL_DATA_TYPE jl_value_t *value; @@ -599,6 +605,7 @@ extern JL_DLLEXPORT jl_datatype_t *jl_datatype_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_uniontype_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_unionall_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_tvar_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_datatype_t *jl_vararg_marker_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_any_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_type_type JL_GLOBALLY_ROOTED; @@ -620,8 +627,6 @@ extern JL_DLLEXPORT jl_datatype_t *jl_anytuple_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_emptytuple_type JL_GLOBALLY_ROOTED; #define jl_tuple_type jl_anytuple_type extern JL_DLLEXPORT jl_unionall_t *jl_anytuple_type_type JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_unionall_t *jl_vararg_type JL_GLOBALLY_ROOTED; -extern JL_DLLEXPORT jl_typename_t *jl_vararg_typename JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_function_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_builtin_type JL_GLOBALLY_ROOTED; diff --git a/src/julia_internal.h b/src/julia_internal.h index 369781b79bed3..13eb41bf1c443 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -528,16 +528,34 @@ typedef enum { JL_VARARG_UNBOUND = 3 } jl_vararg_kind_t; +STATIC_INLINE int jl_is_vararg_marker(jl_value_t *v) JL_NOTSAFEPOINT +{ + return jl_typeof(v) == (jl_value_t*)jl_vararg_marker_type; +} + STATIC_INLINE int jl_is_vararg_type(jl_value_t *v) JL_NOTSAFEPOINT { - v = jl_unwrap_unionall(v); - return (jl_is_datatype(v) && - ((jl_datatype_t*)(v))->name == jl_vararg_typename); + return jl_is_vararg_marker(jl_unwrap_unionall(v)); } STATIC_INLINE jl_value_t *jl_unwrap_vararg(jl_value_t *v) JL_NOTSAFEPOINT { - return jl_tparam0(jl_unwrap_unionall(v)); + assert(jl_is_vararg_type(v)); + v = jl_unwrap_unionall(v); + if (jl_is_vararg_marker(v)) { + return ((jl_vararg_marker_t*)v)->T; + } + return jl_tparam0(v); +} + +STATIC_INLINE jl_value_t *jl_unwrap_vararg_num(jl_value_t *v) JL_NOTSAFEPOINT +{ + assert(jl_is_vararg_type(v)); + v = jl_unwrap_unionall(v); + if (jl_is_vararg_marker(v)) { + return ((jl_vararg_marker_t*)v)->N; + } + return jl_tparam1(v); } STATIC_INLINE jl_vararg_kind_t jl_vararg_kind(jl_value_t *v) JL_NOTSAFEPOINT @@ -553,8 +571,7 @@ STATIC_INLINE jl_vararg_kind_t jl_vararg_kind(jl_value_t *v) JL_NOTSAFEPOINT v = ((jl_unionall_t*)v)->body; } } - assert(jl_is_datatype(v)); - jl_value_t *lenv = jl_tparam1(v); + jl_value_t *lenv = jl_unwrap_vararg_num(v); if (jl_is_long(lenv)) return JL_VARARG_INT; if (jl_is_typevar(lenv) && lenv != (jl_value_t*)v1 && lenv != (jl_value_t*)v2) diff --git a/src/method.c b/src/method.c index 5f4a954f882b8..9b27f80baf038 100644 --- a/src/method.c +++ b/src/method.c @@ -779,7 +779,7 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, for (i = 0; i < na; i++) { jl_value_t *elt = jl_svecref(atypes, i); - if (!jl_is_type(elt) && !jl_is_typevar(elt)) { + if (!jl_is_type(elt) && !jl_is_typevar(elt) && !jl_is_vararg_type(elt)) { jl_sym_t *argname = (jl_sym_t*)jl_array_ptr_ref(f->slotnames, i); if (argname == unused_sym) jl_exceptionf(jl_argumenterror_type, diff --git a/src/serialize.h b/src/serialize.h index 07ff46fdf96db..5ef4191b6c4e8 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -62,8 +62,9 @@ extern "C" { #define TAG_GOTOIFNOT 54 #define TAG_RETURNNODE 55 #define TAG_ARGUMENT 56 +#define TAG_VARARG 57 -#define LAST_TAG 56 +#define LAST_TAG 57 #define write_uint8(s, n) ios_putc((n), (s)) #define read_uint8(s) ((uint8_t)ios_getc(s)) diff --git a/src/staticdata.c b/src/staticdata.c index ddc5f2f9dc335..2836c7fff1118 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -43,7 +43,7 @@ static void *const _tags[] = { &jl_const_type, &jl_partial_struct_type, &jl_method_match_type, &jl_pinode_type, &jl_phinode_type, &jl_phicnode_type, &jl_upsilonnode_type, &jl_type_type, &jl_bottom_type, &jl_ref_type, &jl_pointer_type, &jl_llvmpointer_type, - &jl_vararg_type, &jl_abstractarray_type, + &jl_abstractarray_type, &jl_vararg_marker_type, &jl_densearray_type, &jl_nothing_type, &jl_function_type, &jl_typeofbottom_type, &jl_unionall_type, &jl_typename_type, &jl_builtin_type, &jl_code_info_type, &jl_task_type, &jl_uniontype_type, &jl_abstractstring_type, @@ -59,7 +59,7 @@ static void *const _tags[] = { &jl_number_type, &jl_signed_type, // special typenames &jl_tuple_typename, &jl_pointer_typename, &jl_llvmpointer_typename, &jl_array_typename, &jl_type_typename, - &jl_vararg_typename, &jl_namedtuple_typename, + &jl_namedtuple_typename, &jl_vecelement_typename, // special exceptions &jl_errorexception_type, &jl_argumenterror_type, &jl_typeerror_type, diff --git a/src/subtype.c b/src/subtype.c index f5beb922d8c9b..a921190a8e8d4 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -222,6 +222,10 @@ static int obviously_egal(jl_value_t *a, jl_value_t *b) return ((jl_unionall_t*)a)->var == ((jl_unionall_t*)b)->var && obviously_egal(((jl_unionall_t*)a)->body, ((jl_unionall_t*)b)->body); } + if (jl_is_vararg_marker(a)) { + return obviously_egal(((jl_vararg_marker_t*)a)->T, ((jl_vararg_marker_t*)b)->T) && + obviously_egal(((jl_vararg_marker_t*)a)->N, ((jl_vararg_marker_t*)b)->N); + } if (jl_is_typevar(a)) return 0; return !jl_is_type(a) && jl_egal(a,b); } @@ -407,7 +411,7 @@ static jl_value_t *simple_join(jl_value_t *a, jl_value_t *b) return b; if (b == jl_bottom_type || a == (jl_value_t*)jl_any_type) return a; - if (!(jl_is_type(a) || jl_is_typevar(a)) || !(jl_is_type(b) || jl_is_typevar(b))) + if (!(jl_is_type(a) || jl_is_typevar(a) || jl_is_vararg_type(a)) || !(jl_is_type(b) || jl_is_typevar(b) || jl_is_vararg_type(b))) return (jl_value_t*)jl_any_type; if (jl_is_uniontype(a) && in_union(a, b)) return a; @@ -849,7 +853,7 @@ static jl_datatype_t *unwrap_1_unionall(jl_value_t *t, jl_tvar_t **p1) JL_NOTSAF *p1 = ((jl_unionall_t*)t)->var; t = ((jl_unionall_t*)t)->body; } - assert(jl_is_datatype(t)); + assert(jl_is_datatype(t) || jl_is_vararg_marker(t)); return (jl_datatype_t*)t; } @@ -857,8 +861,8 @@ static jl_datatype_t *unwrap_1_unionall(jl_value_t *t, jl_tvar_t **p1) JL_NOTSAF static int check_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e) { jl_tvar_t *va_p1=NULL; - jl_datatype_t *tail = unwrap_1_unionall(v, &va_p1); - jl_value_t *N = jl_tparam1(tail); + jl_value_t *tail = unwrap_1_unionall(v, &va_p1); + jl_value_t *N = jl_unwrap_vararg_num(tail); // only do the check if N is free in the tuple type's last parameter if (N != (jl_value_t*)va_p1) { jl_value_t *nn = jl_box_long(n); @@ -891,14 +895,14 @@ struct subtype_tuple_env { static int subtype_tuple_varargs(struct subtype_tuple_env *env, jl_stenv_t *e, int param) { jl_tvar_t *yv1=NULL; - jl_datatype_t *yva = unwrap_1_unionall(env->vty, &yv1); + jl_value_t *yva = unwrap_1_unionall(env->vty, &yv1); jl_tvar_t *xv1=NULL; - jl_datatype_t *xva = unwrap_1_unionall(env->vtx, &xv1); + jl_value_t *xva = unwrap_1_unionall(env->vtx, &xv1); - jl_value_t *xp0 = jl_tparam0(xva); jl_value_t *xp1 = jl_tparam1(xva); - jl_value_t *yp0 = jl_tparam0(yva); jl_value_t *yp1 = jl_tparam1(yva); + jl_value_t *xp0 = jl_unwrap_vararg(xva); jl_value_t *xp1 = jl_unwrap_vararg_num(xva); + jl_value_t *yp0 = jl_unwrap_vararg(yva); jl_value_t *yp1 = jl_unwrap_vararg_num(yva); - if (!jl_is_datatype(env->vtx)) { + if (!jl_is_vararg_marker(env->vtx)) { // Unconstrained on the left, constrained on the right jl_value_t *yl = yp1; if (jl_is_typevar(yl)) { @@ -911,7 +915,7 @@ static int subtype_tuple_varargs(struct subtype_tuple_env *env, jl_stenv_t *e, i } } else { - jl_value_t *xl = jl_tparam1(env->vtx); + jl_value_t *xl = jl_unwrap_vararg_num(env->vtx); if (jl_is_typevar(xl)) { jl_varbinding_t *xlv = lookup(e, (jl_tvar_t*)xl); if (xlv) @@ -922,8 +926,8 @@ static int subtype_tuple_varargs(struct subtype_tuple_env *env, jl_stenv_t *e, i // LHS is exhausted. We're a subtype if the RHS is either // exhausted as well or unbounded (in which case we need to // set it to 0). - if (jl_is_datatype(env->vty)) { - jl_value_t *yl = jl_tparam1(env->vty); + if (jl_is_vararg_marker(env->vty)) { + jl_value_t *yl = jl_unwrap_vararg_num(env->vty); if (jl_is_typevar(yl)) { jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)yl); if (ylv) @@ -950,7 +954,7 @@ static int subtype_tuple_varargs(struct subtype_tuple_env *env, jl_stenv_t *e, i if (!subtype(xp0, yp0, e, 1)) return 0; constrain_length: - if (!jl_is_datatype(env->vtx)) { + if (!jl_is_vararg_marker(env->vtx)) { jl_value_t *yl = yp1; if (jl_is_typevar(yl)) { jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)yl); @@ -1050,7 +1054,7 @@ static int subtype_tuple_tail(struct subtype_tuple_env *env, int8_t R, jl_stenv_ } if (env->vx) { - xi = jl_tparam0(jl_unwrap_unionall(env->vtx)); + xi = jl_unwrap_vararg(env->vtx); if (env->j >= env->ly) return 1; } @@ -1059,7 +1063,7 @@ static int subtype_tuple_tail(struct subtype_tuple_env *env, int8_t R, jl_stenv_ } int x_same = env->lastx && jl_egal(xi, env->lastx); if (env->vy) { - yi = jl_tparam0(jl_unwrap_unionall(env->vty)); + yi = jl_unwrap_vararg(env->vty); if (!env->vvx && yi == (jl_value_t*)jl_any_type) goto done; // if y ends in `Vararg{Any}` skip checking everything // keep track of number of consecutive identical types compared to Vararg @@ -1121,7 +1125,7 @@ static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, in if (env.lx > 0) { env.vvx = jl_vararg_kind(jl_tparam(env.xd, env.lx-1)); if (env.vvx == JL_VARARG_BOUND) - xbb = lookup(e, (jl_tvar_t *)jl_tparam1(jl_tparam(env.xd, env.lx - 1))); + xbb = lookup(e, (jl_tvar_t *)jl_unwrap_vararg_num(jl_tparam(env.xd, env.lx - 1))); } if (env.ly > 0) env.vvy = jl_vararg_kind(jl_tparam(env.yd, env.ly-1)); @@ -1173,7 +1177,7 @@ static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, in static int subtype_naked_vararg(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, int param) { // Vararg: covariant in first parameter, invariant in second - jl_value_t *xp1=jl_tparam0(xd), *xp2=jl_tparam1(xd), *yp1=jl_tparam0(yd), *yp2=jl_tparam1(yd); + jl_value_t *xp1=jl_unwrap_vararg(xd), *xp2=jl_unwrap_vararg_num(xd), *yp1=jl_unwrap_vararg(yd), *yp2=jl_unwrap_vararg_num(yd); // 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. @@ -1283,7 +1287,8 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) } if (jl_is_unionall(y)) return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param); - if (jl_is_datatype(x) && jl_is_datatype(y)) { + if ((jl_is_datatype(x) || jl_is_vararg_type(x)) && + (jl_is_datatype(y) || jl_is_vararg_type(y))) { if (x == y) return 1; if (y == (jl_value_t*)jl_any_type) return 1; jl_datatype_t *xd = (jl_datatype_t*)x, *yd = (jl_datatype_t*)y; @@ -1313,6 +1318,15 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) e->invdepth = saved; return issub; } + if (jl_is_vararg_marker(xd)) { + if (!jl_is_vararg_marker(yd)) + return 0; + // N.B.: This case is only used for raw varargs that are not part + // of a tuple (those that are have special handling in subtype_tuple). + // Vararg isn't really a proper type, but it does sometimes show up + // as e.g. Type{Vararg}, so we'd like to handle that correctly. + return subtype_naked_vararg(xd, yd, e, param); + } while (xd != jl_any_type && xd->name != yd->name) { if (xd->super == NULL) jl_errorf("circular type parameter constraint in definition of %s", jl_symbol_name(xd->name->name)); @@ -1321,13 +1335,6 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) if (xd == jl_any_type) return 0; if (xd->name == jl_tuple_typename) return subtype_tuple(xd, yd, e, param); - if (xd->name == jl_vararg_typename) { - // N.B.: This case is only used for raw varargs that are not part - // of a tuple (those that are have special handling in subtype_tuple). - // Vararg isn't really a proper type, but it does sometimes show up - // as e.g. Type{Vararg}, so we'd like to handle that correctly. - return subtype_naked_vararg(xd, yd, e, param); - } size_t i, np = jl_nparams(xd); int ans = 1; e->invdepth++; @@ -1510,6 +1517,8 @@ static int concrete_min(jl_value_t *t) return 0; // Type{T} may have the concrete supertype `typeof(T)`, so don't try to handle them here return jl_is_concrete_type(t) ? 1 : 2; } + if (jl_is_vararg_marker(t)) + return 0; if (jl_is_typevar(t)) return 0; // could be 0 or more, since we didn't track if it was unbound if (jl_is_uniontype(t)) { @@ -1538,6 +1547,11 @@ static jl_value_t *find_var_body(jl_value_t *t, jl_tvar_t *v) if (b) return b; return find_var_body(((jl_uniontype_t*)t)->b, v); } + else if (jl_is_vararg_marker(t)) { + jl_value_t *b = find_var_body(((jl_vararg_marker_t*)t)->T, v); + if (b) return b; + return find_var_body(((jl_vararg_marker_t*)t)->N, v); + } else if (jl_is_datatype(t)) { size_t i; for (i=0; i < jl_nparams(t); i++) { @@ -1594,6 +1608,13 @@ static int obvious_subtype(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *su *subtype = 0; return 1; } + if (jl_is_vararg_marker(x)) { + if (!jl_is_vararg_marker(y)) { + *subtype = 0; + return 1; + } + return 0; + } if (!jl_is_type(x) || !jl_is_type(y)) { *subtype = jl_egal(x, y); return 1; @@ -1635,7 +1656,7 @@ static int obvious_subtype(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *su } if (jl_is_datatype(y)) { int istuple = (((jl_datatype_t*)y)->name == jl_tuple_typename); - int iscov = istuple || (((jl_datatype_t*)y)->name == jl_vararg_typename); + int iscov = istuple; // TODO: this would be a nice fast-path to have, unfortuanately, // datatype allocation fails to correctly hash-cons them // and the subtyping tests include tests for this case @@ -2384,13 +2405,17 @@ static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want return 1; return var_occurs_inside(ua->body, var, inside, want_inv); } + else if (jl_is_vararg_marker(v)) { + jl_vararg_marker_t *vm = (jl_vararg_marker_t*)v; + if (var_occurs_inside(vm->T, var, inside || !want_inv, want_inv)) + return 1; + return var_occurs_inside(vm->N, var, 1, want_inv); + } else if (jl_is_datatype(v)) { size_t i; int istuple = jl_is_tuple_type(v); - int isva = jl_is_vararg_type(v); for (i=0; i < jl_nparams(v); i++) { - int invar = isva ? i == 1 : !istuple; - int ins_i = inside || !want_inv || invar; + int ins_i = inside || !want_inv || !istuple; if (var_occurs_inside(jl_tparam(v,i), var, ins_i, want_inv)) return 1; } @@ -2647,7 +2672,7 @@ static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8 { jl_tvar_t *va_p1=NULL; jl_datatype_t *tail = unwrap_1_unionall(v, &va_p1); - jl_value_t *N = jl_tparam1(tail); + jl_value_t *N = jl_unwrap_vararg_num(tail); // only do the check if N is free in the tuple type's last parameter if (jl_is_typevar(N) && N != (jl_value_t*)va_p1) { jl_value_t *len = jl_box_long(n); @@ -2698,13 +2723,13 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten jl_varbinding_t *xb=NULL, *yb=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_tparam1(jl_unwrap_unionall(xi)); + jl_value_t *xlen = jl_unwrap_vararg_num(jl_unwrap_unionall(xi)); if (jl_is_typevar(xlen)) { xb = lookup(e, (jl_tvar_t*)xlen); if (xb) xb->offset = ly-lx; } - jl_value_t *ylen = jl_tparam1(jl_unwrap_unionall(yi)); + jl_value_t *ylen = jl_unwrap_vararg_num(jl_unwrap_unionall(yi)); if (jl_is_typevar(ylen)) { yb = lookup(e, (jl_tvar_t*)ylen); if (yb) @@ -3017,6 +3042,37 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa } if (jl_is_unionall(y)) return intersect_unionall(x, (jl_unionall_t*)y, e, 1, param); + if (jl_is_vararg_marker(x) && jl_is_vararg_marker(y)) { + // Vararg: covariant in first parameter, invariant in second + jl_value_t *xp1=jl_unwrap_vararg(x), *xp2=jl_unwrap_vararg_num(x), + *yp1=jl_unwrap_vararg(y), *yp2=jl_unwrap_vararg_num(y); + // 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 (intersect(xp1, yp1, e, param==0 ? 1 : param) == jl_bottom_type) + return jl_bottom_type; + 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(xp2)) { + jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)xp2); + if (xb) xb->intvalued = 1; + } + if (jl_is_typevar(yp2)) { + jl_varbinding_t *yb = lookup(e, (jl_tvar_t*)yp2); + if (yb) yb->intvalued = 1; + } + // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 + i2 = intersect_invariant(xp2, yp2, e); + if (i2 == NULL || i2 == jl_bottom_type || (jl_is_long(i2) && jl_unbox_long(i2) < 0) || + !((jl_is_typevar(i2) && ((jl_tvar_t*)i2)->lb == jl_bottom_type && + ((jl_tvar_t*)i2)->ub == (jl_value_t*)jl_any_type) || jl_is_long(i2))) + ii = jl_bottom_type; + else + ii = jl_wrap_vararg(ii, i2); + JL_GC_POP(); + return ii; + } if (jl_is_datatype(x) && jl_is_datatype(y)) { jl_datatype_t *xd = (jl_datatype_t*)x, *yd = (jl_datatype_t*)y; if (param < 2) { @@ -3031,36 +3087,6 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa if (xd->name == yd->name) { if (jl_is_tuple_type(xd)) return intersect_tuple(xd, yd, e, param); - if (jl_is_vararg_type(x)) { - // Vararg: covariant in first parameter, invariant in second - jl_value_t *xp1=jl_tparam0(xd), *xp2=jl_tparam1(xd), *yp1=jl_tparam0(yd), *yp2=jl_tparam1(yd); - // 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 (intersect(xp1, yp1, e, param==0 ? 1 : param) == jl_bottom_type) - return jl_bottom_type; - 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(xp2)) { - jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)xp2); - if (xb) xb->intvalued = 1; - } - if (jl_is_typevar(yp2)) { - jl_varbinding_t *yb = lookup(e, (jl_tvar_t*)yp2); - if (yb) yb->intvalued = 1; - } - // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 - i2 = intersect_invariant(xp2, yp2, e); - if (i2 == NULL || i2 == jl_bottom_type || (jl_is_long(i2) && jl_unbox_long(i2) < 0) || - !((jl_is_typevar(i2) && ((jl_tvar_t*)i2)->lb == jl_bottom_type && - ((jl_tvar_t*)i2)->ub == (jl_value_t*)jl_any_type) || jl_is_long(i2))) - ii = jl_bottom_type; - else - ii = jl_apply_type2((jl_value_t*)jl_vararg_type, ii, i2); - JL_GC_POP(); - return ii; - } size_t i, np = jl_nparams(xd); jl_value_t **newparams; JL_GC_PUSHARGS(newparams, np); @@ -3285,7 +3311,7 @@ static int might_intersect_concrete(jl_value_t *a) return might_intersect_concrete(((jl_uniontype_t*)a)->a) || might_intersect_concrete(((jl_uniontype_t*)a)->b); if (jl_is_vararg_type(a)) - return might_intersect_concrete(jl_tparam0(a)); + return might_intersect_concrete(jl_unwrap_vararg(a)); if (jl_is_type_type(a)) return 1; if (jl_is_datatype(a)) { @@ -3484,10 +3510,10 @@ static jl_value_t *nth_tuple_elt(jl_datatype_t *t JL_PROPAGATES_ROOT, size_t i) return jl_tparam(t, i); jl_value_t *last = jl_unwrap_unionall(jl_tparam(t, len-1)); if (jl_is_vararg_type(last)) { - jl_value_t *n = jl_tparam1(last); + jl_value_t *n = jl_unwrap_vararg_num(last); if (jl_is_long(n) && i >= len-1+jl_unbox_long(n)) return NULL; - return jl_tparam0(last); + return jl_unwrap_vararg(last); } if (i == len-1) return jl_tparam(t, i); @@ -3590,7 +3616,7 @@ static int args_morespecific_fix1(jl_value_t *a, jl_value_t *b, int swap, jl_typ return -1; assert(jl_is_va_tuple((jl_datatype_t*)a)); jl_datatype_t *new_a = NULL; - jl_value_t *e[2] = { jl_tparam1(jl_unwrap_unionall(jl_tparam(a, n-1))), jl_box_long(taillen) }; + jl_value_t *e[2] = { jl_unwrap_vararg_num(jl_unwrap_unionall(jl_tparam(a, n-1))), jl_box_long(taillen) }; JL_GC_PUSH2(&new_a, &e[1]); new_a = (jl_datatype_t*)jl_instantiate_type_with((jl_value_t*)a, e, 1); int changed = 0; @@ -3627,6 +3653,10 @@ static int count_occurs(jl_value_t *t, jl_tvar_t *v) return 0; return count_occurs(((jl_unionall_t*)t)->body, v); } + if (jl_is_vararg_marker(t)) { + jl_vararg_marker_t *vm = (jl_vararg_marker_t*)t; + return count_occurs(vm->T, v) + count_occurs(vm->N, v); + } if (jl_is_datatype(t)) { int i, c=0; for(i=0; i < jl_nparams(t); i++) diff --git a/src/typemap.c b/src/typemap.c index 347b2147d9aea..74d36fa15c0f5 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -28,7 +28,7 @@ static jl_value_t *jl_type_extract_name(jl_value_t *t1 JL_PROPAGATES_ROOT) if (jl_is_unionall(t1)) t1 = jl_unwrap_unionall(t1); if (jl_is_vararg_type(t1)) { - return jl_type_extract_name(jl_tparam0(t1)); + return jl_type_extract_name(jl_unwrap_vararg(t1)); } else if (jl_is_typevar(t1)) { return jl_type_extract_name(((jl_tvar_t*)t1)->ub); @@ -58,7 +58,7 @@ static int jl_type_extract_name_precise(jl_value_t *t1, int invariant) if (jl_is_unionall(t1)) t1 = jl_unwrap_unionall(t1); if (jl_is_vararg_type(t1)) { - return jl_type_extract_name_precise(jl_tparam0(t1), invariant); + return jl_type_extract_name_precise(jl_unwrap_vararg(t1), invariant); } else if (jl_is_typevar(t1)) { return jl_type_extract_name_precise(((jl_tvar_t*)t1)->ub, 0); @@ -144,7 +144,7 @@ static int sig_match_by_type_simple(jl_value_t **types, size_t n, jl_tupletype_t if (n - i != jl_unbox_long(jl_tparam1(decl))) return 0; } - jl_value_t *t = jl_tparam0(decl); + jl_value_t *t = jl_unwrap_vararg(decl); if (jl_is_typevar(t)) t = ((jl_tvar_t*)t)->ub; for (; i < n; i++) { diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 1bee17d6e9891..d3d00a359e299 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -1577,7 +1577,7 @@ function constrains_param(var::TypeVar, @nospecialize(typ), covariant::Bool) end lastp = typ.parameters[fc] vararg = Base.unwrap_unionall(lastp) - if vararg isa DataType && vararg.name === Base._va_typename + if vararg isa Core.VarargMarker N = vararg.parameters[2] constrains_param(var, N, covariant) && return true # T = vararg.parameters[1] doesn't constrain var