diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index ed31e382d1152..be6f5e7a94d39 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -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 diff --git a/src/subtype.c b/src/subtype.c index c2b24a28ccd14..a644e604d843c 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -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; @@ -2289,7 +2289,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; } @@ -2352,7 +2351,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; @@ -2365,7 +2364,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; } @@ -2554,7 +2556,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; } @@ -2977,9 +2979,9 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa else lb = simple_join(xlb, ylb); if (yy) { - if (!subtype_by_bounds(lb, y, e)) + if (!reachable_var(lb, (jl_tvar_t*)y, e)) yy->lb = lb; - if (!subtype_by_bounds(y, ub, e)) + if (!reachable_var(ub, (jl_tvar_t*)y, e)) yy->ub = ub; assert(yy->ub != y); assert(yy->lb != y); diff --git a/test/subtype.jl b/test/subtype.jl index a39be465b6d1a..569ee6ade708e 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -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}}, @@ -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 @@ -1938,3 +1941,10 @@ 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