Skip to content

Commit

Permalink
fix another case where we might return free TypeVar
Browse files Browse the repository at this point in the history
We had an environment here that looked like while computing the upper bound for J{S} where S:

where S=T
where T
where I{T}
where J{S} where S

Then we started handling those, and filling in the values:

First replacing S with T, which creates a `res` of `J{T}`

where T
where I{T}
where J{S} where S

Then we handled T, which is also going to set `wrap=0`, so our result
for `J{T}` will not be made into `J{T} where T`.

where I{T} (wrap 0)
where J{S} where S

Here we then had finished handling all the dependencies for J{S} where
S, which resulted in an upper bound assignment of J{T}

where I{T}
where J{T}

Next, we handle I{T}, though it is now unused, so while we will make `I{T}
where T` (via innervars) here for it, this goes unuesd.

And finally, we had our resulting clause:

where J{T}

But it is missing the `where T`, since `I` (from lhs) was discarded.

Thus we need to add that back now, when handling some innervars, if we
see our term got duplicated to a higher part of the bounds before
reaching this handling for its placement movement.
  • Loading branch information
vtjnash committed Mar 16, 2023
1 parent c1d1bde commit d48110d
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 44 deletions.
48 changes: 36 additions & 12 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -2724,9 +2724,8 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
newvar = jl_new_typevar(vb->var->name, vb->lb, vb->ub);

// remove/replace/rewrap free occurrences of this var in the environment
jl_varbinding_t *btemp = e->vars;
jl_varbinding_t *wrap = NULL;
while (btemp != NULL) {
for (jl_varbinding_t *btemp = e->vars; btemp != NULL; btemp = btemp->prev) {
if (jl_has_typevar(btemp->lb, vb->var)) {
if (vb->lb == (jl_value_t*)btemp->var) {
JL_GC_POP();
Expand All @@ -2742,17 +2741,12 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
}
else if (btemp->lb == (jl_value_t*)vb->var)
btemp->lb = vb->lb;
else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) &&
!jl_has_typevar(vb->ub, btemp->var) && jl_has_typevar(btemp->ub, vb->var)) {
else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var)) {
// if our variable is T, and some outer variable has constraint S = Ref{T},
// move the `where T` outside `where S` instead of putting it here. issue #21243.
if (newvar != vb->var) {
if (newvar != vb->var)
btemp->lb = jl_substitute_var(btemp->lb, vb->var, (jl_value_t*)newvar);
btemp->ub = jl_substitute_var(btemp->ub, vb->var, (jl_value_t*)newvar);
}
wrap = btemp;
btemp = btemp->prev;
continue;
}
else
btemp->lb = jl_new_struct(jl_unionall_type, vb->var, btemp->lb);
Expand All @@ -2774,13 +2768,22 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
res = jl_bottom_type;
}
}
else if (btemp->ub == (jl_value_t*)vb->var)
else if (btemp->ub == (jl_value_t*)vb->var) {
// TODO: this loses some constraints, such as in this test, where we replace T4<:S3 (e.g. T4==S3 since T4 only appears covariantly once) with T4<:Any
// a = Tuple{Float64,T3,T4} where T4 where T3
// b = Tuple{S2,Tuple{S3},S3} where S2 where S3
// Tuple{Float64, T3, T4} where {S3, T3<:Tuple{S3}, T4<:S3}
btemp->ub = vb->ub;
}
else if (btemp->depth0 == vb->depth0 && !jl_has_typevar(vb->lb, btemp->var) && !jl_has_typevar(vb->ub, btemp->var)) {
if (newvar != vb->var)
btemp->ub = jl_substitute_var(btemp->ub, vb->var, (jl_value_t*)newvar);
wrap = btemp;
}
else
btemp->ub = jl_new_struct(jl_unionall_type, vb->var, btemp->ub);
assert((jl_value_t*)btemp->var != btemp->ub);
}
btemp = btemp->prev;
}

if (wrap) {
Expand Down Expand Up @@ -2819,10 +2822,31 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind

if (res != jl_bottom_type && vb->innervars != NULL) {
int i;
for(i=0; i < jl_array_len(vb->innervars); i++) {
for (i = 0; i < jl_array_len(vb->innervars); i++) {
jl_tvar_t *var = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i);
if (jl_has_typevar(res, var))
res = jl_type_unionall((jl_tvar_t*)var, res);
// TODO: full dominator analysis for when handling innervars
// the `btemp->prev` walk is only giving a sort of post-order guarantee (since we are
// iterating 2 trees at once), so once we set `wrap`, there might remain other branches
// of the type walk that now may have incomplete bounds: finish those now too
jl_varbinding_t *btemp = e->vars;
while (btemp != NULL) {
//if (btemp->depth0 == vb->depth0 && (jl_has_typevar(btemp->lb, var) || jl_has_typevar(btemp->ub, var))) {
// if (!jl_has_typevar(vb->lb, var) && !jl_has_typevar(vb->ub, var)) {
// if (btemp->innervars == NULL)
// btemp->innervars = jl_alloc_array_1d(jl_array_any_type, 0);
// jl_array_ptr_1d_push(btemp->innervars, (jl_value_t*)var);
// }
//}
if (btemp->depth0 == vb->depth0) {
if (jl_has_typevar(btemp->lb, var))
btemp->lb = jl_type_unionall((jl_tvar_t*)var, btemp->lb);
if (jl_has_typevar(btemp->ub, var))
btemp->ub = jl_type_unionall((jl_tvar_t*)var, btemp->ub);
}
btemp = btemp->prev;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ end

# issue #21410
f21410(::V, ::Pair{V,E}) where {V, E} = E
@test code_typed(f21410, Tuple{Ref, Pair{Ref{T},Ref{T}} where T<:Number})[1].second ==
@test only(Base.return_types(f21410, Tuple{Ref, Pair{Ref{T},Ref{T}} where T<:Number})) ==
Type{E} where E <: (Ref{T} where T<:Number)

# issue #21369
Expand Down
105 changes: 74 additions & 31 deletions test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -904,11 +904,11 @@ function test_intersection()
# both of these answers seem acceptable
#@testintersect(Tuple{T,T} where T<:Union{UpperTriangular, UnitUpperTriangular},
# Tuple{AbstractArray{T,N}, AbstractArray{T,N}} where N where T,
# Union{Tuple{T,T} where T<:UpperTriangular,
# Tuple{T,T} where T<:UnitUpperTriangular})
# Union{Tuple{T,T} where T<:UpperTriangular{T1},
# Tuple{T,T} where T<:UnitUpperTriangular{T1}} where T)
@testintersect(Tuple{T,T} where T<:Union{UpperTriangular, UnitUpperTriangular},
Tuple{AbstractArray{T,N}, AbstractArray{T,N}} where N where T,
Tuple{T,T} where T<:Union{UpperTriangular, UnitUpperTriangular})
Tuple{T,T} where {T1, T<:Union{UpperTriangular{T1}, UnitUpperTriangular{T1}}})

@testintersect(DataType, Type, DataType)
@testintersect(DataType, Type{T} where T<:Integer, Type{T} where T<:Integer)
Expand Down Expand Up @@ -1209,12 +1209,12 @@ let a = Tuple{Float64,T3,T4} where T4 where T3,
b = Tuple{S2,Tuple{S3},S3} where S2 where S3
I1 = typeintersect(a, b)
I2 = typeintersect(b, a)
@test I1 <: I2
@test_broken I1 <: I2
@test I2 <: I1
@test I1 <: a
@test I2 <: a
@test_broken I1 <: b
@test_broken I2 <: b
@test I2 <: b
end
let a = Tuple{T1,Tuple{T1}} where T1,
b = Tuple{Float64,S3} where S3
Expand All @@ -1231,12 +1231,12 @@ let a = Tuple{5,T4,T5} where T4 where T5,
b = Tuple{S2,S3,Tuple{S3}} where S2 where S3
I1 = typeintersect(a, b)
I2 = typeintersect(b, a)
@test I1 <: I2
@test_broken I1 <: I2
@test I2 <: I1
@test I1 <: a
@test I2 <: a
@test_broken I1 <: b
@test_broken I2 <: b
@test I2 <: b
end
let a = Tuple{T2,Tuple{T4,T2}} where T4 where T2,
b = Tuple{Float64,Tuple{Tuple{S3},S3}} where S3
Expand All @@ -1246,12 +1246,12 @@ let a = Tuple{Tuple{T2,4},T6} where T2 where T6,
b = Tuple{Tuple{S2,S3},Tuple{S2}} where S2 where S3
I1 = typeintersect(a, b)
I2 = typeintersect(b, a)
@test I1 <: I2
@test_broken I1 <: I2
@test I2 <: I1
@test I1 <: a
@test I2 <: a
@test_broken I1 <: b
@test_broken I2 <: b
@test I2 <: b
end
let a = Tuple{T3,Int64,Tuple{T3}} where T3,
b = Tuple{S3,S3,S4} where S4 where S3
Expand Down Expand Up @@ -1883,27 +1883,24 @@ end
# issue #38081
struct AlmostLU{T, S<:AbstractMatrix{T}}
end
let X1 = Tuple{AlmostLU, Vector{T}} where T,
X2 = Tuple{AlmostLU{S, X} where X<:Matrix, Vector{S}} where S<:Union{Float32, Float64},
I = Tuple{AlmostLU{T, S} where S<:Matrix{T}, Vector{T}} where T<:Union{Float32, Float64}
@testintersect(X1, X2, I)
end
@testintersect(Tuple{AlmostLU, Vector{T}} where T,
Tuple{AlmostLU{S, X} where X<:Matrix, Vector{S}} where S<:Union{Float32, Float64},
Tuple{AlmostLU{T, X} where X<:Matrix{T}, Vector{T}} where T<:Union{Float32, Float64})

let
# issue #22787
@testintersect(Tuple{Type{Q}, Q, Ref{Q}} where Q<:Ref,
Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S,
!Union{})
# issue #22787
#this test is currently broken, as is the simplified case just below it
#@testintersect(Tuple{Type{Q}, Q, Ref{Q}} where Q<:Ref,
# Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S,
# !Union{})

t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T,
Tuple{Type{S}, Ref{S}, S} where S)
@test_broken t != Union{}
t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T,
Tuple{Type{S}, Ref{S}, S} where S)
@test_broken t != Union{} # optimal solution: Tuple{Type{T}, Ref{T}, Ref{T}} where T>:Ref

# issue #38279
t = typeintersect(Tuple{<:Array{T, N}, Val{T}} where {T<:Real, N},
Tuple{<:Array{T, N}, Val{<:AbstractString}} where {T<:Real, N})
@test t == Tuple{<:Array{Union{}, N}, Val{Union{}}} where N
end
# issue #38279
t = typeintersect(Tuple{<:Array{T, N}, Val{T}} where {T<:Real, N},
Tuple{<:Array{T, N}, Val{<:AbstractString}} where {T<:Real, N})
@test t == Tuple{<:Array{Union{}, N}, Val{Union{}}} where N

# issue #36951
@testintersect(Type{T} where T>:Missing,
Expand Down Expand Up @@ -1941,10 +1938,23 @@ end
# issue #34170
let A = Tuple{Type{T} where T<:Ref, Ref, Union{T, Union{Ref{T}, T}} where T<:Ref},
B = Tuple{Type{T}, Ref{T}, Union{Int, Ref{T}, T}} where T
I = typeintersect(A,B)
# this was a case where <: disagreed with === (due to a badly-normalized type)
@test I == typeintersect(A,B)
@test I == Tuple{Type{T}, Ref{T}, Ref} where T<:Ref
I = _type_intersect(B, A)
@test I == _type_intersect(B, A) == Union{Tuple{Type{T}, Ref{T}, Ref{T}} where T<:Ref, Tuple{Type{T}, Ref{T}, T} where T<:Ref}
I = typeintersect(B, A)
@test I == typeintersect(B, A) == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref

I = _type_intersect(A, B)
@test !Base.has_free_typevars(I)
J = Tuple{Type{T1}, Ref{T1}, Ref} where {T, T1<:Union{Ref, Ref{T}}}
@test I == _type_intersect(A, B) == J
@test_broken I == Tuple{Type{T}, Ref{T}, T1} where {T<:Ref, T1<:Union{T, Ref{T}}} # a better result, == to the result with arguments switched

I = typeintersect(A, B)
@test !Base.has_free_typevars(I)
J = Tuple{Type{T1}, Ref{T1}, Ref} where {T, T1<:Union{Ref, Ref{T}}}
@test I == typeintersect(A, B) == J

end

# issue #39218
Expand Down Expand Up @@ -1976,9 +1986,16 @@ end
let T = Type{T} where T<:(AbstractArray{I}) where I<:(Base.IteratorsMD.CartesianIndex),
S = Type{S} where S<:(Base.IteratorsMD.CartesianIndices{A, B} where B<:Tuple{Vararg{Any, A}} where A)
I = typeintersect(T, S)
# This intersection returns B<:Tuple{Vararg{Any,N}}, while intersection with T
# should have added a constraint for it to be B<:Tuple{Vararg{OrdinalRange{Int64,Int64},N}}
@test_broken I <: T
@test I <: S
@test_broken I == typeintersect(S, T)
I2 = typeintersect(S, T)
@test_broken I2 <: T
@test I2 <: S
@test I == I2
@test !Base.has_free_typevars(I)
@test !Base.has_free_typevars(I2)
end

# issue #39948
Expand Down Expand Up @@ -2459,3 +2476,29 @@ end

#issue 48961
@test !<:(Type{Union{Missing, Int}}, Type{Union{Missing, Nothing, Int}})

let A = Tuple{Type{T}, T, Val{T}} where T,
B = Tuple{Type{S}, Val{S}, Val{S}} where S
@test_broken typeintersect(A, B) != Union{}
# optimal = Tuple{Type{T}, Val{T}, Val{T}} where T>:Val
end
let A = Tuple{Type{T}, T, Val{T}} where T<:Val,
B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val
@test_broken typeintersect(A, B) != Union{}
# optimal = Tuple{Type{Val}, Val{Val}, Val{Val}}
end
let A = Tuple{Type{T}, T, Val{T}} where T<:Val,
B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val{A} where A
@test typeintersect(A, B) == Union{}
end
let A = Tuple{Type{T}, T, Val{T}} where T<:Val{<:Val},
B = Tuple{Type{S}, Val{S}, Val{S}} where S<:Val
@test_broken typeintersect(A, B) != Union{}
# optimal = Tuple{Type{Val{<:Val}}, Val{Val{<:Val}}, Val{Val{<:Val}}}
end
let T = Tuple{Union{Type{T}, Type{S}}, Union{Val{T}, Val{S}}, Union{Val{T}, S}} where T<:Val{A} where A where S<:Val,
S = Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val)
# optimal = Union{}?
@test typeintersect(T, S) == Tuple{Type{T}, T, Val{T}} where T<:(Val{S} where S<:Val)
@test typeintersect(S, T) == Tuple{Union{Type{T}, Type{T1}}, Union{Val{T1}, Val{S1}, T}, Union{S, S1}} where {T<:(Val{S} where S<:Val), S<:Union{Val{T}, T}, T1<:Val, S1<:Val{T1}}
end

0 comments on commit d48110d

Please sign in to comment.