Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Aug 11, 2021
1 parent 9368bad commit 046f7b5
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
6 changes: 5 additions & 1 deletion base/compiler/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ function subst_trivial_bounds(@nospecialize(atypes))
end
v = atypes.var
if isconcretetype(v.ub) || v.lb === v.ub
return subst_trivial_bounds(atypes{v.ub})
try
return subst_trivial_bounds(atypes{v.ub})
catch
# Note in rare cases a var bound might not be valid to substitute.
end
end
return UnionAll(v, subst_trivial_bounds(atypes.body))
end
Expand Down
40 changes: 24 additions & 16 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ typedef struct jl_varbinding_t {
int8_t concrete; // 1 if another variable has a constraint forcing this one to be concrete
// constraintkind: in covariant position, we try three different ways to compute var ∩ type:
// let ub = var.ub ∩ type
// 0 - var.ub <: type ? (var.ub = ub; return var) : ub
// 0 - var.ub <: type ? var : ub
// 1 - var.ub = ub; return var
// 2 - either (var.ub = ub; return var), or return ub
int8_t constraintkind;
Expand Down Expand Up @@ -2209,24 +2209,31 @@ static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *
}

// See if var y is reachable from x via bounds; used to avoid cycles.
static int reachable_var(jl_value_t *x, jl_tvar_t *y, jl_stenv_t *e)
static int reachable_var(jl_value_t *x, jl_tvar_t *y, jl_stenv_t *e, int recur_union)
{
if (in_union(x, (jl_value_t*)y))
if (x == (jl_value_t*)y)
return 1;
if (recur_union) {
if (jl_is_uniontype(x))
return reachable_var(((jl_uniontype_t*)x)->a, y, e, 1) || reachable_var(((jl_uniontype_t*)x)->b, y, e, 1);
}
else if (in_union(x, (jl_value_t*)y)) {
return 1;
}
if (!jl_is_typevar(x))
return 0;
jl_varbinding_t *xv = lookup(e, (jl_tvar_t*)x);
if (xv == NULL)
return 0;
return reachable_var(xv->ub, y, e) || reachable_var(xv->lb, y, e);
return reachable_var(xv->ub, y, e, recur_union) || reachable_var(xv->lb, y, e, recur_union);
}

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);
if (bb == NULL)
return R ? intersect_aside(a, b->ub, e, 1, 0) : intersect_aside(b->ub, a, e, 0, 0);
if (reachable_var(bb->lb, b, e) || reachable_var(bb->ub, b, e))
if (reachable_var(bb->lb, b, e, 0) || reachable_var(bb->ub, b, e, 0))
return a;
if (bb->lb == bb->ub && jl_is_typevar(bb->lb)) {
return intersect(a, bb->lb, e, param);
Expand Down Expand Up @@ -2289,7 +2296,6 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int
if (bb->constraintkind == 0) {
JL_GC_PUSH1(&ub);
if (!jl_is_typevar(a) && try_subtype_in_env(bb->ub, a, e, 0, d)) {
set_bound(&bb->ub, ub, b, e);
JL_GC_POP();
return (jl_value_t*)b;
}
Expand Down Expand Up @@ -2352,7 +2358,7 @@ static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want
}

// Caller might not have rooted `res`
static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_stenv_t *e)
static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_unionall_t *u, jl_stenv_t *e)
{
jl_value_t *varval = NULL;
jl_tvar_t *newvar = vb->var;
Expand All @@ -2365,7 +2371,10 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
// given x<:T<:x, substitute x for T
varval = vb->ub;
}
else if (!vb->occurs_inv && is_leaf_bound(vb->ub)) {
// TODO: `vb.occurs_cov == 1` here allows substituting Tuple{<:X} => Tuple{X},
// which is valid but changes some ambiguity errors so we don't need to do it yet.
else if ((/*vb->occurs_cov == 1 || */is_leaf_bound(vb->ub)) &&
!var_occurs_invariant(u->body, u->var, 0)) {
// replace T<:x with x in covariant position when possible
varval = vb->ub;
}
Expand All @@ -2385,7 +2394,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind

// prefer generating a fresh typevar, to avoid repeated renaming if the result
// is compared to one of the intersected types later.
if (!varval)
if (!varval && (vb->lb != vb->var->lb || vb->ub != vb->var->ub))
newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub);

// remove/replace/rewrap free occurrences of this var in the environment
Expand Down Expand Up @@ -2503,7 +2512,7 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv
int envsize = 0;
while (btemp != NULL) {
envsize++;
if (envsize > 150)
if (envsize > 70)
return t;
if (btemp->var == u->var || btemp->lb == (jl_value_t*)u->var ||
btemp->ub == (jl_value_t*)u->var) {
Expand Down Expand Up @@ -2554,7 +2563,7 @@ static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv
}
if (res != jl_bottom_type)
// res is rooted by callee
res = finish_unionall(res, vb, e);
res = finish_unionall(res, vb, u, e);
JL_GC_POP();
return res;
}
Expand Down Expand Up @@ -2972,19 +2981,18 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa
jl_value_t *ub=NULL, *lb=NULL;
JL_GC_PUSH2(&lb, &ub);
ub = intersect_aside(xub, yub, e, 0, xx ? xx->depth0 : 0);
if (xlb == y)
if (reachable_var(xlb, (jl_tvar_t*)y, e, 1))
lb = ylb;
else
lb = simple_join(xlb, ylb);
if (yy) {
if (!subtype_by_bounds(lb, y, e))
yy->lb = lb;
if (!subtype_by_bounds(y, ub, e))
yy->lb = lb;
if (!reachable_var(ub, (jl_tvar_t*)y, e, 1))
yy->ub = ub;
assert(yy->ub != y);
assert(yy->lb != y);
}
if (xx && !reachable_var(y, (jl_tvar_t*)x, e)) {
if (xx && !reachable_var(y, (jl_tvar_t*)x, e, 1)) {
xx->lb = y;
xx->ub = y;
assert(xx->ub != x);
Expand Down
18 changes: 16 additions & 2 deletions test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,10 @@ function test_intersection()
Bottom)
@testintersect(Tuple{Type{Z},Z} where Z,
Tuple{Type{Ref{T}} where T, Ref{Float64}},
Tuple{Type{Ref{Float64}}, Ref{Float64}})
!Bottom)
@test_broken typeintersect(Tuple{Type{Z},Z} where Z,
Tuple{Type{Ref{T}} where T, Ref{Float64}}) ==
Tuple{Type{Ref{Float64}},Ref{Float64}}

# issue #32607
@testintersect(Type{<:Tuple{Integer,Integer}},
Expand Down Expand Up @@ -1801,7 +1804,7 @@ let X1 = Tuple{AlmostLU, Vector{T}} where T,
# TODO: the quality of this intersection is not great; for now just test that it
# doesn't stack overflow
@test I<:X1 || I<:X2
actual = Tuple{AlmostLU{S, X} where X<:Matrix{S}, Vector{S}} where S<:Union{Float32, Float64}
actual = Tuple{Union{AlmostLU{S, X} where X<:Matrix{S}, AlmostLU{S, <:Matrix}}, Vector{S}} where S<:Union{Float32, Float64}
@test I == actual
end

Expand Down Expand Up @@ -1938,3 +1941,14 @@ let A = Tuple{Dict{I,T}, I, T} where T where I,
# TODO: we should probably have I == T here
@test typeintersect(A, B) == Tuple{Dict{I,T}, I, T} where {I, T}
end

let A = Tuple{UnionAll, Vector{Any}},
B = Tuple{Type{T}, T} where T<:AbstractArray,
I = typeintersect(A, B)
@test !isconcretetype(I)
@test_broken I == Tuple{Type{T}, Vector{Any}} where T<:AbstractArray
end

@testintersect(Tuple{Type{Vector{<:T}}, T} where {T<:Integer},
Tuple{Type{T}, AbstractArray} where T<:Array,
Bottom)

0 comments on commit 046f7b5

Please sign in to comment.