diff --git a/NEWS.md b/NEWS.md index 3c10d3663c554..13ef0bcc95c2d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -29,6 +29,9 @@ New language features and macros in packages and user code ([#8791]). Type `?@doc` at the repl to see the current syntax and more information. + * Varargs functions may now declare the varargs length as `x::Vararg{T,N}` to + restrict dispatch. + Language changes ---------------- diff --git a/base/base.jl b/base/base.jl index e276d68b05351..7035fe7579593 100644 --- a/base/base.jl +++ b/base/base.jl @@ -35,6 +35,8 @@ call(T::Type{WeakRef}, v::ANY) = Core.call(T, v) call{T}(::Type{T}, args...) = convert(T, args...)::T +typealias NTuple{N,T} Tuple{Vararg{T,N}} + convert{T}(::Type{T}, x::T) = x convert(::Type{Tuple{}}, ::Tuple{}) = () diff --git a/base/boot.jl b/base/boot.jl index e7a851f99327c..25354dab0c1e6 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -3,7 +3,7 @@ #abstract Any <: Any #abstract Type{T} -#abstract Vararg{T} +#abstract Vararg{T,N} #Tuple = (Any...) #type Symbol diff --git a/base/datafmt.jl b/base/datafmt.jl index 1a0db96a24805..13201fe8c9947 100644 --- a/base/datafmt.jl +++ b/base/datafmt.jl @@ -4,6 +4,7 @@ module DataFmt importall Base import Base: _default_delims, tryparse_internal +import Base: NTuple export countlines, readdlm, readcsv, writedlm, writecsv diff --git a/base/exports.jl b/base/exports.jl index 98d358a445aec..60e886a3f617d 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -70,6 +70,7 @@ export MathConst, Matrix, MergeSort, + NTuple, Nullable, ObjectIdDict, OrdinalRange, diff --git a/base/inference.jl b/base/inference.jl index 08f4348be48c5..ab380557fbc47 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -241,8 +241,8 @@ const getfield_tfunc = function (A, s0, name) if !isa(s,DataType) return Any end - if is(s.name,NTuple.name) - return s.parameters[2] + if s <: Tuple && s.types.length == 1 && isvatuple(s) + return s.types[1].parameters[1] end if s.abstract return Any diff --git a/base/methodshow.jl b/base/methodshow.jl index e101deea92f84..68ac4a5ea2fd3 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -1,6 +1,9 @@ # Method and method-table pretty-printing function argtype_decl(n, t) # -> (argname, argtype) + if isvarargtype(t) + return argtype_decl_vararg(n, t) + end if isa(n,Expr) n = n.args[1] # handle n::T in arg list end @@ -12,14 +15,23 @@ function argtype_decl(n, t) # -> (argname, argtype) if t === Any && !isempty(s) return s, "" end - if isvarargtype(t) + return s, string(t) +end + +function argtype_decl_vararg(n, t) + 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(t.parameters[1], "...") end end - return s, string(t) + # x::Vararg, x::Vararg{T}, or x::Vararg{T,N} declaration + s, length(n.args[2].args) < 4 ? + string("Vararg{", t.parameters[1], "}") : + string("Vararg{", t.parameters[1], ",", t.parameters[2], "}") end function arg_decl_parts(m::Method) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index ce0af0a76199a..1bde236fd7fb4 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -4,6 +4,7 @@ module IteratorsMD import Base: eltype, length, start, done, next, last, getindex, setindex!, linearindexing, min, max, eachindex import Base: simd_outer_range, simd_inner_length, simd_index, @generated import Base: @nref, @ncall, @nif, @nexprs, LinearFast, LinearSlow, to_index +import Base: NTuple export CartesianIndex, CartesianRange diff --git a/base/sparse.jl b/base/sparse.jl index 1c391d58f6840..7a735c4826d3b 100644 --- a/base/sparse.jl +++ b/base/sparse.jl @@ -9,6 +9,7 @@ importall Base.LinAlg import Base.promote_eltype import Base.@get! import Base.Broadcast.eltype_plus, Base.Broadcast.broadcast_shape +import Base.NTuple export AbstractSparseArray, AbstractSparseMatrix, AbstractSparseVector, SparseMatrixCSC, blkdiag, dense, droptol!, dropzeros!, etree, issparse, nnz, nonzeros, nzrange, diff --git a/doc/manual/functions.rst b/doc/manual/functions.rst index 2b5df53df6cf8..d9b2d82a331af 100644 --- a/doc/manual/functions.rst +++ b/doc/manual/functions.rst @@ -341,6 +341,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 6baf6b522e6ee..37289a5398825 100644 --- a/doc/manual/methods.rst +++ b/doc/manual/methods.rst @@ -523,6 +523,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. + Note on Optional and keyword Arguments -------------------------------------- diff --git a/src/alloc.c b/src/alloc.c index c381fb6bed204..f0b0fd446748e 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -106,6 +106,7 @@ 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); @@ -119,6 +120,7 @@ static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) memcpy(jl_data_ptr(v), data, nb); return v; } + */ assert(jl_is_datatype(dt)); jl_datatype_t *bt = (jl_datatype_t*)dt; diff --git a/src/builtins.c b/src/builtins.c index 36fa92d4ee208..a20253197db8a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1263,7 +1263,7 @@ 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("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); @@ -1617,6 +1617,171 @@ DLLEXPORT void jl_(void *jl_value) in_jl_--; } +// Useful because the jl_typeof macro isn't available from debugger +void jl_static_show_typeof(JL_STREAM *out, jl_value_t *v) +{ + if (v == NULL) { + jl_printf(out, "#"); + } + else if (jl_typeof(v) == NULL) { + jl_printf(out, ""); + } + else if (jl_astaggedvalue(v)->type_bits < 4096U) { + jl_printf(out, "", (int)jl_astaggedvalue(v)->type_bits); + } + else if (jl_is_lambda_info(v)) { + jl_printf(out, "lambda_info"); + } + else if (jl_is_svec(v)) { + jl_printf(out, "svec"); + } + else if (jl_is_datatype(v)) { + jl_printf(out, "DataType"); + } + else if (jl_is_func(v)) { + jl_printf(out, "#"); + } + else if (jl_typeis(v, jl_intrinsic_type)) { + jl_printf(out, "#"); + } + else if (jl_is_int64(v)) { + jl_printf(out, "Int64"); + } + else if (jl_is_int32(v)) { + jl_printf(out, "Int32"); + } + else if (jl_typeis(v,jl_int16_type)) { + jl_printf(out, "Int16"); + } + else if (jl_typeis(v,jl_int8_type)) { + jl_printf(out, "Int8"); + } + else if (jl_is_uint64(v)) { + jl_printf(out, "UInt64"); + } + else if (jl_is_uint32(v)) { + jl_printf(out, "UInt32"); + } + else if (jl_typeis(v,jl_uint16_type)) { + jl_printf(out, "UInt16"); + } + else if (jl_typeis(v,jl_uint8_type)) { + jl_printf(out, "UInt8"); + } + else if (jl_is_cpointer(v)) { + jl_printf(out, "cpointer"); + } + else if (jl_is_float32(v)) { + jl_printf(out, "Float32"); + } + else if (jl_is_float64(v)) { + jl_printf(out, "Float64"); + } + else if (v == jl_true || v == jl_false) { + jl_printf(out, "jl_boolean"); + } + else if (v == jl_nothing) { + jl_printf(out, "nothing"); + } + else if (jl_is_byte_string(v)) { + jl_printf(out, "ByteString"); + } + else if (jl_is_uniontype(v)) { + jl_printf(out, "Union"); + } + else if (jl_is_typector(v)) { + jl_printf(out, "TypeConstructor"); + } + else if (jl_is_typevar(v)) { + jl_printf(out, "TypeVar"); + } + else if (jl_is_module(v)) { + jl_printf(out, "Module"); + } + else if (jl_is_symbol(v)) { + jl_printf(out, "Symbol"); + } + else if (jl_is_gensym(v)) { + jl_printf(out, "GenSym"); + } + else if (jl_is_symbolnode(v)) { + jl_printf(out, "SymbolNode"); + } + else if (jl_is_globalref(v)) { + jl_printf(out, "GlobalRef"); + } + else if (jl_is_labelnode(v)) { + jl_printf(out, "LabelNode"); + } + else if (jl_is_gotonode(v)) { + jl_printf(out, "GotoNode"); + } + else if (jl_is_quotenode(v)) { + jl_printf(out, "QuoteNode"); + } + else if (jl_is_newvarnode(v)) { + jl_printf(out, "NewVarNode"); + } + else if (jl_is_topnode(v)) { + jl_printf(out, "topnode"); + } + else if (jl_is_linenode(v)) { + jl_printf(out, "LineNode"); + } + else if (jl_is_expr(v)) { + jl_printf(out, "Expr"); + } + else if (jl_is_array(v)) { + jl_printf(out, "Array"); + } + else if (jl_typeis(v,jl_loaderror_type)) { + jl_printf(out, "LoadError"); + } + else if (jl_typeis(v,jl_errorexception_type)) { + jl_printf(out, "ErrorException"); + } + else if (jl_is_datatype(jl_typeof(v))) { + jl_datatype_t *dv = (jl_datatype_t*)jl_typeof(v); + if (dv->name->module != jl_core_module) { + jl_static_show_x(out, (jl_value_t*)dv->name->module, 0); + jl_printf(out, "."); + } + jl_printf(out, "%s", dv->name->name->name); + if (dv->parameters && (jl_value_t*)dv != dv->name->primary) { + size_t j, tlen = jl_nparams(dv); + if (tlen > 0) { + jl_printf(out, "{"); + for (j = 0; j < tlen; j++) { + jl_value_t *p = jl_tparam(dv,j); + jl_static_show_x(out, p, 0); + if (j != tlen-1) + jl_printf(out, ", "); + } + jl_printf(out, "}"); + } + else if (jl_is_tuple_type(dv)) { + jl_printf(out, "{}"); + } + } + } + else { + jl_printf(out, ""); + } +} + +DLLEXPORT void jlt_(void *jl_value) +{ + in_jl_++; + JL_TRY { + (void)jl_static_show_typeof(JL_STDOUT, (jl_value_t*)jl_value); + jl_printf(JL_STDOUT,"\n"); + } + JL_CATCH { + jl_printf(JL_STDOUT, "\n!!! ERROR in jlt_ -- ABORTING !!!\n"); + } + in_jl_--; +} + DLLEXPORT void jl_breakpoint(jl_value_t *v) { // put a breakpoint in you debugger here diff --git a/src/dump.c b/src/dump.c index 1b68a5714add5..7892099174f2d 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1897,7 +1897,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_ntuple_type, */jl_abstractarray_type, jl_densearray_type, jl_box_type, jl_void_type, jl_typector_type, jl_typename_type, jl_task_type, jl_uniontype_type, jl_typetype_type, jl_typetype_tvar, @@ -1910,7 +1910,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_method_type->name, jl_tvar_type->name, - jl_ntuple_type->name, jl_abstractarray_type->name, jl_vararg_type->name, + /*jl_ntuple_type->name, */jl_abstractarray_type->name, jl_vararg_type->name, jl_densearray_type->name, jl_void_type->name, jl_lambda_info_type->name, jl_module_type->name, jl_box_type->name, jl_function_type->name, jl_typector_type->name, jl_intrinsic_type->name, jl_task_type->name, diff --git a/src/gf.c b/src/gf.c index f95f97151af64..7fa488a8d7d6e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -742,7 +742,8 @@ static jl_function_t *cache_method(jl_methtable_t *mt, jl_tupletype_t *type, // 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); for(i=0; i < nspec-1; i++) { @@ -769,7 +770,7 @@ static jl_function_t *cache_method(jl_methtable_t *mt, jl_tupletype_t *type, // 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); @@ -926,6 +927,8 @@ static jl_function_t *cache_method(jl_methtable_t *mt, jl_tupletype_t *type, return newmeth; } +// a holds the argument types, b the signature, tvars the parameters. +// On output, *penv holds (parameter1, value1, ...) pairs from intersection. static jl_value_t *lookup_match(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, jl_svec_t *tvars) { @@ -1058,7 +1061,6 @@ static jl_function_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, } m = m->next; } - if (ti == (jl_value_t*)jl_bottom_type) { if (m != (void*)jl_nothing) { func = m->func; @@ -1174,8 +1176,8 @@ static void check_ambiguous(jl_methlist_t *ml, jl_tupletype_t *type, size_t sl = jl_nparams(sig); // we know !jl_args_morespecific(type, sig) if ((tl==sl || - (tl==sl+1 && jl_is_va_tuple(type)) || - (tl+1==sl && jl_is_va_tuple(sig))) && + (tl==sl+1 && jl_va_tuple_kind(type) == JL_VARARG_UNBOUND) || + (tl+1==sl && jl_va_tuple_kind(sig) == JL_VARARG_UNBOUND)) && !jl_args_morespecific((jl_value_t*)sig, (jl_value_t*)type)) { jl_value_t *isect = jl_type_intersection((jl_value_t*)type, (jl_value_t*)sig); @@ -1259,7 +1261,7 @@ jl_methlist_t *jl_method_list_insert(jl_methlist_t **pml, jl_tupletype_t *type, gc_wb(l, l->sig); l->tvars = tvars; gc_wb(l, l->tvars); - l->va = jl_is_va_tuple(type); + l->va = jl_va_tuple_kind(type) == JL_VARARG_UNBOUND; l->isstaged = isstaged; l->invokes = (struct _jl_methtable_t *)jl_nothing; l->func = method; @@ -1288,7 +1290,7 @@ jl_methlist_t *jl_method_list_insert(jl_methlist_t **pml, jl_tupletype_t *type, jl_set_typeof(newrec, jl_method_type); newrec->sig = type; newrec->tvars = tvars; - newrec->va = jl_is_va_tuple(type); + newrec->va = jl_va_tuple_kind(type) == JL_VARARG_UNBOUND; newrec->isstaged = isstaged; newrec->func = method; newrec->invokes = (struct _jl_methtable_t*)jl_nothing; @@ -1385,7 +1387,7 @@ jl_methlist_t *jl_method_table_insert(jl_methtable_t *mt, jl_tupletype_t *type, } // update max_args 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 70a12ece06f88..5a05948828c7c 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -27,8 +27,8 @@ jl_datatype_t *jl_gensym_type; jl_datatype_t *jl_simplevector_type; jl_typename_t *jl_tuple_typename; jl_tupletype_t *jl_anytuple_type; -jl_datatype_t *jl_ntuple_type; -jl_typename_t *jl_ntuple_typename; + //jl_datatype_t *jl_ntuple_type; + //jl_typename_t *jl_ntuple_typename; jl_datatype_t *jl_vararg_type; jl_datatype_t *jl_tvar_type; jl_datatype_t *jl_uniontype_type; @@ -364,69 +364,194 @@ 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) +/* + +Tuple intersection + +We have two tuples, a and b + +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}} + +Stage 1: parameters for individual tuples +----------------------------------------- +For each tuple these 4 states will be reduced to 2: +- FIXED: tuple of known length. Includes NONE, INT, and any BOUND cases for + which the binding is already determined by the current equality + constraints +- VAR: tuple of unknown length. All UNBOUND and any not-yet-constrained + BOUND tuples + +At the time we compute these quantities, we also compute two notions +of the tuple's length: +- lenr, = "representational length": the number of type parameters +- lenf, = "full length": the length if the tuple were expanded to be of + state NONE +Note that these satisfy the inequality lenf >= lenr-1 (consider +the tuple Tuple{Int,Vararg{Float32,0}} which has lenr=2 and lenf=1). +VAR tuples satisfy lenf == lenr. + +Stage 2: paired length analysis +------------------------------- +The next stage of the algorithm is to 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. + +*/ + +typedef enum { + JL_VARARG_FIXED = 0, + JL_VARARG_VAR = 1 +} JL_VARARG_LENKIND; + +static int recheck_tuple_intersection_flag = 0; + +// Set the parameters for a single tuple +// returns lenf, sets kind and lenkind +static size_t tuple_vararg_params(jl_svec_t *a, cenv_t *eqc, JL_VARARG_KIND *kind, JL_VARARG_LENKIND *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; - } - else { - if (bl == al+1) - return al; - *bot=1; - return 0; + size_t lenr = jl_svec_len(a); + size_t lenf = lenr; + int i; + if (lenr == 0) { + *kind = JL_VARARG_NONE; + *lenkind = JL_VARARG_FIXED; + return lenf; + } + jl_value_t *lastarg = jl_svecref(a, lenr-1); + *lenkind = JL_VARARG_VAR; + *kind = jl_vararg_kind(lastarg); + if (*kind == JL_VARARG_NONE || *kind == JL_VARARG_INT) + *lenkind = JL_VARARG_FIXED; + else if (eqc != NULL) { + jl_value_t *N = jl_tparam1(lastarg); + if (jl_is_typevar(N) && ((jl_tvar_t*)N)->bound) { + // set N from eqc parameters + for (i = 0; i < eqc->n; i+=2) + if (eqc->data[i] == N && jl_is_long(eqc->data[i+1])) { + lenf += jl_unbox_long(eqc->data[i+1])-1; + *lenkind = JL_VARARG_FIXED; + break; + } } } - 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) +jl_datatype_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) { - jl_value_t *env[2]; + if (n == NULL) { + 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); } 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 akind, bkind; + JL_VARARG_LENKIND 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_VARARG_FIXED && blenkind == JL_VARARG_FIXED) { + bottom = alenf != blenf; + n = alenf; + } + else if (alenkind == JL_VARARG_FIXED && blenkind == JL_VARARG_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_VARARG_VAR && blenkind == JL_VARARG_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; + if (akind == JL_VARARG_BOUND || bkind == JL_VARARG_BOUND) + recheck_tuple_intersection_flag = 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=(jl_value_t*)jl_bottom_type; + jl_value_t *be=(jl_value_t*)jl_bottom_type; + jl_value_t *an=NULL, *bn=NULL; int aseq=0, bseq=0; 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_VARARG_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_VARARG_FIXED) { + bn = jl_tparam1(be); + bseq = 1; + } be = jl_tparam0(be); } bi++; @@ -446,8 +571,8 @@ static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, JL_GC_POP(); return (jl_value_t*)jl_bottom_type; } - if (aseq && bseq) - ce = (jl_value_t*)jl_wrap_vararg(ce); + if (aseq && bseq) // && ci == n-1 && alenkind != JL_VARARG_FIXED && blenkind != JL_VARARG_FIXED) + ce = (jl_value_t*)jl_wrap_vararg(ce, akind==JL_VARARG_BOUND ? bn : an); jl_svecset(tc, ci, ce); } done_intersect_tuple: @@ -465,6 +590,8 @@ 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 (0) {} + /* if (a->name == jl_ntuple_typename) { assert(jl_svec_len(p) == 2); // NOTE: tuples are covariant, so NTuple element type is too @@ -478,6 +605,7 @@ static jl_value_t *intersect_tag(jl_datatype_t *a, jl_datatype_t *b, } jl_svecset(p, 1, ti); } + */ else { for(i=0; i < jl_svec_len(p); i++) { jl_value_t *ap = jl_svecref(a->parameters,i); @@ -771,8 +899,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_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, cenv_t *penv, cenv_t *eqc, variance_t var) { @@ -813,8 +939,9 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, long alen = (long)jl_nparams(a); jl_value_t *temp=NULL; JL_GC_PUSH2(&b, &temp); + /* if (jl_is_ntuple_type(b)) { - has_ntuple_intersect_tuple = 1; + recheck_tuple_intersection_flag = 1; jl_value_t *lenvar = jl_tparam0(b); jl_value_t *elty = jl_tparam1(b); int i; @@ -874,6 +1001,7 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, } } } + */ if (jl_is_type_type(b)) { jl_value_t *btp0v = jl_tparam0(b); if (jl_is_typevar(btp0v)) { @@ -894,11 +1022,13 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, if (jl_is_tuple_type(b)) { return jl_type_intersect(b, a, penv,eqc,var); } + /* if (jl_is_ntuple_type(a) && 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; @@ -1352,7 +1482,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_flag = 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 @@ -1372,7 +1502,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_flag) { for(e=0; e < eqc.n; e+=2) { jl_value_t *val = eqc.data[e+1]; if (jl_is_long(val)) { @@ -1651,12 +1781,14 @@ 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) && jl_is_long(params[0])) { size_t nt = jl_unbox_long(params[0]); return (jl_value_t*)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); @@ -2254,16 +2386,33 @@ static int jl_tuple_subtype_(jl_value_t **child, size_t cl, { size_t pl = jl_nparams(pdt); jl_value_t **parent = jl_svec_data(pdt->parameters); - size_t ci=0, pi=0; + size_t ci=0, pi=0, cseqpi=0, pseqci=0; + int cseq=0, pseq=0; while (1) { - int cseq = !ta && (ci= cl) - return pi>=pl || (pseq && !invariant); - if (pi >= pl) + if (ci >= cl) { + if (pi >= pl) + return 1; + if (!(pseq && !invariant)) + return 0; + JL_VARARG_KIND vakind = jl_vararg_kind(parent[pi]); + if (vakind == JL_VARARG_UNBOUND || (vakind == JL_VARARG_BOUND && pl==cl+1)) + return 1; + return (vakind == JL_VARARG_INT && + ci-pseqci == jl_unbox_long(jl_tparam1(parent[pi]))); + } + if (pi >= pl) { + if (cseq && jl_vararg_kind(child[ci])==JL_VARARG_INT) + return pi-pseqci == jl_unbox_long(jl_tparam1(child[ci])); return 0; + } jl_value_t *ce = child[ci]; jl_value_t *pe = parent[pi]; if (cseq) ce = jl_tparam0(ce); @@ -2272,7 +2421,23 @@ static int jl_tuple_subtype_(jl_value_t **child, size_t cl, if (!jl_subtype_le(ce, pe, ta, invariant)) return 0; - if (cseq && pseq) return 1; + if (cseq && pseq) { + ce = child[ci]; + pe = parent[pi]; + JL_VARARG_KIND cvakind = jl_vararg_kind(ce); + JL_VARARG_KIND pvakind = jl_vararg_kind(pe); + return ((cvakind == JL_VARARG_UNBOUND && + pvakind == JL_VARARG_UNBOUND) || + (cvakind == JL_VARARG_INT && + pvakind == JL_VARARG_UNBOUND && + cseqpi+jl_unbox_long(jl_tparam1(ce)) == pi) || + (cvakind == JL_VARARG_INT && + pvakind == JL_VARARG_INT && + ci-cseqpi+jl_unbox_long(jl_tparam1(ce)) == pi-pseqci+jl_unbox_long(jl_tparam1(pe))) || + (cvakind == JL_VARARG_BOUND && + pvakind == JL_VARARG_BOUND && + pi == pseqci && jl_tparam1(ce) == jl_tparam1(pe))); + } if (!cseq) ci++; if (!pseq) pi++; } @@ -2358,6 +2523,7 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) 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)) { @@ -2369,6 +2535,7 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) } return 0; } + */ } if (jl_is_tuple_type(b)) { return jl_tuple_subtype_(jl_svec_data(((jl_datatype_t*)a)->parameters), jl_nparams(a), @@ -2380,6 +2547,7 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, 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} @@ -2388,6 +2556,7 @@ static int jl_subtype_le(jl_value_t *a, jl_value_t *b, int ta, int invariant) return jl_subtype_le(ntp, jl_tparam0(jl_tparam0(b)), 0, invariant); } } + */ return 0; } @@ -2398,10 +2567,12 @@ 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; @@ -2562,10 +2733,12 @@ 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); } @@ -2626,6 +2799,7 @@ 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] @@ -2634,6 +2808,7 @@ static int jl_type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant) return jl_type_morespecific_(ntp, jl_tparam0(jl_tparam0(b)), invariant); } } + */ return 0; } @@ -2648,10 +2823,12 @@ 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; @@ -2897,7 +3074,18 @@ static jl_value_t *type_match_(jl_value_t *child, jl_value_t *parent, return jl_false; } + if (jl_is_tuple_type(child) && jl_nparams(child) == 1 && + jl_is_vararg_type(jl_tparam(child,0)) && + jl_is_tuple_type(parent) && jl_nparams(parent) == 1 && + jl_is_vararg_type(jl_tparam(parent,0))) { + // We're matching Tuple{Vararg{S,M}} against Tuple{Vararg{T,N}} + // Use the DataType rules rather than the tuple rules + child = jl_tparam(child, 0); + parent = jl_tparam(parent,0); + } + 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; @@ -2925,6 +3113,7 @@ static jl_value_t *type_match_(jl_value_t *child, jl_value_t *parent, JL_GC_POP(); return tmp; } + */ if (jl_is_tuple_type(parent)) { return tuple_match((jl_datatype_t*)child, (jl_datatype_t*)parent, env, @@ -2933,6 +3122,7 @@ 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] @@ -2941,6 +3131,7 @@ static jl_value_t *type_match_(jl_value_t *child, jl_value_t *parent, return type_match_(ntp, jl_tparam0(jl_tparam0(parent)), env, morespecific, invariant); } } + */ return jl_false; } @@ -3156,15 +3347,15 @@ void jl_init_types(void) 0, 0, 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); + tv = jl_svec2(tvar("T"), tvar("N")); vararg_sym = jl_symbol("Vararg"); + 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->types = jl_anytuple_type->parameters; jl_anytuple_type->nfields = 1; @@ -3172,10 +3363,12 @@ 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); jl_emptytuple = ((jl_datatype_t*)empty_tuple_type)->instance; diff --git a/src/julia.h b/src/julia.h index ce7565dc151d8..335c721016919 100644 --- a/src/julia.h +++ b/src/julia.h @@ -368,8 +368,8 @@ extern DLLEXPORT jl_datatype_t *jl_simplevector_type; extern DLLEXPORT jl_typename_t *jl_tuple_typename; extern DLLEXPORT jl_datatype_t *jl_anytuple_type; #define jl_tuple_type jl_anytuple_type -extern DLLEXPORT jl_datatype_t *jl_ntuple_type; -extern DLLEXPORT jl_typename_t *jl_ntuple_typename; + //extern DLLEXPORT jl_datatype_t *jl_ntuple_type; + //extern DLLEXPORT jl_typename_t *jl_ntuple_typename; extern DLLEXPORT jl_datatype_t *jl_vararg_type; extern DLLEXPORT jl_datatype_t *jl_tvar_type; extern DLLEXPORT jl_datatype_t *jl_task_type; @@ -857,23 +857,13 @@ STATIC_INLINE int jl_is_tuple_type(void *t) ((jl_datatype_t*)(t))->name == jl_tuple_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) { @@ -920,7 +910,7 @@ DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super, DLLEXPORT jl_datatype_t *jl_new_bitstype(jl_value_t *name, jl_datatype_t *super, jl_svec_t *parameters, size_t nbits); 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); // constructors DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *bt, void *data); @@ -999,6 +989,46 @@ DLLEXPORT ssize_t jl_unbox_gensym(jl_value_t *v); #define jl_long_type jl_int32_type #endif +typedef enum { + JL_VARARG_NONE = 0, + JL_VARARG_INT = 1, + JL_VARARG_BOUND = 2, + JL_VARARG_UNBOUND = 3 +} JL_VARARG_KIND; + +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 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; + assert(jl_is_typevar(lenv)); + return ((jl_tvar_t*)lenv)->bound ? JL_VARARG_BOUND : 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 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 DLLEXPORT int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err); DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i); diff --git a/test/core.jl b/test/core.jl index d86aa5773e4ac..0a7303ee7a7ac 100644 --- a/test/core.jl +++ b/test/core.jl @@ -66,6 +66,7 @@ let T = TypeVar(:T,true) @test typeintersect(Type{Array{T}}, Type{AbstractArray{T}}) === Bottom + @test typeintersect(Tuple{Vararg{T}},Tuple{Float64,Int}) === Bottom @test typeintersect(Type{Tuple{Bool,Vararg{Int}}}, Type{Tuple{Vararg{T}}}) === Bottom @test typeintersect(Type{Tuple{Bool,Vararg{Int}}}, Type{Tuple{T,Vararg{T}}}) === Bottom @@ -112,6 +113,25 @@ end let T = TypeVar(:T,Union(Float32,Float64),true) @test typeintersect(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)}) @test !isa(DataType,Type{TypeVar(:T,Number)}) @@ -132,7 +152,6 @@ 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(Type{Tuple{Void}}, Tuple{Type{Void}}) @@ -518,15 +537,27 @@ begin @test firstlast(Val{false}) == "Last" end -# x::Vararg{Any} declarations +# x::Vararg{T,N} declarations begin - local f1, f2, f3 + local f1, f2, f3, f4, f5, f6 f1(x...) = [x...] f2(x::Vararg{Any}) = [x...] f3(x::Vararg) = [x...] + f4(x::Vararg{Any,3}) = [x...] @test f1(1,2,3) == [1,2,3] @test f2(1,2,3) == [1,2,3] @test f3(1,2,3) == [1,2,3] + @test_throws MethodError f4(1,2) + @test f4(1,2,3) == [1,2,3] + @test_throws MethodError f4(1,2,3,4) + f5{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Int,N}) = [indexes...] + @test_throws MethodError f5(zeros(2,2), 1) + @test_throws MethodError f5(zeros(2,2), 1.0, 2.0) + @test f5(zeros(2,2), 1, 2) == [1,2] + @test_throws MethodError f5(zeros(2,2), 1, 2, 3) + f6{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Number,N}) = [indexes...] + @test f6(zeros(2,2), 1.0, 2) == [1.0,2.0] + @test_throws MethodError f6(zeros(2,2), 1, 2:3) end # try/finally diff --git a/test/show.jl b/test/show.jl index b9f576380a8b0..9097e2beb98ef 100644 --- a/test/show.jl +++ b/test/show.jl @@ -221,3 +221,28 @@ end # issue #9865 @test ismatch(r"^Set\(\[.+….+\]\)$", replstr(Set(1:100))) + +# Vararg methods in method tables +function test_mt(f, str) + mt = methods(f) + @test length(mt) == 1 + defs = mt.defs + io = IOBuffer() + show(io, defs) + strio = takebuf_string(io) + @test strio[1:length(str)] == str +end +begin + local f1, f2, f3, f4, f5 + f1(x...) = [x...] + f2(x::Vararg{Any}) = [x...] + f3(x::Vararg) = [x...] + f4(x::Vararg{Any,3}) = [x...] + f5{T,N}(A::AbstractArray{T,N}, indexes::Vararg{Int,N}) = [indexes...] + test_mt(f1, "f1(x...)") + test_mt(f2, "f2(x::Vararg{Any})") + test_mt(f3, "f3(x::Vararg{T})") # FIXME? better as x::Vararg? + test_mt(f4, "f4(x::Vararg{Any,3})") + intstr = string(Int) + test_mt(f5, "f5{T,N}(A::AbstractArray{T,N},indexes::Vararg{$intstr,N})") +end