diff --git a/NEWS.md b/NEWS.md index d77efdbd58b36..d05757004ca62 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,9 @@ New language features However note that the argument types refer to the syntax tree representation, and not to the types of run time values. + * Varargs functions like `foo{T}(x::T...)` may now restrict the number + of such arguments using `foo{T,N}(x::Vararg{T,N})` ([#11242]). + * `x ∈ X` is now a synonym for `x in X` in `for` loops and comprehensions, as it already was in comparisons ([#13824]). @@ -42,6 +45,9 @@ Language changes * The `if` keyword cannot be followed immediately by a line break ([#15763]). + * The built-in `NTuple` type has been removed; `NTuple{N,T}` is now + implemented internally as `Tuple{Vararg{T,N}}` ([#11242]). + Command-line option changes --------------------------- @@ -175,11 +181,13 @@ Deprecated or removed [#4163]: https://github.com/JuliaLang/julia/issues/4163 [#4211]: https://github.com/JuliaLang/julia/issues/4211 +[#6190]: https://github.com/JuliaLang/julia/issues/6190 [#8036]: https://github.com/JuliaLang/julia/issues/8036 [#8846]: https://github.com/JuliaLang/julia/issues/8846 [#9503]: https://github.com/JuliaLang/julia/issues/9503 [#9627]: https://github.com/JuliaLang/julia/issues/9627 [#11196]: https://github.com/JuliaLang/julia/issues/11196 +[#11242]: https://github.com/JuliaLang/julia/issues/11242 [#13062]: https://github.com/JuliaLang/julia/issues/13062 [#13232]: https://github.com/JuliaLang/julia/issues/13232 [#13338]: https://github.com/JuliaLang/julia/issues/13338 @@ -211,4 +219,3 @@ Deprecated or removed [#15550]: https://github.com/JuliaLang/julia/issues/15550 [#15609]: https://github.com/JuliaLang/julia/issues/15609 [#15763]: https://github.com/JuliaLang/julia/issues/15763 -[#6190]: https://github.com/JuliaLang/julia/issues/6190 diff --git a/base/boot.jl b/base/boot.jl index ade257c92f727..92adfcbe58901 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -293,6 +293,8 @@ convert{T}(::Type{T}, x::T) = x cconvert{T}(::Type{T}, x) = convert(T, x) unsafe_convert{T}(::Type{T}, x::T) = x +typealias NTuple{N,T} Tuple{Vararg{T,N}} + # primitive array constructors (::Type{Array{T,N}}){T,N}(d::NTuple{N,Int}) = ccall(:jl_new_array, Array{T,N}, (Any,Any), Array{T,N}, d) diff --git a/base/exports.jl b/base/exports.jl index 39f6a11853d5d..b66ab814bb997 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -76,6 +76,7 @@ export Irrational, Matrix, MergeSort, + NTuple, Nullable, ObjectIdDict, OrdinalRange, diff --git a/base/inference.jl b/base/inference.jl index 5810ecad53db0..98739134b0b76 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -202,7 +202,7 @@ function istopfunction(topmod, f::ANY, sym) return false end -isknownlength(t::DataType) = !isvatuple(t) && !(t.name===NTuple.name && !isa(t.parameters[1],Int)) +isknownlength(t::DataType) = !isvatuple(t) || (length(t.parameters) == 1 && isa(t.parameters[1].parameters[2],Int)) # t[n:end] tupletype_tail(t::ANY, n) = Tuple{t.parameters[n:end]...} @@ -303,7 +303,7 @@ function typeof_tfunc(t::ANY) Type{typeof(t)} end elseif isa(t,DataType) - if isleaftype(t) + if isleaftype(t) || isvarargtype(t) Type{t} elseif t === Any DataType @@ -397,7 +397,7 @@ function limit_type_depth(t::ANY, d::Int, cov::Bool, vars) else return t end - if inexact + if inexact && !isvarargtype(R) R = TypeVar(:_,R) push!(vars, R) end @@ -428,9 +428,6 @@ function getfield_tfunc(s0::ANY, name) return reduce(tmerge, Bottom, map(t->getfield_tfunc(t, name)[1], s.types)), false end if isa(s,DataType) - if is(s.name,NTuple.name) - return (name ⊑ Symbol ? Bottom : s.parameters[2]), true - end if s.abstract return Any, false end @@ -501,7 +498,7 @@ function fieldtype_tfunc(s::ANY, name) if is(t,Bottom) return t end - Type{exact || isleaftype(t) || isa(t,TypeVar) ? t : TypeVar(:_, t)} + Type{exact || isleaftype(t) || isa(t,TypeVar) || isvarargtype(t) ? t : TypeVar(:_, t)} end add_tfunc(fieldtype, 2, 2, fieldtype_tfunc) @@ -581,7 +578,7 @@ function apply_type_tfunc(args...) if type_too_complex(appl,0) return Type{TypeVar(:_,headtype)} end - !isa(appl,TypeVar) ? Type{TypeVar(:_,appl)} : Type{appl} + !(isa(appl,TypeVar) || isvarargtype(appl)) ? Type{TypeVar(:_,appl)} : Type{appl} end add_tfunc(apply_type, 1, IInf, apply_type_tfunc) @@ -593,6 +590,9 @@ add_tfunc(apply_type, 1, IInf, apply_type_tfunc) end function invoke_tfunc(f::ANY, types::ANY, argtype::ANY, sv::InferenceState) + if !isleaftype(Type{types}) + return Any + end argtype = typeintersect(types,limit_tuple_type(argtype)) if is(argtype,Bottom) return Bottom @@ -861,7 +861,9 @@ function precise_container_types(args, types, vtypes::VarTable, sv) assert(n == length(types)) result = cell(n) for i = 1:n - ai = args[i]; ti = types[i]; tti = widenconst(ti) + ai = args[i] + ti = types[i] + tti = widenconst(ti) if isa(ai,Expr) && ai.head === :call && (abstract_evals_to_constant(ai.args[1], svec, vtypes, sv) || abstract_evals_to_constant(ai.args[1], tuple, vtypes, sv)) aa = ai.args @@ -873,8 +875,8 @@ function precise_container_types(args, types, vtypes::VarTable, sv) return nothing elseif ti ⊑ Tuple if i == n - if tti.name === NTuple.name - result[i] = Any[Vararg{tti.parameters[2]}] + if isvatuple(tti) && length(tti.parameters) == 1 + result[i] = Any[Vararg{tti.parameters[1].parameters[1]}] else result[i] = tti.parameters end @@ -1121,7 +1123,7 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) # abstract types yield Type{<:T} instead of Type{T}. # this doesn't really model the situation perfectly, but # "isleaftype(inference_stack.types)" should be good enough. - if isa(t,TypeVar) + if isa(t,TypeVar) || isvarargtype(t) t = Type{t} else t = Type{TypeVar(:_,t)} diff --git a/base/methodshow.jl b/base/methodshow.jl index a209eb5bc8f2e..04239c6339805 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -15,17 +15,41 @@ function argtype_decl(env, n, t) # -> (argname, argtype) return s, "" end if isvarargtype(t) - if t.parameters[1] === Any - return string(s, "..."), "" - else - return s, string_with_env(env, t.parameters[1]) * "..." + tt, tn = t.parameters[1], t.parameters[2] + if isa(tn, TypeVar) && !tn.bound + if tt === Any || (isa(tt, TypeVar) && !tt.bound) + return string(s, "..."), "" + else + return s, string_with_env(env, tt) * "..." + end end + return s, string_with_env(env, "Vararg{", tt, ",", tn, "}") elseif t == String return s, "String" end return s, string_with_env(env, t) end +function argtype_decl_vararg(env, n, t) + if isa(n, Expr) + s = string(n.args[1]) + if n.args[2].head == :... + # x... or x::T... declaration + if t.parameters[1] === Any + return string(s, "..."), "" + else + return s, string_with_env(env, t.parameters[1]) * "..." + end + elseif t == String + return s, "String" + end + end + # x::Vararg, x::Vararg{T}, or x::Vararg{T,N} declaration + s, length(n.args[2].args) < 4 ? + string_with_env(env, "Vararg{", t.parameters[1], "}") : + string_with_env(env, "Vararg{", t.parameters[1], ",", t.parameters[2], "}") +end + function arg_decl_parts(m::Method) tv = m.tvars if !isa(tv,SimpleVector) diff --git a/base/promotion.jl b/base/promotion.jl index 1ca5530a7b828..c52285ede9fc0 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -32,38 +32,40 @@ function typejoin(a::ANY, b::ANY) return Any end ap, bp = a.parameters, b.parameters - la = length(ap)::Int; lb = length(bp)::Int - if la==0 || lb==0 + lar = length(ap)::Int; lbr = length(bp)::Int + laf, afixed = full_va_len(ap) + lbf, bfixed = full_va_len(bp) + if lar==0 || lbr==0 return Tuple end - if la < lb - if isvarargtype(ap[la]) - c = cell(la) - c[la] = Vararg{typejoin(ap[la].parameters[1], tailjoin(bp,la))} - n = la-1 + if laf < lbf + if isvarargtype(ap[lar]) && !afixed + c = cell(laf) + c[laf] = Vararg{typejoin(ap[lar].parameters[1], tailjoin(bp,laf))} + n = laf-1 else - c = cell(la+1) - c[la+1] = Vararg{tailjoin(bp,la+1)} - n = la + c = cell(laf+1) + c[laf+1] = Vararg{tailjoin(bp,laf+1)} + n = laf end - elseif lb < la - if isvarargtype(bp[lb]) - c = cell(lb) - c[lb] = Vararg{typejoin(bp[lb].parameters[1], tailjoin(ap,lb))} - n = lb-1 + elseif lbf < laf + if isvarargtype(bp[lbr]) && !bfixed + c = cell(lbf) + c[lbf] = Vararg{typejoin(bp[lbr].parameters[1], tailjoin(ap,lbf))} + n = lbf-1 else - c = cell(lb+1) - c[lb+1] = Vararg{tailjoin(ap,lb+1)} - n = lb + c = cell(lbf+1) + c[lbf+1] = Vararg{tailjoin(ap,lbf+1)} + n = lbf end else - c = cell(la) - n = la + c = cell(laf) + n = laf end for i = 1:n - ai = ap[i]; bi = bp[i] + ai = ap[min(i,lar)]; bi = bp[min(i,lbr)] ci = typejoin(unwrapva(ai),unwrapva(bi)) - c[i] = isvarargtype(ai) || isvarargtype(bi) ? Vararg{ci} : ci + c[i] = i == length(c) && (isvarargtype(ai) || isvarargtype(bi)) ? Vararg{ci} : ci end return Tuple{c...} elseif b <: Tuple @@ -92,8 +94,24 @@ function typejoin(a::ANY, b::ANY) return Any end +# Returns length, isfixed +function full_va_len(p) + isempty(p) && return 0, true + if isvarargtype(p[end]) + N = p[end].parameters[2] + if isa(N, Integer) + return (length(p) + N - 1)::Int, true + end + return length(p)::Int, false + end + return length(p)::Int, true +end + # reduce typejoin over A[i:end] function tailjoin(A, i) + if i > length(A) + return unwrapva(A[end]) + end t = Bottom for j = i:length(A) t = typejoin(t, unwrapva(A[j])) diff --git a/doc/manual/functions.rst b/doc/manual/functions.rst index 9c4e5ef8c7b24..ebef984ac9eb2 100644 --- a/doc/manual/functions.rst +++ b/doc/manual/functions.rst @@ -333,6 +333,8 @@ the zero or more values passed to ``bar`` after its first two arguments: In all these cases, ``x`` is bound to a tuple of the trailing values passed to ``bar``. +It is possible to constrain the number of values passed as a variable argument; this will be discussed later in :ref:`man-vararg-fixedlen`. + On the flip side, it is often handy to "splice" the values contained in an iterable collection into a function call as individual arguments. To do this, one also uses ``...`` but in the function call instead: diff --git a/doc/manual/methods.rst b/doc/manual/methods.rst index f501613d4388a..530d2b48c166d 100644 --- a/doc/manual/methods.rst +++ b/doc/manual/methods.rst @@ -550,6 +550,32 @@ can also constrain type parameters of methods:: The ``same_type_numeric`` function behaves much like the ``same_type`` function defined above, but is only defined for pairs of numbers. +.. _man-vararg-fixedlen: + +Parametrically-constrained Varargs methods +------------------------------------------ + +Function parameters can also be used to constrain the number of arguments that may be supplied to a "varargs" function (:ref:`man-varargs-functions`). The notation ``Vararg{T,N}`` is used to indicate such a constraint. For example: + +.. doctest:: + + julia> bar(a,b,x::Vararg{Any,2}) = (a,b,x) + + julia> bar(1,2,3) + ERROR: MethodError: `bar` has no matching method bar(::Int, ::Int, ::Int) + + julia> bar(1,2,3,4) + (1,2,(3,4)) + + julia> bar(1,2,3,4,5) + ERROR: MethodError: `bar` has no method matching bar(::Int, ::Int, ::Int, ::Int, ::Int) + +More usefully, it is possible to constrain varargs methods by a parameter. For example:: + + function getindex{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Number,N}) + +would be called only when the number of ``indexes`` matches the dimensionality of the array. + .. _man-note-on-optional-and-keyword-arguments: Note on Optional and keyword Arguments diff --git a/src/Makefile b/src/Makefile index 214da1d85a1a7..9589bf3bd5ed9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -24,7 +24,6 @@ FLAGS += -Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fvisibility=hidden override CFLAGS += -Wold-style-definition -Wstrict-prototypes -Wc++-compat endif - SRCS := \ jltypes gf typemap ast builtins module interpreter \ alloc dlload sys init task array dump toplevel jl_uv jlapi signal-handling \ diff --git a/src/alloc.c b/src/alloc.c index cfe11991efda6..9207c37d83ab8 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -110,20 +110,6 @@ typedef struct { // Note that this function updates len static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) { - if (jl_is_ntuple_type(dt)) { - jl_value_t *lenvar = jl_tparam0(dt); - jl_value_t *elty = jl_tparam1(dt); - assert(jl_is_datatype(elty)); - size_t alignment = ((jl_datatype_t*)elty)->alignment; - *len = LLT_ALIGN((*len), alignment); - assert(jl_is_long(lenvar)); - size_t l = jl_unbox_long(lenvar); - size_t nb = l*LLT_ALIGN(jl_datatype_size(elty), alignment); - jl_value_t *v = (jl_value_t*)newobj(dt, NWORDS(nb)); - memcpy(jl_data_ptr(v), data, nb); - return v; - } - assert(jl_is_datatype(dt)); jl_datatype_t *bt = (jl_datatype_t*)dt; size_t nb = jl_datatype_size(bt); diff --git a/src/builtins.c b/src/builtins.c index 542cd80e2a365..71fc4f17eb8f4 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1129,7 +1129,6 @@ void jl_init_primitives(void) add_builtin("TypeName", (jl_value_t*)jl_typename_type); add_builtin("TypeConstructor", (jl_value_t*)jl_typector_type); add_builtin("Tuple", (jl_value_t*)jl_anytuple_type); - add_builtin("NTuple", (jl_value_t*)jl_ntuple_type); add_builtin("Vararg", (jl_value_t*)jl_vararg_type); add_builtin("Type", (jl_value_t*)jl_type_type); add_builtin("DataType", (jl_value_t*)jl_datatype_type); @@ -1580,7 +1579,7 @@ JL_DLLEXPORT void jl_(void *jl_value) JL_DLLEXPORT void jl_breakpoint(jl_value_t *v) { - // put a breakpoint in you debugger here + // put a breakpoint in your debugger here } #ifdef __cplusplus diff --git a/src/dump.c b/src/dump.c index 9a5e30d8156da..4729b8fc5ea95 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2444,7 +2444,7 @@ void jl_init_serializer(void) jl_labelnode_type, jl_linenumbernode_type, jl_gotonode_type, jl_quotenode_type, jl_topnode_type, jl_type_type, jl_bottom_type, jl_ref_type, jl_pointer_type, - jl_vararg_type, jl_ntuple_type, jl_abstractarray_type, + jl_vararg_type, jl_abstractarray_type, jl_densearray_type, jl_void_type, jl_function_type, jl_typector_type, jl_typename_type, jl_builtin_type, jl_task_type, jl_uniontype_type, jl_typetype_type, jl_typetype_tvar, @@ -2458,7 +2458,7 @@ void jl_init_serializer(void) jl_datatype_type->name, jl_uniontype_type->name, jl_array_type->name, jl_expr_type->name, jl_typename_type->name, jl_type_type->name, jl_methtable_type->name, jl_typemap_level_type->name, jl_typemap_entry_type->name, jl_tvar_type->name, - jl_ntuple_type->name, jl_abstractarray_type->name, jl_vararg_type->name, + jl_abstractarray_type->name, jl_vararg_type->name, jl_densearray_type->name, jl_void_type->name, jl_lambda_info_type->name, jl_method_type->name, jl_module_type->name, jl_function_type->name, jl_typedslot_type->name, jl_abstractslot_type->name, jl_slotnumber_type->name, diff --git a/src/gf.c b/src/gf.c index 9fd2e8a7efc17..52d3ab76a0beb 100644 --- a/src/gf.c +++ b/src/gf.c @@ -488,7 +488,8 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union jl_typemap_t *ca // in general, here we want to find the biggest type that's not a // supertype of any other method signatures. so far we are conservative // and the types we find should be bigger. - if (!isstaged && jl_nparams(type) > mt->max_args && jl_is_va_tuple(decl)) { + if (!isstaged && jl_nparams(type) > mt->max_args + && jl_va_tuple_kind(decl) == JL_VARARG_UNBOUND) { size_t nspec = mt->max_args + 2; limited = jl_alloc_svec(nspec); temp3 = (jl_value_t*)limited; @@ -520,7 +521,7 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union jl_typemap_t *ca // avoid Type{Type{...}}... if (jl_is_type_type(lasttype) && jl_is_type_type(jl_tparam0(lasttype))) lasttype = (jl_value_t*)jl_type_type; - jl_svecset(limited, i, jl_wrap_vararg(lasttype)); + jl_svecset(limited, i, jl_wrap_vararg(lasttype, (jl_value_t*)NULL)); } else { jl_value_t *lastdeclt = jl_tparam(decl,jl_nparams(decl)-1); @@ -875,7 +876,7 @@ static void invalidate_conflicting(union jl_typemap_t *pml, jl_value_t *type, jl static void update_max_args(jl_methtable_t *mt, jl_tupletype_t *type) { size_t na = jl_nparams(type); - if (jl_is_va_tuple(type)) + if (jl_va_tuple_kind(type) == JL_VARARG_UNBOUND) na--; if (na > mt->max_args) mt->max_args = na; diff --git a/src/jltypes.c b/src/jltypes.c index ef7e695be7020..b5c08b112926e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -33,8 +33,6 @@ jl_datatype_t *jl_simplevector_type; jl_typename_t *jl_tuple_typename; jl_tupletype_t *jl_anytuple_type; jl_datatype_t *jl_anytuple_type_type; -jl_datatype_t *jl_ntuple_type; -jl_typename_t *jl_ntuple_typename; jl_typename_t *jl_vecelement_typename; jl_datatype_t *jl_vararg_type; jl_datatype_t *jl_tvar_type; @@ -408,69 +406,190 @@ static jl_value_t *intersect_union(jl_uniontype_t *a, jl_value_t *b, return tu; } -// if returns with *bot!=0, then intersection is Union{} -static size_t tuple_intersect_size(jl_svec_t *a, jl_svec_t *b, int *bot) +/* +Simplification of varargs tuple types: + JL_TUPLE_FIXED: tuples of known length (e.g., JL_VARARG_NONE or JL_VARARG_INT) + JL_TUPLE_VAR: tuples of unknown length (e.g., JL_VARARG_BOUND or JL_VARARG_UNBOUND) + +In some cases, JL_VARARG_BOUND tuples get described as JL_TUPLE_FIXED, +if the constraints on length are already known. + +lenr = "representation length" (the number of parameters) +lenf = "full length" (including the Vararg length, if known) + +In general, lenf >= lenr-1. The lower bound is achieved only for a Vararg of length 0. +*/ +typedef enum { + JL_TUPLE_FIXED = 0, + JL_TUPLE_VAR = 1 +} jl_tuple_lenkind_t; + +// Set the parameters for a single tuple +// returns lenf, sets kind and lenkind +static size_t data_vararg_params(jl_value_t **data, size_t lenr, cenv_t *eqc, jl_vararg_kind_t *kind, jl_tuple_lenkind_t *lenkind) { - size_t al = jl_svec_len(a); - size_t bl = jl_svec_len(b); - *bot = 0; - if (al == bl) return al; - if (al > bl) return tuple_intersect_size(b, a, bot); - assert(al < bl); - if (jl_is_vararg_type(jl_svecref(b,bl-1))) { - if (al > 0 && jl_is_vararg_type(jl_svecref(a,al-1))) { - return bl; + size_t lenf = lenr; + int i; + if (lenr == 0) { + *kind = JL_VARARG_NONE; + *lenkind = JL_TUPLE_FIXED; + return lenf; + } + *lenkind = JL_TUPLE_VAR; + *kind = jl_vararg_kind(data[lenr-1]); + if (*kind == JL_VARARG_NONE || *kind == JL_VARARG_INT) + *lenkind = JL_TUPLE_FIXED; + if (*kind == JL_VARARG_INT || *kind == JL_VARARG_BOUND) { + // try to set N from eqc parameters + jl_value_t *N = jl_tparam1(data[lenr-1]); + if (!jl_is_long(N) && eqc != NULL) { + for (i = 0; i < eqc->n; i+=2) + if (eqc->data[i] == N && jl_is_long(eqc->data[i+1])) { + N = eqc->data[i+1]; + break; + } } - else { - if (bl == al+1) - return al; - *bot=1; - return 0; + if (jl_is_long(N)) { + lenf += jl_unbox_long(N)-1; + *lenkind = JL_TUPLE_FIXED; } } - if (al > 0 && jl_is_vararg_type(jl_svecref(a,al-1))) - return bl; - *bot=1; - return 0; + return lenf; } -jl_datatype_t *jl_wrap_vararg(jl_value_t *t) +static size_t tuple_vararg_params(jl_svec_t *a, cenv_t *eqc, jl_vararg_kind_t *kind, jl_tuple_lenkind_t *lenkind) { - jl_value_t *env[2]; + return data_vararg_params(jl_svec_data(a), jl_svec_len(a), eqc, kind, lenkind); +} + +jl_datatype_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) +{ + if (n == NULL) { + if (t == NULL) + return (jl_datatype_t*)jl_instantiate_type_with((jl_value_t*)jl_vararg_type, NULL, 0); + jl_value_t *env[2]; + env[0] = jl_tparam0(jl_vararg_type); + env[1] = t; + return (jl_datatype_t*)jl_instantiate_type_with((jl_value_t*)jl_vararg_type, env, 1); + } + jl_value_t *env[4]; env[0] = jl_tparam0(jl_vararg_type); env[1] = t; - return (jl_datatype_t*)jl_instantiate_type_with((jl_value_t*)jl_vararg_type, env, 1); + env[2] = jl_tparam1(jl_vararg_type); + env[3] = n; + return (jl_datatype_t*)jl_instantiate_type_with((jl_value_t*)jl_vararg_type, env, 2); } +/* +Tuple intersection +Stage 1: compute lengths of each tuple +-------------------------------------- +See above + +Stage 2: paired length analysis +------------------------------- +Check and combine lengths. In cells of the following table, +- row 1 is the criterion that must be satisfied, or Bottom will be returned +- row 2 is the allocated length for the output tuple +- row 3, if present, indicates any additional steps taken at the time + of length computation + + b + FIXED VAR + |---------------------------------------| + | alenf == blenf | alenf+1 >= blenr | + FIXED | alenf | alenf | + | | bind b? | +a |---------------------------------------| + | blenf+1 >= alenr | | + VAR | blenf | max(alenr,blenr) | + | bind a? | flag? | + |---------------------------------------| + +"bind" is performed if the VAR tuple is of state BOUND, using (for +the b BOUND case) N == alenf-blenr+1 for b's length parameter N. + +"flag" is set if at least one of the tuples is of state BOUND. With +this, we signify that the intersection of these tuples is going to +have to be repeated once all lengths are constrained. + +Stage 3: slot type intersection +------------------------------- +Iterate over each slot of the _output_ tuple, intersecting +corresponding pairs of types. Any intersection failure causes Bottom +to be returned, with one exception illustrated by: + typeintersect(Tuple{A, Vararg{B}}, Tuple{A, Vararg{C}}) == Tuple{A} +where typeintersect(B,C) == Bottom. +*/ + +int recheck_tuple_intersection = 0; // "flag" above + static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, cenv_t *penv, cenv_t *eqc, variance_t var) { jl_svec_t *ap = a->parameters, *bp = b->parameters; - size_t al = jl_svec_len(ap), bl = jl_svec_len(bp); - int bot=0; - size_t n = tuple_intersect_size(ap, bp, &bot); - if (bot) return (jl_value_t*)jl_bottom_type; + size_t alenr = jl_svec_len(ap), blenr = jl_svec_len(bp); + size_t alenf, blenf; + jl_vararg_kind_t akind, bkind; + jl_tuple_lenkind_t alenkind, blenkind; + int bottom = 0; + size_t n; + // Stage 1 + alenf = tuple_vararg_params(ap, eqc, &akind, &alenkind); + blenf = tuple_vararg_params(bp, eqc, &bkind, &blenkind); + // Stage 2 + if (alenkind == JL_TUPLE_FIXED && blenkind == JL_TUPLE_FIXED) { + bottom = alenf != blenf; + n = alenf; + } + else if (alenkind == JL_TUPLE_FIXED && blenkind == JL_TUPLE_VAR) { + bottom = alenf+1 < blenf; + n = alenf; + if (bkind == JL_VARARG_BOUND) + extend(jl_tparam1(jl_svecref(bp, blenr-1)), jl_box_long(alenf-blenr+1), eqc); + } + else if (alenkind == JL_TUPLE_VAR && blenkind == JL_TUPLE_FIXED) { + bottom = blenf+1 < alenf; + n = blenf; + if (akind == JL_VARARG_BOUND) + extend(jl_tparam1(jl_svecref(ap, alenr-1)), jl_box_long(blenf-alenr+1), eqc); + } + else { + n = alenr > blenr ? alenr : blenr; + // Do we need to store "at least N" constraints in penv? + // Formerly, typeintersect(Tuple{A,Vararg{B}}, NTuple{N,C}) did that + if (akind == JL_VARARG_BOUND || bkind == JL_VARARG_BOUND) + recheck_tuple_intersection = 1; + } + if (bottom) return (jl_value_t*) jl_bottom_type; if (n == 0) return jl_typeof(jl_emptytuple); jl_svec_t *tc = jl_alloc_svec(n); jl_value_t *result = (jl_value_t*)tc; jl_value_t *ce = NULL; JL_GC_PUSH2(&tc, &ce); size_t ai=0, bi=0, ci; - jl_value_t *ae=NULL, *be=NULL; + jl_value_t *ae=NULL, *be=NULL, *an=NULL, *bn=NULL; int aseq=0, bseq=0; + // Stage 3 for(ci=0; ci < n; ci++) { - if (ai < al) { + if (ai < alenr) { ae = jl_svecref(ap,ai); if (jl_is_vararg_type(ae)) { - aseq=1; + if (alenkind != JL_TUPLE_FIXED) { + an = jl_tparam1(ae); + aseq = 1; + } ae = jl_tparam0(ae); } ai++; } - if (bi < bl) { + if (bi < blenr) { be = jl_svecref(bp,bi); if (jl_is_vararg_type(be)) { - bseq=1; + if (blenkind != JL_TUPLE_FIXED) { + bn = jl_tparam1(be); + bseq=1; + } be = jl_tparam0(be); } bi++; @@ -480,6 +599,8 @@ static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, if (ce == (jl_value_t*)jl_bottom_type) { if (var!=invariant && aseq && bseq) { // (X∩Y)==∅ → (X...)∩(Y...) == () + // We don't need to set bindings here because + // recheck_tuple_intersection=1 if (n == 1) { JL_GC_POP(); return (jl_value_t*)jl_typeof(jl_emptytuple); @@ -491,7 +612,7 @@ static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, return (jl_value_t*)jl_bottom_type; } if (aseq && bseq) - ce = (jl_value_t*)jl_wrap_vararg(ce); + ce = (jl_value_t*)jl_wrap_vararg(ce, akind==JL_VARARG_BOUND ? bn : an); jl_svecset(tc, ci, ce); } done_intersect_tuple: @@ -509,84 +630,69 @@ static jl_value_t *intersect_tag(jl_datatype_t *a, jl_datatype_t *b, JL_GC_PUSH1(&p); jl_value_t *ti; size_t i; - if (a->name == jl_ntuple_typename) { - assert(jl_svec_len(p) == 2); - // NOTE: tuples are covariant, so NTuple element type is too - ti = jl_type_intersect(jl_tparam0(a),jl_tparam0(b),penv,eqc,invariant); - jl_svecset(p, 0, ti); - ti = jl_type_intersect(jl_tparam1(a),jl_tparam1(b),penv,eqc,var); - if (ti==(jl_value_t*)jl_bottom_type || - jl_svecref(p,0)==(jl_value_t*)jl_bottom_type) { - JL_GC_POP(); - return (jl_value_t*)jl_bottom_type; + for(i=0; i < jl_svec_len(p); i++) { + jl_value_t *ap = jl_svecref(a->parameters,i); + jl_value_t *bp = jl_svecref(b->parameters,i); + if (jl_is_typevar(ap)) { + if (var==invariant && jl_is_typevar(bp)) { + if (((jl_tvar_t*)ap)->bound != ((jl_tvar_t*)bp)->bound) { + JL_GC_POP(); + return (jl_value_t*)jl_bottom_type; + } + if ((is_unspec(a) && is_bnd((jl_tvar_t*)bp,penv)) || + (is_bnd((jl_tvar_t*)ap,penv) && is_unspec(b))) { + // Foo{T} and Foo can never be equal since the former + // is always a subtype of the latter + JL_GC_POP(); + return (jl_value_t*)jl_bottom_type; + } + } + ti = jl_type_intersect(ap,bp,penv,eqc,invariant); + if (bp == (jl_value_t*)jl_bottom_type && + !((jl_tvar_t*)ap)->bound) { + // "Union{}" as a type parameter + jl_svecset(p, i, ti); + continue; + } } - jl_svecset(p, 1, ti); - } - else { - for(i=0; i < jl_svec_len(p); i++) { - jl_value_t *ap = jl_svecref(a->parameters,i); - jl_value_t *bp = jl_svecref(b->parameters,i); - if (jl_is_typevar(ap)) { - if (var==invariant && jl_is_typevar(bp)) { - if (((jl_tvar_t*)ap)->bound != ((jl_tvar_t*)bp)->bound) { - JL_GC_POP(); - return (jl_value_t*)jl_bottom_type; - } - if ((is_unspec(a) && is_bnd((jl_tvar_t*)bp,penv)) || - (is_bnd((jl_tvar_t*)ap,penv) && is_unspec(b))) { - // Foo{T} and Foo can never be equal since the former - // is always a subtype of the latter - JL_GC_POP(); - return (jl_value_t*)jl_bottom_type; - } + else if (jl_is_typevar(bp)) { + ti = jl_type_intersect(ap,bp,penv,eqc,invariant); + if (ap == (jl_value_t*)jl_bottom_type && + !((jl_tvar_t*)bp)->bound) { + // "Union{}" as a type parameter + jl_svecset(p, i, ti); + continue; + } + } + else { + int tva = jl_has_typevars_(ap,0); + int tvb = jl_has_typevars_(bp,0); + if (tva || tvb) { + if (jl_subtype_invariant(ap,bp,0) || + jl_subtype_invariant(bp,ap,0)) { + ti = jl_type_intersect(ap,bp,penv,eqc,invariant); } - ti = jl_type_intersect(ap,bp,penv,eqc,invariant); - if (bp == (jl_value_t*)jl_bottom_type && - !((jl_tvar_t*)ap)->bound) { - // "Union{}" as a type parameter - jl_svecset(p, i, ti); - continue; + else { + ti = (jl_value_t*)jl_bottom_type; } } - else if (jl_is_typevar(bp)) { - ti = jl_type_intersect(ap,bp,penv,eqc,invariant); - if (ap == (jl_value_t*)jl_bottom_type && - !((jl_tvar_t*)bp)->bound) { + else if (type_eqv_(ap,bp)) { + ti = ap; + if (ti == (jl_value_t*)jl_bottom_type) { // "Union{}" as a type parameter jl_svecset(p, i, ti); continue; } } else { - int tva = jl_has_typevars_(ap,0); - int tvb = jl_has_typevars_(bp,0); - if (tva || tvb) { - if (jl_subtype_invariant(ap,bp,0) || - jl_subtype_invariant(bp,ap,0)) { - ti = jl_type_intersect(ap,bp,penv,eqc,invariant); - } - else { - ti = (jl_value_t*)jl_bottom_type; - } - } - else if (type_eqv_(ap,bp)) { - ti = ap; - if (ti == (jl_value_t*)jl_bottom_type) { - // "Union{}" as a type parameter - jl_svecset(p, i, ti); - continue; - } - } - else { - ti = (jl_value_t*)jl_bottom_type; - } - } - if (ti == (jl_value_t*)jl_bottom_type) { - JL_GC_POP(); - return (jl_value_t*)jl_bottom_type; + ti = (jl_value_t*)jl_bottom_type; } - jl_svecset(p, i, ti); } + if (ti == (jl_value_t*)jl_bottom_type) { + JL_GC_POP(); + return (jl_value_t*)jl_bottom_type; + } + jl_svecset(p, i, ti); } if (a->name->primary != NULL) { jl_value_t *res = (jl_value_t*)jl_apply_type(a->name->primary, p); @@ -804,10 +910,6 @@ static jl_value_t *approxify_type(jl_datatype_t *dt, jl_svec_t *pp) return nt; } -static int has_ntuple_intersect_tuple = 0; - -static jl_datatype_t *inst_tupletype_unchecked_uncached(jl_svec_t *p); - static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, cenv_t *penv, cenv_t *eqc, variance_t var) { @@ -845,100 +947,13 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, if (b == (jl_value_t*)jl_any_type || b == jl_ANY_flag) return a; // tuple if (jl_is_tuple_type(a)) { - size_t alen = jl_nparams(a); - jl_value_t *temp=NULL; - JL_GC_PUSH2(&b, &temp); - if (jl_is_ntuple_type(b)) { - has_ntuple_intersect_tuple = 1; - jl_value_t *lenvar = jl_tparam0(b); - jl_value_t *elty = jl_tparam1(b); - int i; - for(i=0; i < eqc->n; i+=2) { - if (eqc->data[i] == lenvar) { - jl_value_t *v = eqc->data[i+1]; - // N is already known in NTuple{N,...} - if (jl_get_size(v, &alen)) break; - } - } - b = (jl_value_t*)jl_tupletype_fill(alen, elty); - if (i >= eqc->n) { - // don't know N yet, so add a constraint for it based on - // the length of the other tuple - if (jl_is_va_tuple((jl_datatype_t*)a)) { - temp = (jl_value_t*)jl_svec_copy(((jl_datatype_t*)b)->parameters); - jl_svecset(temp, alen-1, jl_wrap_vararg(elty)); - b = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)temp); - if (jl_is_typevar(lenvar)) { - // store "at least N" constraints in the <: env - for(i=0; i < penv->n; i+=2) { - if (penv->data[i] == lenvar) { - jl_value_t *v = penv->data[i+1]; - size_t vallen; - if (jl_get_size(v, &vallen)) { - int bot = 0; - long met = meet_tuple_lengths(~vallen, ~(alen-1), &bot); - if (bot) { - JL_GC_POP(); - return (jl_value_t*)jl_bottom_type; - } - penv->data[i+1] = jl_box_long(~met); - break; - } - } - } - if (i >= penv->n) { - temp = jl_box_long(alen-1); - extend(lenvar, temp, penv); - } - } - } - else { - if (jl_is_typevar(lenvar)) { - // store "== N" constraints in the == env - temp = jl_box_long(alen); - if (intersect_typevar((jl_tvar_t*)lenvar,temp,penv,eqc, - invariant) == - (jl_value_t*)jl_bottom_type) { - JL_GC_POP(); - return (jl_value_t*)jl_bottom_type; - } - } - } - } - } - if (jl_is_type_type(b)) { - jl_value_t *btp0v = jl_tparam0(b); - if (jl_is_typevar(btp0v)) { - jl_tvar_t *btp0 = (jl_tvar_t*)btp0v; - if (jl_subtype(btp0->ub, a, 1)) { - JL_GC_POP(); - return b; - } - } - } if (jl_is_tuple_type(b)) { - a = intersect_tuple((jl_datatype_t*)a, (jl_datatype_t*)b, penv,eqc,var); - JL_GC_POP(); - return a; + return intersect_tuple((jl_datatype_t*)a, (jl_datatype_t*)b, penv,eqc,var); } - JL_GC_POP(); } if (jl_is_tuple_type(b)) { return jl_type_intersect(b, a, penv,eqc,var); } - if (jl_is_ntuple_type(a)) { - if (jl_is_ntuple_type(b)) { - jl_value_t *tag = intersect_tag((jl_datatype_t*)a, - (jl_datatype_t*)b, penv, eqc, var); - // The length parameter must be a TypeVar - return tag == jl_bottom_type ? jl_typeof(jl_emptytuple) : tag; - } - else if (jl_is_type_type(b)) { - jl_value_t *temp = a; - a = b; - b = temp; - } - } // tag if (!jl_is_datatype(a) || !jl_is_datatype(b)) return (jl_value_t*)jl_bottom_type; @@ -1035,14 +1050,14 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, // uses to instantiate its supertype. this tells us what subtype parameter // values are implied by the intersected supertype, or that the // intersected supertype cannot come from this subtype (in which case - // our final answer is Union{}). + // our final answer is Union()). size_t i; // hack: we need type_match to find assignments for all typevars int prev_mim = match_intersection_mode; match_intersection_mode = 1; // TODO get rid of these intermediate tuple types - p = (jl_value_t*)inst_tupletype_unchecked_uncached(super->parameters); - temp3 = (jl_value_t*)inst_tupletype_unchecked_uncached(subs_sup_params); + p = (jl_value_t*)jl_apply_tuple_type(super->parameters); + temp3 = (jl_value_t*)jl_apply_tuple_type(subs_sup_params); env = jl_type_match(p, temp3); int sub_needs_parameters = 0; if (env == jl_false) { @@ -1083,7 +1098,7 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, if (jl_svecref(env, e) == tp) { elt = jl_type_intersect(elt, jl_svecref(env, e+1), penv, eqc, invariant); - // note: elt might be Union{} if "Union{}" was the type parameter + // note: elt might be Union() if "Union()" was the type parameter break; } } @@ -1444,7 +1459,7 @@ jl_value_t *jl_type_intersection_matching(jl_value_t *a, jl_value_t *b, jl_value_t **pti = &rts[0]; jl_value_t **extraroot = &rts[1]; - has_ntuple_intersect_tuple = 0; + recheck_tuple_intersection = 0; JL_TRY { // This is kind of awful, but an inner call to instantiate_type // might fail due to a mismatched type parameter. The problem is @@ -1464,7 +1479,7 @@ jl_value_t *jl_type_intersection_matching(jl_value_t *a, jl_value_t *b, int e; - if (has_ntuple_intersect_tuple) { + if (recheck_tuple_intersection) { for(e=0; e < eqc.n; e+=2) { jl_value_t *val = eqc.data[e+1]; if (jl_is_long(val)) @@ -1739,18 +1754,6 @@ jl_value_t *jl_apply_type_(jl_value_t *tc, jl_value_t **params, size_t n) pi); } } - if (tc == (jl_value_t*)jl_ntuple_type && (n == 1 || n == 2)) { - if (!jl_is_typevar(params[0])) { - size_t nt; - if (!jl_get_size(params[0], &nt)) { - // Only allow Int or TypeVar as the first parameter to - // NTuple. issue #9233 - jl_type_error_rt("NTuple", "parameter 1", - (jl_value_t*)jl_long_type, params[0]); - } - return jl_tupletype_fill(nt, (n==2) ? params[1] : (jl_value_t*)jl_any_type); - } - } size_t ntp = jl_svec_len(tp); if (n > ntp) jl_errorf("too many parameters for type %s", tname); @@ -2054,8 +2057,25 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i if (stack_lkup) return stack_lkup; + if (istuple && ntp == 1 && jl_is_vararg_type(iparams[0])) { + // normalize Tuple{Vararg{Int,3}} to Tuple{Int,Int,Int} + jl_datatype_t *va = (jl_datatype_t*)iparams[0]; + if (jl_is_long(jl_tparam1(va))) { + ssize_t nt = jl_unbox_long(jl_tparam1(va)); + if (nt < 0) + jl_errorf("apply_type: Vararg length N is negative: %zd", nt); + if (jl_is_leaf_type(jl_tparam0(va))) + return jl_tupletype_fill(nt, jl_tparam0(va)); + } + } + // always use original type constructor if (!istuple) { + if (jl_is_vararg_type((jl_value_t*)dt) && ntp == 2) { + if (!jl_is_long(iparams[1]) && !jl_is_typevar(iparams[1])) { + jl_type_error_rt("apply_type", "Vararg count", (jl_value_t*)jl_long_type, iparams[1]); + } + } if (tc != (jl_value_t*)dt) return (jl_value_t*)jl_apply_type_(tc, iparams, ntp); } @@ -2191,11 +2211,6 @@ jl_datatype_t *jl_inst_concrete_tupletype_v(jl_value_t **p, size_t np) return (jl_datatype_t*)inst_datatype(jl_anytuple_type, NULL, p, np, 1, 0, NULL, NULL, 0); } -static jl_datatype_t *inst_tupletype_unchecked_uncached(jl_svec_t *p) -{ - return (jl_datatype_t*)inst_datatype(jl_anytuple_type, p, jl_svec_data(p), jl_svec_len(p), 0, 1, NULL, NULL, 0); -} - static jl_svec_t *inst_all(jl_svec_t *p, jl_value_t **env, size_t n, jl_typestack_t *stack, int check) { @@ -2216,6 +2231,32 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_value_t **env, size_t n, jl_datatype_t *tt = (jl_datatype_t*)t; jl_svec_t *tp = tt->parameters; size_t ntp = jl_svec_len(tp); + // Instantiate NTuple{3,Int} + // Note this does not instantiate Tuple{Vararg{Int,3}}; that's done in + // jl_apply_tuple_type_v_ + if (jl_is_va_tuple(tt) && ntp == 1 && n == 2) { + // If this is a Tuple{Vararg{T,N}} with known N, expand it to + // a fixed-length tuple + jl_value_t *T=NULL, *N=NULL; + jl_value_t *ttT = jl_tparam0(jl_tparam0(tt)); + jl_value_t *ttN = jl_tparam1(jl_tparam0(tt)); + int i; + for (i = 0; i < 2*n; i+=2) { + jl_value_t *tv = env[i]; + if (jl_is_typevar(tv)) { + if (tv == ttT) + T = env[i+1]; + else if (tv == ttN) + N = env[i+1]; + } + } + if (T != NULL && N != NULL && jl_is_long(N)) { + 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); + } + } jl_value_t **iparams; int onstack = ntp < jl_page_size/sizeof(jl_value_t*); JL_GC_PUSHARGS(iparams, onstack ? ntp : 1); @@ -2373,34 +2414,66 @@ void jl_reinstantiate_inner_types(jl_datatype_t *t) static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant); -static int jl_tuple_subtype_(jl_value_t **child, size_t cl, +static int jl_tuple_subtype_(jl_value_t **child, size_t clenr, jl_datatype_t *pdt, int ta, int invariant) { - size_t pl = jl_nparams(pdt); + size_t plenr = jl_nparams(pdt); jl_value_t **parent = jl_svec_data(pdt->parameters); + size_t plenf, clenf; + jl_vararg_kind_t ckind, pkind; + jl_tuple_lenkind_t clenkind, plenkind; + int bottom = 0; + // Stage 1 + clenf = data_vararg_params(child, clenr, NULL, &ckind, &clenkind); + plenf = tuple_vararg_params(pdt->parameters, NULL, &pkind, &plenkind); + // Stage 2 + if (clenkind == JL_TUPLE_FIXED && plenkind == JL_TUPLE_FIXED) { + bottom = clenf != plenf; + } + else if (clenkind == JL_TUPLE_FIXED && plenkind == JL_TUPLE_VAR) { + bottom = clenf+1 < plenf; + } + if (plenr == 0 && clenr > 0) + bottom = 1; + if (bottom) return 0; size_t ci=0, pi=0; + jl_value_t *ce=NULL, *pe=NULL; + int cseq=0, pseq=0; + // Stage 3 + int result = 0; while (1) { - int cseq = !ta && (ci= cl) - return pi>=pl || (pseq && !invariant); - if (pi >= pl) - return 0; - jl_value_t *ce = child[ci]; - jl_value_t *pe = parent[pi]; - if (cseq) ce = jl_tparam0(ce); - if (pseq) pe = jl_tparam0(pe); + break; + if (ci >= clenf) { + result = pi >= plenf || pseq; + break; + } + if (pi >= plenf && !pseq) + break; + if (ci < clenr) { + ce = child[ci]; + if (jl_is_vararg_type(ce)) ce = jl_tparam0(ce); + } + if (pi < plenr) { + pe = parent[pi]; + if (jl_is_vararg_type(pe)) pe = jl_tparam0(pe); + } if (!jl_subtype_le(ce, pe, ta, invariant)) - return 0; + break; - if (cseq && pseq) return 1; - if (!cseq) ci++; - if (!pseq) pi++; + if (cseq && pseq) { + result = 1; + break; + } + ci++; + pi++; } - return 0; + return result; } int jl_tuple_subtype(jl_value_t **child, size_t cl, jl_datatype_t *pdt, int ta) @@ -2408,21 +2481,8 @@ int jl_tuple_subtype(jl_value_t **child, size_t cl, jl_datatype_t *pdt, int ta) return jl_tuple_subtype_(child, cl, pdt, ta, 0); } -static int tuple_all_subtype(jl_datatype_t *t, jl_value_t *super, int ta, int invariant) -{ - size_t ci; - for(ci=0; ci < jl_nparams(t); ci++) { - jl_value_t *ce = jl_tparam(t,ci); - if (!ta && jl_is_vararg_type(ce)) - ce = jl_tparam0(ce); - if (!jl_subtype_le(ce, super, ta, invariant)) - return 0; - } - return 1; -} - // ta specifies whether typeof() should be implicitly applied to a. -static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) +int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) { if (!ta&&jl_is_typector(a)) a = (jl_value_t*)((jl_typector_t*)a)->body; if (jl_is_typector(b)) b = (jl_value_t*)((jl_typector_t*)b)->body; @@ -2483,19 +2543,6 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) if (ta) a = (jl_value_t*)jl_typeof(a); if (jl_is_tuple_type(a)) { - if (jl_is_datatype(b)) { - if (((jl_datatype_t*)b)->name == jl_ntuple_typename) { - jl_value_t *tp = jl_tparam1(b); - if (tuple_all_subtype((jl_datatype_t*)a, tp, 0, invariant)) { - if (invariant) { - return (jl_datatype_t*)b != jl_ntuple_type || - jl_subtype_le((jl_value_t*)jl_anytuple_type, a, 0, 1); - } - return 1; - } - return 0; - } - } if (jl_is_tuple_type(b)) { return jl_tuple_subtype_(jl_svec_data(((jl_datatype_t*)a)->parameters), jl_nparams(a), (jl_datatype_t*)b, 0, invariant); @@ -2505,18 +2552,6 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) if (a == b) return 1; if (!invariant && (jl_datatype_t*)b == jl_any_type) return 1; - if (jl_is_tuple_type(b)) { - if (jl_is_datatype(a) && - ((jl_datatype_t*)a)->name == jl_ntuple_typename) { - // only ((T>:S)...,) can be a supertype of NTuple{N,S} - jl_value_t *ntp = jl_tparam1(a); - if (jl_nparams(b) == 1 && jl_is_va_tuple((jl_datatype_t*)b)) { - return jl_subtype_le(ntp, jl_tparam0(jl_tparam0(b)), 0, invariant); - } - } - return 0; - } - if (jl_is_datatype(a) && jl_is_datatype(b)) { if ((jl_datatype_t*)a == jl_any_type) return 0; jl_datatype_t *tta = (jl_datatype_t*)a; @@ -2524,10 +2559,6 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) int super=0; while (tta != (jl_datatype_t*)jl_any_type) { if (tta->name == ttb->name) { - if (tta->name == jl_ntuple_typename) { - // NTuple must be covariant - return jl_subtype_le(jl_tparam(tta,1), jl_tparam(ttb,1), 0, invariant); - } if (super && ttb->name == jl_type_type->name && jl_is_typevar(jl_tparam0(b))) { if (jl_subtype_le(a, jl_tparam0(b), 0, 1)) return 1; @@ -2601,29 +2632,42 @@ static int type_eqv_with_ANY(jl_value_t *a, jl_value_t *b) static int jl_type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant); -static int jl_tuple_morespecific_(jl_datatype_t *cdt, jl_datatype_t *pdt, int invariant) +static int jl_tuple_morespecific(jl_datatype_t *cdt, jl_datatype_t *pdt, int invariant) { - size_t cl = jl_nparams(cdt); + size_t clenr = jl_nparams(cdt); jl_value_t **child = jl_svec_data(cdt->parameters); - size_t pl = jl_nparams(pdt); + size_t plenr = jl_nparams(pdt); jl_value_t **parent = jl_svec_data(pdt->parameters); + size_t plenf, clenf; + jl_vararg_kind_t ckind, pkind; + jl_tuple_lenkind_t clenkind, plenkind; + clenf = tuple_vararg_params(cdt->parameters, NULL, &ckind, &clenkind); + plenf = tuple_vararg_params(pdt->parameters, NULL, &pkind, &plenkind); size_t ci=0, pi=0; + int cseq=0, pseq=0; int some_morespecific = 0; + jl_value_t *ce=NULL, *pe=NULL; while (1) { - int cseq = (ci= cl) + if (!cseq) + cseq = (ci= clenf && !cseq) return 1; - if (pi >= pl) + if (pi >= plenf && !pseq) return some_morespecific; - jl_value_t *ce = child[ci]; - jl_value_t *pe = parent[pi]; - if (cseq) ce = jl_tparam0(ce); - if (pseq) pe = jl_tparam0(pe); + if (ci < clenr) { + ce = child[ci]; + if (jl_is_vararg_type(ce)) ce = jl_tparam0(ce); + } + if (pi < plenr) { + pe = parent[pi]; + if (jl_is_vararg_type(pe)) pe = jl_tparam0(pe); + } if (!jl_type_morespecific_(ce, pe, invariant)) { if (type_eqv_with_ANY(ce,pe)) { - if (ci==cl-1 && pi==pl-1) { + if (ci==clenf-1 && pi==plenf-1) { if (!cseq && pseq) return 1; if (!some_morespecific) @@ -2649,25 +2693,12 @@ static int jl_tuple_morespecific_(jl_datatype_t *cdt, jl_datatype_t *pdt, int in } if (cseq && pseq) return 1; - if (!cseq) ci++; - if (!pseq) pi++; + ci++; + pi++; } return 0; } -static int tuple_all_morespecific(jl_datatype_t *t, jl_value_t *super, int invariant) -{ - size_t ci; - for(ci=0; ci < jl_nparams(t); ci++) { - jl_value_t *ce = jl_tparam(t,ci); - if (jl_is_vararg_type(ce)) - ce = jl_tparam0(ce); - if (!jl_type_morespecific_(ce, super, invariant)) - return 0; - } - return 1; -} - static int partially_morespecific(jl_value_t *a, jl_value_t *b, int invariant) { if (jl_is_uniontype(b)) { @@ -2695,12 +2726,8 @@ static int jl_type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant) } size_t i; if (jl_is_tuple_type(a)) { - if (jl_is_datatype(b) && - ((jl_datatype_t*)b)->name == jl_ntuple_typename) { - return tuple_all_morespecific((jl_datatype_t*)a, jl_tparam(b,1), invariant); - } if (jl_is_tuple_type(b)) { - return jl_tuple_morespecific_((jl_datatype_t*)a, (jl_datatype_t*)b, invariant); + return jl_tuple_morespecific((jl_datatype_t*)a, (jl_datatype_t*)b, invariant); } } @@ -2758,19 +2785,6 @@ static int jl_type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant) if (!invariant && (jl_datatype_t*)b == jl_any_type) return 1; - if (jl_is_tuple_type(b)) { - if (jl_is_datatype(a) && - ((jl_datatype_t*)a)->name == jl_ntuple_typename) { - // only ((T>:S)...,) can be a supertype of NTuple[N,S] - jl_value_t *ntp = jl_tparam(a, 1); - if (jl_nparams(b) == 1 && jl_is_va_tuple((jl_datatype_t*)b)) { - return jl_type_morespecific_(ntp, jl_tparam0(jl_tparam0(b)), invariant); - } - } - if (!jl_is_typevar(a)) - return 0; - } - if (jl_is_datatype(a) && jl_is_datatype(b)) { if ((jl_datatype_t*)a == jl_any_type) return 0; jl_datatype_t *tta = (jl_datatype_t*)a; @@ -2782,10 +2796,6 @@ static int jl_type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant) if (tta->name != jl_type_type->name) return 1; } - if (tta->name == jl_ntuple_typename) { - // NTuple must be covariant - return jl_type_morespecific_(jl_tparam(tta,1), jl_tparam(ttb,1), invariant); - } if (super && ttb->name == jl_type_type->name && jl_is_typevar(jl_tparam0(b))) { if (jl_type_morespecific_(a, jl_tparam0(b), 1)) return 1; @@ -2845,29 +2855,48 @@ static jl_value_t *tuple_match(jl_datatype_t *child, jl_datatype_t *parent, cenv_t *env, int morespecific, int invariant) { size_t ci=0, pi=0; - size_t cl = jl_nparams(child); - size_t pl = jl_nparams(parent); + size_t clenr = jl_nparams(child); + size_t plenr = jl_nparams(parent); + size_t plenf, clenf; + jl_vararg_kind_t ckind, pkind; + jl_tuple_lenkind_t clenkind, plenkind; + clenf = tuple_vararg_params(child->parameters, NULL, &ckind, &clenkind); + plenf = tuple_vararg_params(parent->parameters, NULL, &pkind, &plenkind); + int cseq=0, pseq=0; + jl_value_t *ce=NULL, *pe=NULL, *cn=NULL, *pn=NULL; int mode = 0; invariant = invariant & type_match_invariance_mask; while(1) { - int cseq = (ci= cl) - return (mode || pi>=pl || (pseq && !invariant)) ? jl_true : jl_false; - if (pi >= pl) + if (ci >= clenf && !cseq) + return (mode || pi>=plenf || (pseq && !invariant)) ? jl_true : jl_false; + if (pi >= plenf && !pseq) return mode ? jl_true : jl_false; - jl_value_t *ce = jl_tparam(child,ci); - jl_value_t *pe = jl_tparam(parent,pi); - if (cseq) ce = jl_tparam0(ce); - if (pseq) pe = jl_tparam0(pe); + if (ci < clenr) { + ce = jl_tparam(child,ci); + if (jl_is_vararg_type(ce)) { + cn = jl_tparam1(ce); + ce = jl_tparam0(ce); + } + } + if (pi < plenr) { + pe = jl_tparam(parent,pi); + if (jl_is_vararg_type(pe)) { + pn = jl_tparam1(pe); + pe = jl_tparam0(pe); + } + } int n = env->n; if (type_match_(ce, pe, env, morespecific, invariant) == jl_false) { env->n = n; if (jl_types_equal_generic(ce,pe,1)) { - if (ci==cl-1 && pi==pl-1 && !cseq && pseq) { + if (ci==clenf-1 && pi==plenf-1 && !cseq && pseq) { return jl_true; } if (!mode) return jl_false; @@ -2876,6 +2905,14 @@ static jl_value_t *tuple_match(jl_datatype_t *child, jl_datatype_t *parent, return jl_false; } } + // Match the number parameter in Vararg, too + if (cseq && pseq) { + n = env->n; + if (type_match_(cn, pn, env, morespecific, invariant) == jl_false) { + env->n = n; + return jl_false; + } + } if (mode && cseq && !pseq) return jl_true; @@ -2889,8 +2926,8 @@ static jl_value_t *tuple_match(jl_datatype_t *child, jl_datatype_t *parent, } if (cseq && pseq) return jl_true; - if (!cseq) ci++; - if (!pseq) pi++; + ci++; + pi++; } return jl_false; } @@ -3036,34 +3073,6 @@ static jl_value_t *type_match_(jl_value_t *child, jl_value_t *parent, } if (jl_is_tuple_type(child)) { - if (jl_is_datatype(parent) && - ((jl_datatype_t*)parent)->name == jl_ntuple_typename) { - jl_svec_t *tp = ((jl_datatype_t*)parent)->parameters; - // if child has a sequence type, there exists no N such that - // NTuple[N,Any] could be its supertype. - if (jl_is_va_tuple((jl_datatype_t*)child)) - return jl_false; - jl_value_t *nt_len = jl_svecref(tp,0); - jl_value_t *childlen = jl_box_long(jl_nparams(child)); - if (jl_is_typevar(nt_len)) { - int n = env->n; - if (type_match_(childlen, nt_len, env, morespecific, - invariant) == jl_false) - { env->n = n; return jl_false; } - } - else { - return jl_false; - } - jl_value_t *p_seq = (jl_value_t*)jl_wrap_vararg(jl_svecref(tp,1)); - JL_GC_PUSH1(&p_seq); - p_seq = (jl_value_t*)jl_svec1(p_seq); - p_seq = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)p_seq); - tmp = tuple_match((jl_tupletype_t*)child, (jl_tupletype_t*)p_seq, - env, morespecific, invariant); - JL_GC_POP(); - return tmp; - } - if (jl_is_tuple_type(parent)) { return tuple_match((jl_datatype_t*)child, (jl_datatype_t*)parent, env, morespecific, invariant); @@ -3071,14 +3080,6 @@ static jl_value_t *type_match_(jl_value_t *child, jl_value_t *parent, return jl_false; } if (jl_is_tuple_type(parent)) { - if (jl_is_datatype(child) && - ((jl_datatype_t*)child)->name == jl_ntuple_typename) { - // only ((T>:S)...,) can be a supertype of NTuple[N,S] - jl_value_t *ntp = jl_tparam(child, 1); - if (jl_nparams(parent) == 1 && jl_is_va_tuple((jl_datatype_t*)parent)) { - return type_match_(ntp, jl_tparam0(jl_tparam0(parent)), env, morespecific, invariant); - } - } return jl_false; } @@ -3326,16 +3327,17 @@ void jl_init_types(void) jl_type_type, jl_any_type), 0, 1, 3); - jl_svec_t *tv; - tv = jl_svec1(tvar("T")); - jl_vararg_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Vararg"), jl_any_type, tv); vararg_sym = jl_symbol("Vararg"); + jl_svec_t *tv; + tv = jl_svec2(tvar("T"),tvar("N")); + jl_vararg_type = jl_new_abstracttype((jl_value_t*)vararg_sym, jl_any_type, tv); jl_anytuple_type = jl_new_datatype(jl_symbol("Tuple"), jl_any_type, jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); jl_tuple_typename = jl_anytuple_type->name; jl_anytuple_type->uid = 0; - jl_anytuple_type->parameters = jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type)); + jl_anytuple_type->parameters = jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type, (jl_value_t*)NULL)); + //jl_anytuple_type->parameters = jl_svec(1, jl_wrap_vararg((jl_value_t*)NULL, (jl_value_t*)NULL)); jl_anytuple_type->types = jl_anytuple_type->parameters; jl_anytuple_type->nfields = 1; @@ -3343,11 +3345,6 @@ void jl_init_types(void) (jl_value_t*)jl_bottom_type,(jl_value_t*)jl_any_type); jl_type_type->parameters = jl_svec(1, tttvar); - tv = jl_svec2(tvar("N"), tvar("T")); - jl_ntuple_type = jl_new_abstracttype((jl_value_t*)jl_symbol("NTuple"), - jl_any_type, tv); - jl_ntuple_typename = jl_ntuple_type->name; - jl_tupletype_t *empty_tuple_type = jl_apply_tuple_type(jl_emptysvec); empty_tuple_type->uid = jl_assign_type_uid(); jl_emptytuple = ((jl_datatype_t*)empty_tuple_type)->instance; diff --git a/src/julia.h b/src/julia.h index 8f1d893ed5171..9a55d5dbe2b39 100644 --- a/src/julia.h +++ b/src/julia.h @@ -452,8 +452,6 @@ extern JL_DLLEXPORT jl_typename_t *jl_vecelement_typename; extern JL_DLLEXPORT jl_datatype_t *jl_anytuple_type; #define jl_tuple_type jl_anytuple_type extern JL_DLLEXPORT jl_datatype_t *jl_anytuple_type_type; -extern JL_DLLEXPORT jl_datatype_t *jl_ntuple_type; -extern JL_DLLEXPORT jl_typename_t *jl_ntuple_typename; extern JL_DLLEXPORT jl_datatype_t *jl_vararg_type; extern JL_DLLEXPORT jl_datatype_t *jl_tvar_type; extern JL_DLLEXPORT jl_datatype_t *jl_task_type; @@ -951,24 +949,6 @@ STATIC_INLINE int is_vecelement_type(jl_value_t* t) ((jl_datatype_t*)(t))->name == jl_vecelement_typename); } -STATIC_INLINE int jl_is_vararg_type(jl_value_t *v) -{ - return (jl_is_datatype(v) && - ((jl_datatype_t*)(v))->name == jl_vararg_type->name); -} - -STATIC_INLINE int jl_is_va_tuple(jl_datatype_t *t) -{ - size_t l = jl_svec_len(t->parameters); - return (l>0 && jl_is_vararg_type(jl_tparam(t,l-1))); -} - -STATIC_INLINE int jl_is_ntuple_type(jl_value_t *v) -{ - return (jl_is_datatype(v) && - ((jl_datatype_t*)v)->name == jl_ntuple_typename); -} - STATIC_INLINE int jl_is_type_type(jl_value_t *v) { return (jl_is_datatype(v) && @@ -1085,6 +1065,52 @@ JL_DLLEXPORT int jl_get_size(jl_value_t *val, size_t *pnt); #define jl_long_type jl_int32_type #endif +// Each tuple can exist in one of 4 Vararg states: +// NONE: no vararg Tuple{Int,Float32} +// INT: vararg with integer length Tuple{Int,Vararg{Float32,2}} +// BOUND: vararg with bound TypeVar length Tuple{Int,Vararg{Float32,N}} +// UNBOUND: vararg with unbound length Tuple{Int,Vararg{Float32}} +typedef enum { + JL_VARARG_NONE = 0, + JL_VARARG_INT = 1, + JL_VARARG_BOUND = 2, + JL_VARARG_UNBOUND = 3 +} jl_vararg_kind_t; + +STATIC_INLINE int jl_is_vararg_type(jl_value_t *v) +{ + return (jl_is_datatype(v) && + ((jl_datatype_t*)(v))->name == jl_vararg_type->name); +} + +STATIC_INLINE jl_vararg_kind_t jl_vararg_kind(jl_value_t *v) +{ + if (!jl_is_vararg_type(v)) + return JL_VARARG_NONE; + jl_value_t *lenv = jl_tparam1(v); + if (jl_is_long(lenv)) + return JL_VARARG_INT; + if (jl_is_typevar(lenv)) + return ((jl_tvar_t*)lenv)->bound ? JL_VARARG_BOUND : JL_VARARG_UNBOUND; + return JL_VARARG_UNBOUND; +} + +STATIC_INLINE int jl_is_va_tuple(jl_datatype_t *t) +{ + assert(jl_is_tuple_type(t)); + size_t l = jl_svec_len(t->parameters); + return (l>0 && jl_is_vararg_type(jl_tparam(t,l-1))); +} + +STATIC_INLINE jl_vararg_kind_t jl_va_tuple_kind(jl_datatype_t *t) +{ + assert(jl_is_tuple_type(t)); + size_t l = jl_svec_len(t->parameters); + if (l == 0) + return JL_VARARG_NONE; + return jl_vararg_kind(jl_tparam(t,l-1)); +} + // structs JL_DLLEXPORT int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err); JL_DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i); diff --git a/src/julia_internal.h b/src/julia_internal.h index ec3a40fffa65f..2cc304efc968b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -188,7 +188,7 @@ jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n); jl_datatype_t *jl_new_abstracttype(jl_value_t *name, jl_datatype_t *super, jl_svec_t *parameters); jl_datatype_t *jl_wrap_Type(jl_value_t *t); // x -> Type{x} -jl_datatype_t *jl_wrap_vararg(jl_value_t *t); +jl_datatype_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n); void jl_assign_bits(void *dest, jl_value_t *bits); jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module); diff --git a/test/core.jl b/test/core.jl index 4a1fb33e10f7e..c629ba96aa274 100644 --- a/test/core.jl +++ b/test/core.jl @@ -4,10 +4,17 @@ const Bottom = Union{} -function testintersect(a, b, result, cmp=is) - @test cmp(typeintersect(a, b), result) - @test cmp(typeintersect(b, a), result) +macro testintersect(args...) + _testintersect(args...) end + +function _testintersect(a, b, result, cmp=:is) + quote + @test $(esc(cmp))(typeintersect($(esc(a)), $(esc(b))), $(esc(result))) + @test $(esc(cmp))(typeintersect($(esc(b)), $(esc(a))), $(esc(result))) + end +end + isnot(x,y) = !is(x,y) # basic type relationships @@ -29,97 +36,122 @@ isnot(x,y) = !is(x,y) @test Array{Int8,1} <: Array{Int8,1} @test !(Type{Bottom} <: Type{Int32}) @test !(Vector{Float64} <: Vector{Union{Float64,Float32}}) -testintersect(Vector{Float64}, Vector{Union{Float64,Float32}}, Bottom) +@testintersect(Vector{Float64}, Vector{Union{Float64,Float32}}, Bottom) @test !isa(Array,Type{Any}) @test Type{Complex} <: DataType @test isa(Complex,Type{Complex}) @test !(Type{Ptr{Bottom}} <: Type{Ptr}) @test !(Type{Rational{Int}} <: Type{Rational}) +@test Tuple{} <: Tuple{Vararg} +@test Tuple{} <: NTuple{TypeVar(:N,true)} +@test Type{Tuple{}} <: Type{Tuple{Vararg}} +@test Type{Tuple{}} <: Type{NTuple{TypeVar(:N,true)}} let T = TypeVar(:T,true) - testintersect(Array{Bottom},AbstractArray{T}, Bottom, isnot) - testintersect(Tuple{Type{Ptr{UInt8}},Ptr{Bottom}}, + @testintersect(Array{Bottom},AbstractArray{T}, Bottom, isnot) + @testintersect(Tuple{Type{Ptr{UInt8}},Ptr{Bottom}}, Tuple{Type{Ptr{T}},Ptr{T}}, Bottom) @test !(Type{T} <: TypeVar) - testintersect(Tuple{Range{Int},Tuple{Int,Int}},Tuple{AbstractArray{T},Dims}, + @testintersect(Tuple{Range{Int},Tuple{Int,Int}},Tuple{AbstractArray{T},Dims}, Tuple{Range{Int},Tuple{Int,Int}}) - testintersect(Tuple{T, AbstractArray{T}}, Tuple{Number, Array{Int,1}}, + @testintersect(Tuple{T, AbstractArray{T}}, Tuple{Number, Array{Int,1}}, Tuple{Int, Array{Int,1}}) - testintersect(Tuple{T, AbstractArray{T}}, Tuple{Int, Array{Number,1}}, + @testintersect(Tuple{T, AbstractArray{T}}, Tuple{Int, Array{Number,1}}, Tuple{Int, Array{Number,1}}) - testintersect(Tuple{T, AbstractArray{T}},Tuple{Any, Array{Number,1}}, + @testintersect(Tuple{T, AbstractArray{T}},Tuple{Any, Array{Number,1}}, Tuple{Number, Array{Number,1}}, isequal) - testintersect(Tuple{Array{T}, Array{T}}, Tuple{Array, Array{Any}}, Bottom, isnot) + @testintersect(Tuple{Array{T}, Array{T}}, Tuple{Array, Array{Any}}, Bottom, isnot) f47{T}(x::Vector{Vector{T}}) = 0 @test_throws MethodError f47(Array(Vector,0)) @test f47(Array(Vector{Int},0)) == 0 - testintersect(Tuple{T,T}, Tuple{Union{Float64,Int64},Int64}, Tuple{Int64,Int64}) - testintersect(Tuple{T,T}, Tuple{Int64,Union{Float64,Int64}}, Tuple{Int64,Int64}) + @testintersect(Tuple{T,T}, Tuple{Union{Float64,Int64},Int64}, Tuple{Int64,Int64}) + @testintersect(Tuple{T,T}, Tuple{Int64,Union{Float64,Int64}}, Tuple{Int64,Int64}) TT = TypeVar(:T) S = TypeVar(:S,true); N = TypeVar(:N,true); SN = TypeVar(:S,Number,true) - testintersect(Type{TypeVar(:T,Array{TT,1})},Type{Array{SN,N}}, Type{Array{SN,1}}) + @testintersect(Type{TypeVar(:T,Array{TT,1})},Type{Array{SN,N}}, Type{Array{SN,1}}) # issue #5359 - testintersect(Tuple{Type{Array{T,1}},Array{T,1}}, + @testintersect(Tuple{Type{Array{T,1}},Array{T,1}}, Tuple{Type{AbstractVector},Vector{Int}}, Bottom) # issue #5559 - testintersect(Tuple{Type{Vector{Complex128}}, AbstractVector}, + @testintersect(Tuple{Type{Vector{Complex128}}, AbstractVector}, Tuple{Type{Array{T,N}}, Array{S,N}}, Tuple{Type{Vector{Complex128}},Vector}, isequal) - testintersect(Tuple{Type{Vector{Complex128}}, AbstractArray}, + @testintersect(Tuple{Type{Vector{Complex128}}, AbstractArray}, Tuple{Type{Array{T,N}}, Array{S,N}}, Tuple{Type{Vector{Complex128}},Vector}, isequal) - testintersect(Type{Array{T}}, Type{AbstractArray{T}}, Bottom) + @testintersect(Type{Array{T}}, Type{AbstractArray{T}}, Bottom) - testintersect(Type{Tuple{Bool,Vararg{Int}}}, Type{Tuple{Vararg{T}}}, Bottom) - testintersect(Type{Tuple{Bool,Vararg{Int}}}, Type{Tuple{T,Vararg{T}}}, Bottom) + @testintersect(Type{Tuple{Bool,Vararg{Int}}}, Type{Tuple{Vararg{T}}}, Bottom) + @testintersect(Type{Tuple{Bool,Vararg{Int}}}, Type{Tuple{T,Vararg{T}}}, Bottom) + @testintersect(Tuple{Vararg{T}}, Tuple{Float64,Int}, Bottom) - testintersect(Tuple{Rational{T},T}, Tuple{Rational{Integer},Int}, Tuple{Rational{Integer},Int}) + @testintersect(Tuple{Rational{T},T}, Tuple{Rational{Integer},Int}, Tuple{Rational{Integer},Int}) # issue #1631 - testintersect(Pair{T,Ptr{T}}, Pair{Ptr{S},S}, Bottom) - testintersect(Tuple{T,Ptr{T}}, Tuple{Ptr{S},S}, Bottom) + @testintersect(Pair{T,Ptr{T}}, Pair{Ptr{S},S}, Bottom) + @testintersect(Tuple{T,Ptr{T}}, Tuple{Ptr{S},S}, Bottom) end let N = TypeVar(:N,true) - testintersect(Tuple{NTuple{N,Integer},NTuple{N,Integer}}, + @testintersect(Tuple{NTuple{N,Integer},NTuple{N,Integer}}, Tuple{Tuple{Integer,Integer}, Tuple{Vararg{Integer}}}, Tuple{Tuple{Integer,Integer}, Tuple{Integer,Integer}}) - testintersect(Tuple{NTuple{N,Integer},NTuple{N,Integer}}, + @testintersect(Tuple{NTuple{N,Integer},NTuple{N,Integer}}, Tuple{Tuple{Vararg{Integer}}, Tuple{Integer,Integer}}, Tuple{Tuple{Integer,Integer}, Tuple{Integer,Integer}}) local A = typeintersect(Tuple{NTuple{N,Any},Array{Int,N}}, Tuple{Tuple{Int,Vararg{Int}},Array}) local B = Tuple{Tuple{Int,Vararg{Int}},Array{Int,N}} @test A<:B && B<:A - testintersect(Tuple{NTuple{N,Any},Array{Int,N}}, + @testintersect(Tuple{NTuple{N,Any},Array{Int,N}}, Tuple{Tuple{Int,Vararg{Int}},Array{Int,2}}, Tuple{Tuple{Int,Int}, Array{Int,2}}) end -testintersect(Type{Any},Type{Complex}, Bottom) -testintersect(Type{Any},Type{TypeVar(:T,Real)}, Bottom) +@testintersect(Type{Any},Type{Complex}, Bottom) +@testintersect(Type{Any},Type{TypeVar(:T,Real)}, Bottom) @test !(Type{Array{Integer}} <: Type{AbstractArray{Integer}}) @test !(Type{Array{Integer}} <: Type{Array{TypeVar(:T,Integer)}}) -testintersect(Type{Function},Union,Bottom) -testintersect(Type{Int32}, DataType, Type{Int32}) +@testintersect(Type{Function},Union,Bottom) +@testintersect(Type{Int32}, DataType, Type{Int32}) @test !(Type <: TypeVar) -testintersect(DataType, Type, Bottom, isnot) -testintersect(Union, Type, Bottom, isnot) -testintersect(DataType, Type{Int}, Bottom, isnot) -testintersect(DataType, Type{TypeVar(:T,Int)}, Bottom, isnot) -testintersect(DataType, Type{TypeVar(:T,Integer)}, Bottom, isnot) +@testintersect(DataType, Type, Bottom, isnot) +@testintersect(Union, Type, Bottom, isnot) +@testintersect(DataType, Type{Int}, Bottom, isnot) +@testintersect(DataType, Type{TypeVar(:T,Int)}, Bottom, isnot) +@testintersect(DataType, Type{TypeVar(:T,Integer)}, Bottom, isnot) -testintersect(Tuple{Vararg{Int}}, Tuple{Vararg{Bool}}, Tuple{}) -testintersect(Type{Tuple{Vararg{Int}}}, Type{Tuple{Vararg{Bool}}}, Bottom) -testintersect(Tuple{Bool,Vararg{Int}}, Tuple{Vararg{Bool}}, Tuple{Bool,}) +@testintersect(Tuple{Vararg{Int}}, Tuple{Vararg{Bool}}, Tuple{}) +@testintersect(Type{Tuple{Vararg{Int}}}, Type{Tuple{Vararg{Bool}}}, Bottom) +@testintersect(Tuple{Bool,Vararg{Int}}, Tuple{Vararg{Bool}}, Tuple{Bool,}) let T = TypeVar(:T,Union{Float32,Float64}) - testintersect(AbstractArray, Matrix{T}, Matrix{T}) + @testintersect(AbstractArray, Matrix{T}, Matrix{T}) end let T = TypeVar(:T,Union{Float32,Float64},true) - testintersect(AbstractArray, Matrix{T}, Matrix{T}) + @testintersect(AbstractArray, Matrix{T}, Matrix{T}) +end + +# Vararg{T,N} +let N = TypeVar(:N,true) + @test is(Bottom,typeintersect(Tuple{Array{Int,N},Vararg{Int,N}}, Tuple{Vector{Int},Real,Real,Real})) + @test is(Bottom,typeintersect(Tuple{Vector{Int},Real,Real,Real}, Tuple{Array{Int,N},Vararg{Int,N}})) + @test Tuple{Int,Vararg{Int,2}} == Tuple{Int,Int,Int} + @test Tuple{Int,Vararg{Int,2}} == Tuple{Int,Int,Vararg{Int,1}} + @test Tuple{Int,Vararg{Int,2}} == Tuple{Int,Int,Int,Vararg{Int,0}} + @test !(Tuple{Int,Vararg{Int,2}} <: Tuple{Int,Int,Int,Vararg{Int,1}}) + #@test !(Tuple{Int,Vararg{Int,2}} <: Tuple{Int,Vararg{Int,N}}) + @test Tuple{Int,Vararg{Int,N}} == Tuple{Int,Vararg{Int,N}} + #@test !(Tuple{Int,Vararg{Int,2}} <: Tuple{Int,Int,Vararg{Int}}) + @test typeintersect(Tuple{Array{Int,N},Vararg{Int,N}},Tuple{Array{Int,0}}) == Tuple{Array{Int,0}} + @test typeintersect(Tuple{Array{Int,N},Vararg{Int,N}},Tuple{Array{Int,2}}) == Bottom + + @test typeintersect(Tuple{Int,Vararg{Int,N}}, Tuple{Int,Int,Int,Vararg{Float64}}) == Tuple{Int,Int,Int} + @test typeintersect(Tuple{Int,Vararg{Int,N}}, Tuple{Int,Vararg{Float64}}) == Tuple{Int} + @test typeintersect(Tuple{Array{Int,N},Vararg{Int,N}}, Tuple{Matrix{Int},Int,Int,Vararg{Float64}}) == Tuple{Matrix{Int},Int,Int} + @test typeintersect(Tuple{Array{Int,N},Vararg{Int,N}}, Tuple{Matrix{Int},Int,Vararg{Float64}}) == Bottom end @test isa(Int,Type{TypeVar(:T,Number)}) @@ -139,8 +171,7 @@ end # issue #6561 @test issubtype(Array{Tuple}, Array{NTuple}) @test issubtype(Array{Tuple{Vararg{Any}}}, Array{NTuple}) -@test !issubtype(Array{Tuple{Vararg{Int}}}, Array{NTuple}) -@test !issubtype(Array{Tuple{Int,Int}}, Array{NTuple}) +@test issubtype(Array{Tuple{Vararg}}, Array{NTuple}) @test !issubtype(Type{Tuple{Void}}, Tuple{Type{Void}}) # this is fancy: know that any type T<:Number must be either a DataType or a Union @@ -151,7 +182,7 @@ end # issue #2997 let T = TypeVar(:T,Union{Float64,Array{Float64,1}},true) - testintersect(T,Real,Float64) + @testintersect(T,Real,Float64) end # issue #8652 @@ -231,6 +262,10 @@ let @test typejoin(typejoin(b,c), a) == typejoin(typejoin(b,a), c) == Foo____{Int64} end +# typejoin with Vararg{T,N} +@test is(typejoin(Tuple{Vararg{Int,2}}, Tuple{Int,Int,Int}), Tuple{Int,Int,Vararg{Int}}) +@test is(typejoin(Tuple{Vararg{Int,2}}, Tuple{Vararg{Int}}), Tuple{Vararg{Int}}) + @test promote_type(Bool,Bottom) === Bool # ntuples @@ -617,6 +652,21 @@ let @test is(g(a),a) end +# Method specificity +begin + local f + f{T}(dims::Tuple{}, A::AbstractArray{T,0}) = 1 + f{T,N}(dims::NTuple{N,Int}, A::AbstractArray{T,N}) = 2 + f{T,M,N}(dims::NTuple{M,Int}, A::AbstractArray{T,N}) = 3 + A = zeros(2,2) + @test f((1,2,3), A) == 3 + @test f((1,2), A) == 2 + @test f((), reshape([1])) == 1 + f{T,N}(dims::NTuple{N,Int}, A::AbstractArray{T,N}) = 4 + @test f((1,2), A) == 4 + @test f((1,2,3), A) == 3 +end + # dispatch using Val{T}. See discussion in #9452 for instances vs types let local firstlast @@ -1448,7 +1498,7 @@ abstract IT4805{N, T} let T = TypeVar(:T,Int,true) N = TypeVar(:N,true) - testintersect(Type{IT4805{1,T}}, Type{TypeVar(:_,IT4805{N,Int})}, Bottom, isnot) + @testintersect(Type{IT4805{1,T}}, Type{TypeVar(:_,IT4805{N,Int})}, Bottom, isnot) end let @@ -2054,7 +2104,7 @@ abstract AbstractThing{T,N} type ConcreteThing{T<:AbstractFloat,N} <: AbstractThing{T,N} end -testintersect(AbstractThing{TypeVar(:T,true),2}, ConcreteThing, ConcreteThing{TypeVar(:T,AbstractFloat),2}, isequal) +@testintersect(AbstractThing{TypeVar(:T,true),2}, ConcreteThing, ConcreteThing{TypeVar(:T,AbstractFloat),2}, isequal) # issue #8978 module I8978 @@ -2879,13 +2929,13 @@ end type A11136 end type B11136 end let T = TypeVar(:T, true), TB = TypeVar(:T, B11136, true) - testintersect(Tuple{T, T}, Tuple{A11136, TB}, Bottom) + @testintersect(Tuple{T, T}, Tuple{A11136, TB}, Bottom) end # issue #11367 abstract Foo11367 let T1 = TypeVar(:T1, true), T2 = TypeVar(:T2, Foo11367, true) - testintersect(Tuple{T1, T1}, Tuple{Type{BigInt}, T2}, Bottom) + @testintersect(Tuple{T1, T1}, Tuple{Type{BigInt}, T2}, Bottom) end # issue #11355 @@ -3050,7 +3100,7 @@ let @test false catch err @test isa(err, TypeError) - @test err.func == :NTuple + @test err.func == :apply_type @test err.expected == Int @test err.got == Int end @@ -3060,7 +3110,7 @@ let @test false catch err @test isa(err, TypeError) - @test err.func == :NTuple + @test err.func == :apply_type @test err.expected == Int @test err.got == 0x1 end diff --git a/test/show.jl b/test/show.jl index dacb25bb03e9c..237406a6126a6 100644 --- a/test/show.jl +++ b/test/show.jl @@ -392,6 +392,29 @@ A = reshape(1:16,4,4) @test replstr(UpperTriangular(copy(A))) == "4×4 UpperTriangular{$Int,Array{$Int,2}}:\n 1 5 9 13\n ⋅ 6 10 14\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(LowerTriangular(copy(A))) == "4×4 LowerTriangular{$Int,Array{$Int,2}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n 3 7 11 ⋅\n 4 8 12 16" +# Vararg methods in method tables +function test_mt(f, str) + mt = methods(f) + @test length(mt) == 1 + defs = first(mt) + io = IOBuffer() + show(io, defs) + strio = takebuf_string(io) + strio = split(strio, " at")[1] + @test strio[1:length(str)] == str +end +show_f1(x...) = [x...] +show_f2(x::Vararg{Any}) = [x...] +show_f3(x::Vararg) = [x...] +show_f4(x::Vararg{Any,3}) = [x...] +show_f5{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Int,N}) = [indexes...] +test_mt(show_f1, "show_f1(x...)") +test_mt(show_f2, "show_f2(x...)") +test_mt(show_f3, "show_f3(x...)") +test_mt(show_f4, "show_f4(x::Vararg{Any,3})") +intstr = string(Int) +test_mt(show_f5, "show_f5{T,N}(A::AbstractArray{T,N}, indexes::Vararg{$intstr,N})") + # Issue #15525, printing of vcat @test sprint(show, :([a;])) == ":([a;])" @test sprint(show, :([a;b])) == ":([a;b])"