From 7db82e9e224a03fc4a459a5f0e6b549654ef7dd8 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 27 Apr 2015 20:57:31 -0500 Subject: [PATCH] Fix up subtype and intersection for new Vararg tuples. The block of tests focused on subtype and intersect all pass now. --- base/exports.jl | 1 + src/gf.c | 14 ++-- src/jltypes.c | 177 ++++++++++++++++++++++++++++++++++++++---------- src/julia.h | 54 +++++++++------ test/core.jl | 12 ++++ 5 files changed, 192 insertions(+), 66 deletions(-) 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/src/gf.c b/src/gf.c index 35a5282d958e5..7fa488a8d7d6e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -743,7 +743,7 @@ static jl_function_t *cache_method(jl_methtable_t *mt, jl_tupletype_t *type, // 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_varlen(decl)) { + && 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++) { @@ -770,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); @@ -1176,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_varlen(type)) || - (tl+1==sl && jl_is_va_tuple_varlen(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); @@ -1261,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_varlen(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; @@ -1290,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_varlen(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; @@ -1387,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_varlen(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 a4fe7cf927455..e26557fc47463 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -364,38 +364,90 @@ static jl_value_t *intersect_union(jl_uniontype_t *a, jl_value_t *b, return tu; } +static jl_value_t *solve_tuple_fixedlen(jl_svec_t *a, cenv_t *eqc) +{ + int i; + size_t l = jl_svec_len(a); + assert(l > 0); + jl_value_t *last = jl_svecref(a,l-1); + assert(jl_is_vararg_type(last)); + jl_value_t *N = jl_tparam1(last); + 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])) { + N = eqc->data[i+1]; + break; + } + } + return N; +} + // 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) +static size_t tuple_intersect_size(jl_svec_t *a, jl_svec_t *b, cenv_t *eqc, int *bot) { 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); + long an=0, bn=0; + int aknown=1, bknown=1; + jl_value_t *N; + if (al > bl) return tuple_intersect_size(b, a, eqc, bot); + if (al > 0 && jl_is_vararg_type(jl_svecref(a, al-1))) { + N = solve_tuple_fixedlen(a, eqc); + if (jl_is_long(N)) + an = jl_unbox_long(N)-1; + else + aknown = 0; + } + if (bl > 0 && jl_is_vararg_type(jl_svecref(b, bl-1))) { + N = solve_tuple_fixedlen(b, eqc); + if (jl_is_long(N)) + bn = jl_unbox_long(N)-1; + else + bknown = 0; + } + if (al+an == bl+bn) return al+an; + if (al == bl) { + if (aknown && bknown) { + // Same number of slots but different when including Vararg{T,N} + *bot = 1; + return 0; + } + else + return an > bn ? al+an : bl+bn; + } 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))) { + if (!bknown) { + if (!aknown) return bl; - } else { - if (bl == al+1) - return al; + if (bl == al+an+1) + return al+an; *bot=1; return 0; } } - if (al > 0 && jl_is_vararg_type(jl_svecref(a,al-1))) - return bl; + if (!aknown) + return bl+bn; *bot=1; return 0; } -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 int intersect_vararg_tuple(jl_value_t *cn, cenv_t *eqc, int len) @@ -405,7 +457,7 @@ static int intersect_vararg_tuple(jl_value_t *cn, cenv_t *eqc, int len) if (!jl_is_long(cn)) { // set cn from eqc parameters for (i = 0; i < eqc->n; i+=2) - if (eqc->data[i] == cn) { + if (eqc->data[i] == cn && jl_is_long(eqc->data[i+1])) { cn = eqc->data[i+1]; break; } @@ -422,13 +474,16 @@ static int intersect_vararg_tuple(jl_value_t *cn, cenv_t *eqc, int len) return 1; } +static int has_ntuple_intersect_tuple = 0; + 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); + size_t alext = al, blext = bl; int bot=0; - size_t n = tuple_intersect_size(ap, bp, &bot); + size_t n = tuple_intersect_size(ap, bp, eqc, &bot); if (bot) return (jl_value_t*)jl_bottom_type; if (n == 0) return jl_typeof(jl_emptytuple); jl_svec_t *tc = jl_alloc_svec(n); @@ -438,12 +493,30 @@ static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, size_t ai=0, bi=0, ci; jl_value_t *ae=NULL, *be=NULL, *an=NULL, *bn=NULL; int aseq=0, bseq=0; + JL_VARARG_KIND avakind=jl_va_tuple_kind(a), bvakind=jl_va_tuple_kind(b); + jl_value_t *N=NULL; + if (avakind == JL_VARARG_BOUND) { + N = solve_tuple_fixedlen(ap, eqc); + if (jl_is_long(N)) { + avakind = JL_VARARG_INT; + alext += jl_unbox_long(N); + } + } + if (bvakind == JL_VARARG_BOUND) { + N = solve_tuple_fixedlen(bp, eqc); + if (jl_is_long(N)) { + bvakind = JL_VARARG_INT; + blext += jl_unbox_long(N); + } + } for(ci=0; ci < n; ci++) { if (ai < al) { ae = jl_svecref(ap,ai); if (jl_is_vararg_type(ae)) { - aseq=1; - an = jl_tparam1(ae); + if (avakind != JL_VARARG_INT) { + aseq=1; + an = jl_tparam1(ae); + } ae = jl_tparam0(ae); } ai++; @@ -451,8 +524,10 @@ static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, if (bi < bl) { be = jl_svecref(bp,bi); if (jl_is_vararg_type(be)) { - bseq=1; - bn = jl_tparam1(be); + if (bvakind != JL_VARARG_INT) { + bseq=1; + bn = jl_tparam1(be); + } be = jl_tparam0(be); } bi++; @@ -460,8 +535,15 @@ static jl_value_t *intersect_tuple(jl_datatype_t *a, jl_datatype_t *b, assert(ae!=NULL && be!=NULL); ce = jl_type_intersect(ae,be,penv,eqc,var); if (ce == (jl_value_t*)jl_bottom_type) { - if (var!=invariant && aseq && bseq) { + if (var!=invariant && + (aseq || (jl_is_vararg_type(jl_svecref(ap,al-1)) && ai==alext)) && + (bseq || (jl_is_vararg_type(jl_svecref(bp,bl-1)) && bi==blext))) { + //if (var!=invariant && aseq && bseq) { // (X∩Y)==∅ → (X...)∩(Y...) == () + if (avakind == JL_VARARG_BOUND) + extend(an, jl_box_long(bi-ai), eqc); + if (bvakind == JL_VARARG_BOUND) + extend(bn, jl_box_long(ai-bi), eqc); if (n == 1) { JL_GC_POP(); return (jl_value_t*)jl_typeof(jl_emptytuple); @@ -473,22 +555,24 @@ 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, avakind==JL_VARARG_BOUND ? bn : an); jl_svecset(tc, ci, ce); } - if (aseq) { + if (aseq && !bseq) { if (!intersect_vararg_tuple(an, eqc, bi-ai+1)) { JL_GC_POP(); return (jl_value_t*)jl_bottom_type; } } - if (bseq) { + if (bseq && !aseq) { if (!intersect_vararg_tuple(bn, eqc, ai-bi+1)) { JL_GC_POP(); return (jl_value_t*)jl_bottom_type; } } done_intersect_tuple: + if (aseq && bseq && (avakind != bvakind)) + has_ntuple_intersect_tuple = 1; result = (jl_value_t*)jl_apply_tuple_type(tc); JL_GC_POP(); return result; @@ -812,8 +896,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) { @@ -2301,28 +2383,34 @@ 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, pseqci=0; - int pseq=0; + size_t ci=0, pi=0, cseqpi=0, pseqci=0; + int cseq=0, pseq=0; while (1) { + if (!cseq) + cseqpi = pi; if (!pseq) pseqci = ci; - int cseq = !ta && (ci= cl) { if (pi >= pl) return 1; if (!(pseq && !invariant)) return 0; - jl_value_t *lastarg = parent[pl-1]; - if (!jl_is_vararg_fixedlen(lastarg)) + JL_VARARG_KIND vakind = jl_vararg_kind(parent[pi]); + if (vakind == JL_VARARG_UNBOUND) return 1; - return (jl_is_long(jl_tparam1(lastarg)) && - ci-pseqci == jl_unbox_long(jl_tparam1(lastarg))); + return (vakind == JL_VARARG_INT && + ci-pseqci == jl_unbox_long(jl_tparam1(parent[pi]))); } - if (pi >= pl) + 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); @@ -2331,8 +2419,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 !jl_is_vararg_fixedlen(parent[pi]); + 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++; } @@ -3250,7 +3353,7 @@ void jl_init_types(void) 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; diff --git a/src/julia.h b/src/julia.h index 53228f4dc2004..335c721016919 100644 --- a/src/julia.h +++ b/src/julia.h @@ -857,18 +857,6 @@ 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) { @@ -922,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); @@ -1001,22 +989,44 @@ DLLEXPORT ssize_t jl_unbox_gensym(jl_value_t *v); #define jl_long_type jl_int32_type #endif -STATIC_INLINE int jl_is_vararg_fixedlen(jl_value_t *v) +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) { - assert(jl_is_vararg_type(v)); + if (!jl_is_vararg_type(v)) + return JL_VARARG_NONE; jl_value_t *lenv = jl_tparam1(v); - if (jl_is_typevar(lenv)) - return ((jl_tvar_t*)lenv)->bound != 0; - return jl_is_long(lenv); + 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 int jl_is_va_tuple_varlen(jl_datatype_t *t) +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 0; - jl_value_t *last = jl_tparam(t,l-1); - return jl_is_vararg_type(last) && !jl_is_vararg_fixedlen(last); + return JL_VARARG_NONE; + return jl_vararg_kind(jl_tparam(t,l-1)); } // structs diff --git a/test/core.jl b/test/core.jl index c00bec7c2fa33..5d49dd62cc737 100644 --- a/test/core.jl +++ b/test/core.jl @@ -116,6 +116,18 @@ end 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{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)})