Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subtype: more conservative intersection of triangular variables #49591

Merged
merged 1 commit into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ typedef struct jl_varbinding_t {
// let ub = var.ub ∩ type
// 0 - var.ub <: type ? var : ub
// 1 - var.ub = ub; return var
// 2 - either (var.ub = ub; return var), or return ub
// 2 - var.lb = lb; return ub
int8_t constraintkind;
int8_t intvalued; // intvalued: must be integer-valued; i.e. occurs as N in Vararg{_,N}
int8_t limited;
Expand Down Expand Up @@ -2646,14 +2646,24 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int
return ub;
}
assert(bb->constraintkind == 2);
if (!jl_is_typevar(a)) {
if (ub == a && bb->lb != jl_bottom_type)
return ub;
else if (jl_egal(bb->ub, bb->lb))
return ub;
set_bound(&bb->ub, ub, b, e);
}
return (jl_value_t*)b;
if (ub == a && bb->lb != jl_bottom_type)
return ub;
if (jl_egal(bb->ub, bb->lb))
return ub;
if (is_leaf_bound(ub))
set_bound(&bb->lb, ub, b, e);
// TODO: can we improve this bound by pushing a new variable into the environment
// and adding that to the lower bound of our variable?
//jl_value_t *ntv = NULL;
//JL_GC_PUSH2(&ntv, &ub);
//if (bb->innervars == NULL)
// bb->innervars = jl_alloc_array_1d(jl_array_any_type, 0);
//ntv = (jl_value_t*)jl_new_typevar(b->name, bb->lb, ub);
//jl_array_ptr_1d_push(bb->innervars, ntv);
//jl_value_t *lb = simple_join(b->lb, ntv);
//JL_GC_POP();
//bb->lb = lb;
return ub;
}

// test whether `var` occurs inside constructors. `want_inv` tests only inside
Expand Down
80 changes: 46 additions & 34 deletions test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,11 @@ function test_intersection()
@testintersect((@UnionAll T Tuple{T, AbstractArray{T}}), Tuple{Int, Array{Number,1}},
Tuple{Int, Array{Number,1}})

# TODO: improve this result
#@testintersect((@UnionAll S Tuple{S,Vector{S}}), (@UnionAll T<:Real Tuple{T,AbstractVector{T}}),
# (@UnionAll S<:Real Tuple{S,Vector{S}}))
@testintersect((@UnionAll S Tuple{S,Vector{S}}), (@UnionAll T<:Real Tuple{T,AbstractVector{T}}),
(@UnionAll S<:Real Tuple{S,Vector{S}}))
(@UnionAll S<:Real Tuple{Real,Vector{S}}))

# typevar corresponding to a type it will end up being neither greater than nor
# less than
Expand Down Expand Up @@ -819,9 +822,9 @@ function test_intersection()
Tuple{Tuple{Vararg{Integer}}, Tuple{Integer,Integer}},
Tuple{Tuple{Integer,Integer}, Tuple{Integer,Integer}})

#@test isequal_type(typeintersect((@UnionAll N Tuple{NTuple{N,Any},Array{Int,N}}),
# Tuple{Tuple{Int,Vararg{Int}},Array}),
# Tuple{Tuple{Int,Vararg{Int}},Array{Int,N}})
@test isequal_type(typeintersect((@UnionAll N Tuple{NTuple{N,Any},Array{Int,N}}),
Tuple{Tuple{Int,Vararg{Int}},Array}),
@UnionAll N Tuple{Tuple{Int,Vararg{Int}},Array{Int,N}})

@testintersect((@UnionAll N Tuple{NTuple{N,Any},Array{Int,N}}),
Tuple{Tuple{Int,Vararg{Int}},Array{Int,2}},
Expand Down Expand Up @@ -930,9 +933,10 @@ function test_intersection()
# since this T is inside the invariant ctor Type{}, we allow T == Any here
@testintersect((Type{Tuple{Vararg{T}}} where T), Type{Tuple}, Type{Tuple})

# TODO: improve this
@testintersect(Tuple{Type{S}, Tuple{Any, Vararg{Any}}} where S<:Tuple{Any, Vararg{Any}},
Tuple{Type{T}, T} where T,
Tuple{Type{S},S} where S<:Tuple{Any,Vararg{Any}})
Tuple{Type{S}, Tuple{Any, Vararg{Any}}} where S<:Tuple{Any, Vararg{Any}})

# part of issue #20450
@testintersect(Tuple{Array{Ref{T}, 1}, Array{Pair{M, V}, 1}} where V where T where M,
Expand Down Expand Up @@ -1079,8 +1083,7 @@ function test_intersection_properties()
I2 = _type_intersect(S,T)
@test isequal_type(I, I2)
if i > length(easy_menagerie) || j > length(easy_menagerie)
# TODO: these cases give a conservative answer
@test issub(I, T) || issub(I, S)
# @test issub(I, T) || issub(I, S)
else
@test issub(I, T) && issub(I, S)
end
Expand Down Expand Up @@ -1601,7 +1604,7 @@ end
Tuple{Type{A29955{T,TV,TM}},
TM} where {T,TV<:AbstractVector{T},TM<:M29955{T,TV}},
Tuple{Type{A29955{Float64,Array{Float64,1},TM}},
TM} where TM<:M29955{Float64,Array{Float64,1}})
M29955{Float64,Vector{Float64}}} where TM<:M29955{Float64,Array{Float64,1}})
let M = M29955{T,Vector{Float64}} where T
@test M == (M29955{T,Vector{Float64}} where T)
@test M{Float64} == M29955{Float64,Vector{Float64}}
Expand All @@ -1619,9 +1622,9 @@ end
Tuple{LT,R,I} where LT<:Union{I, R} where R<:Rational{I} where I<:Integer,
Tuple{LT,Rational{Int},Int} where LT<:Union{Rational{Int},Int})

#@testintersect(Tuple{Any,Tuple{Int},Int},
# Tuple{LT,R,I} where LT<:Union{I, R} where R<:Tuple{I} where I<:Integer,
# Tuple{LT,Tuple{Int},Int} where LT<:Union{Tuple{Int},Int})
@testintersect(Tuple{Any,Tuple{Int},Int},
Tuple{LT,R,I} where LT<:Union{I, R} where R<:Tuple{I} where I<:Integer,
Tuple{LT,Tuple{Int},Int} where LT<:Union{Tuple{Int},Int})
# fails due to this:
let U = Tuple{Union{LT, LT1},Union{R, R1},Int} where LT1<:R1 where R1<:Tuple{Int} where LT<:Int where R<:Tuple{Int},
U2 = Union{Tuple{LT,R,Int} where LT<:Int where R<:Tuple{Int}, Tuple{LT,R,Int} where LT<:R where R<:Tuple{Int}},
Expand All @@ -1638,9 +1641,10 @@ end
# issue #31082 and #30741
@test typeintersect(Tuple{T, Ref{T}, T} where T,
Tuple{Ref{S}, S, S} where S) != Union{}
# TODO: improve this bound
@testintersect(Tuple{Pair{B,C},Union{C,Pair{B,C}},Union{B,Real}} where {B,C},
Tuple{Pair{B,C},C,C} where {B,C},
Tuple{Pair{B,C},C,C} where C<:Union{Real, B} where B)
Tuple{Pair{B,C}, Union{Pair{B,C},C},Union{Real,B}} where {B,C})
f31082(::Pair{B, C}, ::Union{C, Pair{B, C}}, ::Union{B, Real}) where {B, C} = 0
f31082(::Pair{B, C}, ::C, ::C) where {B, C} = 1
@test f31082(""=>1, 2, 3) == 1
Expand Down Expand Up @@ -1904,12 +1908,14 @@ end

# 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{})
Tuple{Type{S}, Union{Ref{S}, Ref{R}}, R} where R where S,
Tuple{Type{Q}, Union{Ref{Q}, Ref{R}}, Ref{Q}} where {Q<:Ref, R}) # likely suboptimal

t = typeintersect(Tuple{Type{T}, T, Ref{T}} where T,
let 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
@test_broken t == Tuple{Type{T}, Ref{T}, Ref{T}} where T>:Ref
@test t == Tuple{Type{T}, Ref{T}, Ref{T}} where T
end

# issue #38279
t = typeintersect(Tuple{<:Array{T, N}, Val{T}} where {T<:Real, N},
Expand Down Expand Up @@ -1954,9 +1960,11 @@ 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
# this was a case where <: disagreed with === (due to a badly-normalized type)
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}
@test_broken I == Union{Tuple{Type{T}, Ref{T}, Ref{T}} where T<:Ref, Tuple{Type{T}, Ref{T}, T} where T<:Ref}
@test I == _type_intersect(B, A) == Tuple{Type{T}, Ref{T}, Ref} where T<:Ref
I = typeintersect(B, A)
@test I == typeintersect(B, A) == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref
@test_broken I == Tuple{Type{T}, Ref{T}, Union{Ref{T}, T}} where T<:Ref
@test I == typeintersect(B, A) <: Tuple{Type{T}, Ref{T}, Ref} where T<:Ref

I = _type_intersect(A, B)
@test !Base.has_free_typevars(I)
Expand Down Expand Up @@ -2026,17 +2034,17 @@ let A = Tuple{Ref{T}, Vararg{T}} where T,
I = typeintersect(A, B)
Ts = (Tuple{Ref{Int}, Int, Int}, Tuple{Ref{Ref{Int}}, Ref{Int}, Ref{Int}})
@test I != Union{}
@test I <: A
@test_broken I <: B
@test_broken I <: A
@test I <: B
for T in Ts
if T <: A && T <: B
@test T <: I
end
end
J = typeintersect(A, C)
@test J != Union{}
@test J <: A
@test_broken J <: C
@test_broken J <: A
@test J <: C
for T in Ts
if T <: A && T <: C
@test T <: J
Expand All @@ -2045,9 +2053,13 @@ let A = Tuple{Ref{T}, Vararg{T}} where T,
end

let A = Tuple{Dict{I,T}, I, T} where T where I,
B = Tuple{AbstractDict{I,T}, T, I} 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}
B = Tuple{AbstractDict{I,T}, T, I} where T where I,
I = typeintersect(A, B)
# TODO: we should probably have something approaching I == T here,
# though note something more complex is needed since the intersection must also include types such as;
# Tuple{Dict{Integer,Any}, Integer, Int}
@test_broken I <: A && I <: B
@test I == typeintersect(B, A) == Tuple{Dict{I, T}, Any, Any} where {I, T}
end

let A = Tuple{UnionAll, Vector{Any}},
Expand Down Expand Up @@ -2378,7 +2390,7 @@ let S = Tuple{Type{T1}, T1, Val{T1}} where T1<:(Val{S1} where S1<:Val),
@test I1 !== Union{} && I2 !== Union{}
@test_broken I1 <: S
@test_broken I2 <: T
@test I2 <: S
@test_broken I2 <: S
@test_broken I2 <: T
end

Expand Down Expand Up @@ -2512,26 +2524,26 @@ end

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
@test_broken typeintersect(A, B) == Tuple{Type{T}, Val{T}, Val{T}} where T>:Val
@test typeintersect(A, B) <: Tuple{Type{T}, Val{T}, Val{T}} where T
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}}
@test_broken typeintersect(A, B) == Tuple{Type{Val}, Val{Val}, Val{Val}}
@test typeintersect(A, B) <: 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{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}}}
@test_broken typeintersect(A, B) == Tuple{Type{Val{<:Val}}, Val{Val{<:Val}}, Val{Val{<:Val}}}
@test typeintersect(A, B) <: Tuple{Type{T}, Val{T}, Val{T}} where T<:(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<:Val{T}, T1<:Val, S1<:Val{T1}}
@test typeintersect(T, S) == Tuple{Type{A}, Union{Val{A}, Val{S} where S<:Union{Val, A}, Val{x} where x<:Val, Val{x} where x<:Union{Val, A}}, Val{A}} where A<:(Val{S} where S<:Val)
@test typeintersect(S, T) == Tuple{Type{T}, Union{Val{T}, Val{S}}, Val{T}} where {T<:Val, S<:(Union{Val{A}, Val} where A)}
end