Skip to content

Commit

Permalink
Merge pull request #46446 from N5N3/Fix-36443
Browse files Browse the repository at this point in the history
Fix `typeintersect` bug on `Vararg` length.
  • Loading branch information
N5N3 authored Sep 2, 2022
2 parents 31e4e26 + 4654cfa commit 852e313
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 46 deletions.
147 changes: 102 additions & 45 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ typedef struct jl_varbinding_t {
// 1 - var.ub = ub; return var
// 2 - either (var.ub = ub; return var), or return ub
int8_t constraintkind;
int8_t intvalued; // must be integer-valued; i.e. occurs as N in Vararg{_,N}
// intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N}
// 0: No restriction
// 1: must be unbounded/ or fixed to a `Int`/typevar
// 2: we have some imprecise vararg length intersection that can be improved if this var is const valued.
int8_t intvalued;
int8_t limited;
int16_t depth0; // # of invariant constructors nested around the UnionAll type for this var
// when this variable's integer value is compared to that of another,
Expand Down Expand Up @@ -2165,13 +2169,24 @@ static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_
offset = -othervar->offset;
assert(!othervar || othervar->offset == -offset);
if (bb->lb == jl_bottom_type && bb->ub == (jl_value_t*)jl_any_type) {
if (jl_is_long(v))
v = jl_box_long(jl_unbox_long(v) + offset);
bb->lb = bb->ub = v;
if (offset == 0)
bb->lb = bb->ub = v;
else if (jl_is_long(v)) {
size_t iv = jl_unbox_long(v);
v = jl_box_long(iv + offset);
bb->lb = bb->ub = v;
// Here we always return the shorter `Vararg`'s length.
if (offset > 0)
return jl_box_long(iv);
}
else
return jl_bottom_type;
}
else if (jl_is_long(v) && jl_is_long(bb->lb)) {
if (jl_unbox_long(v) != jl_unbox_long(bb->lb))
if (jl_unbox_long(v) + offset != jl_unbox_long(bb->lb))
return jl_bottom_type;
// Here we always return the shorter `Vararg`'s length.
if (offset < 0) return bb->lb;
}
else if (!jl_egal(v, bb->lb)) {
return jl_bottom_type;
Expand All @@ -2186,11 +2201,17 @@ static jl_value_t *bound_var_below(jl_tvar_t *tv, jl_varbinding_t *bb, jl_stenv_
return jl_bottom_type;
record_var_occurrence(bb, e, 2);
if (jl_is_long(bb->lb)) {
if (bb->offset == 0)
return bb->lb;
if (jl_unbox_long(bb->lb) < bb->offset)
ssize_t blb = jl_unbox_long(bb->lb);
if ((blb < bb->offset) || (blb < 0))
return jl_bottom_type;
return jl_box_long(jl_unbox_long(bb->lb) - bb->offset);
// Here we always return the shorter `Vararg`'s length.
if (bb->offset <= 0)
return bb->lb;
return jl_box_long(blb - bb->offset);
}
if (bb->offset > 0) {
bb->intvalued = 2;
return NULL;
}
return (jl_value_t*)tv;
}
Expand Down Expand Up @@ -2280,6 +2301,26 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS
return 0;
}

static int has_free_vararg_length(jl_value_t *a, jl_stenv_t *e) {
if (jl_is_unionall(a))
a = jl_unwrap_unionall(a);
if (jl_is_datatype(a) && jl_is_tuple_type((jl_datatype_t *)a)) {
size_t lx = jl_nparams((jl_datatype_t *)a);
if (lx > 0) {
jl_value_t *la = jl_tparam((jl_datatype_t *)a, lx-1);
if (jl_is_vararg(la)) {
jl_value_t *len = jl_unwrap_vararg_num((jl_vararg_t *)la);
// return 1 if we meet a vararg with Null length
if (!len) return 1;
// or a typevar not in the current env.
if (jl_is_typevar(len))
return lookup(e, (jl_tvar_t *)len) == NULL;
}
}
}
return 0;
}

static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param)
{
jl_varbinding_t *bb = lookup(e, b);
Expand Down Expand Up @@ -2334,6 +2375,11 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int
}
}
bb->ub = ub;
// We get a imprecise Tuple here. Don't change `lb` and return the typevar directly.
if (has_free_vararg_length(ub, e) && !has_free_vararg_length(a, e)) {
JL_GC_POP();
return (jl_value_t*)b;
}
bb->lb = ub;
}
JL_GC_POP();
Expand Down Expand Up @@ -2443,6 +2489,10 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
}
}

// vb is still unbounded.
if (vb->intvalued == 2 && !(varval && jl_is_long(varval)))
vb->intvalued = 1;

// TODO: this can prevent us from matching typevar identities later
if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub))
newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub);
Expand Down Expand Up @@ -2635,7 +2685,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_
e->vars->limited = 1;
}
else if (res != jl_bottom_type) {
if (vb.concrete || vb.occurs_inv>1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) {
if (vb.concrete || vb.occurs_inv>1 || vb.intvalued > 1 || u->var->lb != jl_bottom_type || (vb.occurs_inv && vb.occurs_cov)) {
restore_env(e, NULL, &se);
vb.occurs_cov = vb.occurs_inv = 0;
vb.constraintkind = vb.concrete ? 1 : 2;
Expand Down Expand Up @@ -2670,7 +2720,7 @@ static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8
}

static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t *e);
static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, jl_stenv_t *e, int param)
static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, ssize_t offset, jl_stenv_t *e, int param)
{
// Vararg: covariant in first parameter, invariant in second
jl_value_t *xp1=jl_unwrap_vararg(vmx), *xp2=jl_unwrap_vararg_num(vmx),
Expand All @@ -2688,19 +2738,24 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, jl_sten
JL_GC_POP();
return ii;
}
jl_varbinding_t *xb = NULL, *yb = NULL;
if (xp2 && jl_is_typevar(xp2)) {
jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)xp2);
if (xb) xb->intvalued = 1;
if (!yp2) {
i2 = bound_var_below((jl_tvar_t*)xp2, xb, e);
xb = lookup(e, (jl_tvar_t*)xp2);
if (xb) {
if (xb->intvalued == 0) xb->intvalued = 1;
xb->offset = offset;
}
if (!yp2)
i2 = bound_var_below((jl_tvar_t*)xp2, xb, e);
}
if (yp2 && jl_is_typevar(yp2)) {
jl_varbinding_t *yb = lookup(e, (jl_tvar_t*)yp2);
if (yb) yb->intvalued = 1;
if (!xp2) {
i2 = bound_var_below((jl_tvar_t*)yp2, yb, e);
yb = lookup(e, (jl_tvar_t*)yp2);
if (yb) {
if (yb->intvalued == 0) yb->intvalued = 1;
yb->offset = -offset;
}
if (!xp2)
i2 = bound_var_below((jl_tvar_t*)yp2, yb, e);
}
if (xp2 && yp2) {
// Vararg{T,N} <: Vararg{T2,N2}; equate N and N2
Expand All @@ -2711,6 +2766,8 @@ static jl_value_t *intersect_varargs(jl_vararg_t *vmx, jl_vararg_t *vmy, jl_sten
i2 = jl_bottom_type;
}
}
if (xb) xb->offset = 0;
if (yb) yb->offset = 0;
ii = i2 == jl_bottom_type ? (jl_value_t*)jl_bottom_type : (jl_value_t*)jl_wrap_vararg(ii, i2);
JL_GC_POP();
return ii;
Expand Down Expand Up @@ -2749,28 +2806,14 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten
res = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(params), i);
break;
}
jl_varbinding_t *xb=NULL, *yb=NULL;
jl_value_t *ii = NULL;
if (vx && vy) {
// {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}} = {(A∩S)^n...,Vararg{T∩S,N}} plus N = M-n
jl_value_t *xlen = jl_unwrap_vararg_num(xi);
if (xlen && jl_is_typevar(xlen)) {
xb = lookup(e, (jl_tvar_t*)xlen);
if (xb)
xb->offset = ly-lx;
}
jl_value_t *ylen = jl_unwrap_vararg_num(yi);
if (ylen && jl_is_typevar(ylen)) {
yb = lookup(e, (jl_tvar_t*)ylen);
if (yb)
yb->offset = lx-ly;
}
if (vx && vy)
ii = intersect_varargs((jl_vararg_t*)xi,
(jl_vararg_t*)yi,
ly - lx, // xi's offset: {A^n...,Vararg{T,N}} ∩ {Vararg{S,M}}
// {(A∩S)^n...,Vararg{T∩S,N}} plus N = M-n
e, param);
if (xb) xb->offset = 0;
if (yb) yb->offset = 0;
} else {
else {
if (vx)
xi = jl_unwrap_vararg(xi);
if (vy)
Expand All @@ -2779,6 +2822,13 @@ static jl_value_t *intersect_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_sten
}
if (ii == jl_bottom_type) {
if (vx && vy) {
jl_varbinding_t *xb=NULL, *yb=NULL;
jl_value_t *xlen = jl_unwrap_vararg_num(xi);
if (xlen && jl_is_typevar(xlen))
xb = lookup(e, (jl_tvar_t*)xlen);
jl_value_t *ylen = jl_unwrap_vararg_num(yi);
if (ylen && jl_is_typevar(ylen))
yb = lookup(e, (jl_tvar_t*)ylen);
int len = i > j ? i : j;
if ((xb && jl_is_long(xb->lb) && lx-1+jl_unbox_long(xb->lb) != len) ||
(yb && jl_is_long(yb->lb) && ly-1+jl_unbox_long(yb->lb) != len)) {
Expand Down Expand Up @@ -2877,6 +2927,11 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t
jl_value_t *ii = intersect(x, y, e, 2);
e->invdepth--;
e->Rinvdepth--;
// Skip the following subtype check if `ii` was returned from `set_vat_to_const`.
// As `var_gt`/`var_lt` might not handle `Vararg` length offset correctly.
// TODO: fix this on subtype side and remove this branch.
if (jl_is_long(ii) && ((jl_is_typevar(x) && jl_is_long(y)) || (jl_is_typevar(y) && jl_is_long(x))))
return ii;
if (jl_is_typevar(x) && jl_is_typevar(y) && (jl_is_typevar(ii) || !jl_is_type(ii)))
return ii;
if (ii == jl_bottom_type) {
Expand All @@ -2894,13 +2949,10 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t
jl_savedenv_t se;
JL_GC_PUSH2(&ii, &root);
save_env(e, &root, &se);
if (!subtype_in_env_existential(x, y, e, 0, e->invdepth)) {
if (!subtype_in_env_existential(x, y, e, 0, e->invdepth))
ii = NULL;
else if (!subtype_in_env_existential(y, x, e, 0, e->invdepth))
ii = NULL;
}
else {
if (!subtype_in_env_existential(y, x, e, 0, e->invdepth))
ii = NULL;
}
restore_env(e, root, &se);
free_env(&se);
JL_GC_POP();
Expand Down Expand Up @@ -3035,19 +3087,24 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa
lb = ylb;
else
lb = simple_join(xlb, ylb);
if (yy) {
if (yy && yy->offset == 0) {
yy->lb = lb;
if (!reachable_var(ub, (jl_tvar_t*)y, e))
yy->ub = ub;
assert(yy->ub != y);
assert(yy->lb != y);
}
if (xx && !reachable_var(y, (jl_tvar_t*)x, e)) {
if (xx && xx->offset == 0 && !reachable_var(y, (jl_tvar_t*)x, e)) {
xx->lb = y;
xx->ub = y;
assert(xx->ub != x);
}
JL_GC_POP();
// Here we always return the shorter `Vararg`'s length.
if ((xx && xx->offset < 0) || (yy && yy->offset > 0)) {
if (yy) yy->intvalued = 2;
return x;
}
return y;
}
record_var_occurrence(xx, e, param);
Expand Down
Loading

0 comments on commit 852e313

Please sign in to comment.