-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Type instability with Base.promote_type
and Static.jl types for Julia v1.10-beta2 and --check-bounds=no
#50985
Comments
I take it this is distinct from (though possibly indirectly related to) the issue discussed in #50239? |
On 1.9 with
On 1.10 with
This might be #48684 cc: @aviatesk I hoped #50561 would improve the situation, but it doesn't seem enough. |
Is this another consequence of the removal of |
Kinda, #48684 replaced special handling of the almost intrinsics. So it's more than just a removal of pure. |
Putting this on the milestone since we will need to fix this in either |
Can we promise that ▶ julia +nightly --check-bounds=no -q
julia> using Static
julia> f(::Type{T}, ::Type{S}) where {T,S} = typejoin(T,S)
f (generic function with 1 method)
julia> @code_warntype f(Int, StaticInt{2})
# ...
Body::Any
# ...
julia> @eval Base @assume_effects :foldable :nothrow function typejoin(@nospecialize(a), @nospecialize(b))
# ... whole body here
julia> @code_warntype f(Int, StaticInt{2})
# ...
Body::Type{Number}
# ...
julia> @code_warntype promote_type(Int, StaticInt{2})
# ...
Body::Type{Number}
# ...
│ %13 = (%2)(%3, %4, %8, %12)::Core.Const(Number)
└── return %13 complete session▶ julia +nightly --check-bounds=no -q
julia> using Static
julia> f(::Type{T}, ::Type{S}) where {T,S} = typejoin(T,S)
f (generic function with 1 method)
julia> @code_warntype f(Int, StaticInt{2})
MethodInstance for f(::Type{Int64}, ::Type{StaticInt{2}})
from f(::Type{T}, ::Type{S}) where {T, S} @ Main REPL[2]:1
Static Parameters
T = Int64
S = StaticInt{2}
Arguments
#self#::Core.Const(Main.f)
_::Core.Const(Int64)
_::Core.Const(StaticInt{2})
Body::Any
1 ─ %1 = Main.typejoin::Core.Const(typejoin)
│ %2 = $(Expr(:static_parameter, 1))::Core.Const(Int64)
│ %3 = $(Expr(:static_parameter, 2))::Core.Const(StaticInt{2})
│ %4 = (%1)(%2, %3)::Any
└── return %4
julia> @eval Base @assume_effects :foldable :nothrow function typejoin(@nospecialize(a), @nospecialize(b))
@_nospecializeinfer_meta
if isa(a, TypeVar)
return typejoin(a.ub, b)
elseif isa(b, TypeVar)
return typejoin(a, b.ub)
elseif a <: b
return b
elseif b <: a
return a
elseif isa(a, UnionAll)
return UnionAll(a.var, typejoin(a.body, b))
elseif isa(b, UnionAll)
return UnionAll(b.var, typejoin(a, b.body))
elseif isa(a, Union)
return typejoin(typejoin(a.a, a.b), b)
elseif isa(b, Union)
return typejoin(a, typejoin(b.a, b.b))
end
# a and b are DataTypes
# We have to hide Constant info from inference, see #44390
a, b = inferencebarrier(a)::DataType, inferencebarrier(b)::DataType
if a <: Tuple
if !(b <: Tuple)
return Any
end
ap, bp = a.parameters, b.parameters
lar = length(ap)
lbr = length(bp)
if lar == 0
return Tuple{Vararg{tailjoin(bp, 1)}}
end
if lbr == 0
return Tuple{Vararg{tailjoin(ap, 1)}}
end
laf, afixed = full_va_len(ap)
lbf, bfixed = full_va_len(bp)
if laf < lbf
if isvarargtype(ap[lar]) && !afixed
c = Vector{Any}(undef, laf)
c[laf] = Vararg{typejoin(unwrapva(ap[lar]), tailjoin(bp, laf))}
n = laf-1
else
c = Vector{Any}(undef, laf+1)
c[laf+1] = Vararg{tailjoin(bp, laf+1)}
n = laf
end
elseif lbf < laf
if isvarargtype(bp[lbr]) && !bfixed
c = Vector{Any}(undef, lbf)
c[lbf] = Vararg{typejoin(unwrapva(bp[lbr]), tailjoin(ap, lbf))}
n = lbf-1
else
c = Vector{Any}(undef, lbf+1)
c[lbf+1] = Vararg{tailjoin(ap, lbf+1)}
n = lbf
end
else
c = Vector{Any}(undef, laf)
n = laf
end
for i = 1:n
ai = ap[min(i,lar)]; bi = bp[min(i,lbr)]
ci = typejoin(unwrapva(ai), unwrapva(bi))
c[i] = i == length(c) && (isvarargtype(ai) || isvarargtype(bi)) ? Vararg{ci} : ci
end
return Tuple{c...}
elseif b <: Tuple
return Any
end
while !(b === Any)
if a <: b.name.wrapper
while !(a.name === b.name)
a = supertype(a)::DataType
end
if a.name === Type.body.name
ap = a.parameters[1]
bp = b.parameters[1]
if ((isa(ap,TypeVar) && ap.lb === Bottom && ap.ub === Any) ||
(isa(bp,TypeVar) && bp.lb === Bottom && bp.ub === Any))
# handle special Type{T} supertype
return Type
end
end
aprimary = a.name.wrapper
# join on parameters
n = length(a.parameters)
if n == 0
return aprimary
end
vars = []
for i = 1:n
ai, bi = a.parameters[i], b.parameters[i]
if ai === bi || (isa(ai,Type) && isa(bi,Type) && ai <: bi && bi <: ai)
aprimary = aprimary{ai}
else
aprimary = aprimary::UnionAll
# pushfirst!(vars, aprimary.var)
_growbeg!(vars, 1)
vars[1] = aprimary.var
aprimary = aprimary.body
end
end
for v in vars
aprimary = UnionAll(v, aprimary)
end
return aprimary
end
b = supertype(b)::DataType
end
return Any
end
typejoin (generic function with 6 methods)
julia> @code_warntype f(Int, StaticInt{2})
MethodInstance for f(::Type{Int64}, ::Type{StaticInt{2}})
from f(::Type{T}, ::Type{S}) where {T, S} @ Main REPL[2]:1
Static Parameters
T = Int64
S = StaticInt{2}
Arguments
#self#::Core.Const(Main.f)
_::Core.Const(Int64)
_::Core.Const(StaticInt{2})
Body::Type{Number}
1 ─ %1 = Main.typejoin::Core.Const(typejoin)
│ %2 = $(Expr(:static_parameter, 1))::Core.Const(Int64)
│ %3 = $(Expr(:static_parameter, 2))::Core.Const(StaticInt{2})
│ %4 = (%1)(%2, %3)::Core.Const(Number)
└── return %4
julia> @code_warntype promote_type(Int, StaticInt{2})
MethodInstance for promote_type(::Type{Int64}, ::Type{StaticInt{2}})
from promote_type(::Type{T}, ::Type{S}) where {T, S} @ Base promotion.jl:310
Static Parameters
T = Int64
S = StaticInt{2}
Arguments
#self#::Core.Const(promote_type)
_::Core.Const(Int64)
_::Core.Const(StaticInt{2})
Body::Type{Number}
1 ─ nothing
│ %2 = Base.promote_result::Core.Const(Base.promote_result)
│ %3 = $(Expr(:static_parameter, 1))::Core.Const(Int64)
│ %4 = $(Expr(:static_parameter, 2))::Core.Const(StaticInt{2})
│ %5 = Base.promote_rule::Core.Const(promote_rule)
│ %6 = $(Expr(:static_parameter, 1))::Core.Const(Int64)
│ %7 = $(Expr(:static_parameter, 2))::Core.Const(StaticInt{2})
│ %8 = (%5)(%6, %7)::Core.Const(Union{})
│ %9 = Base.promote_rule::Core.Const(promote_rule)
│ %10 = $(Expr(:static_parameter, 2))::Core.Const(StaticInt{2})
│ %11 = $(Expr(:static_parameter, 1))::Core.Const(Int64)
│ %12 = (%9)(%10, %11)::Core.Const(Union{})
│ %13 = (%2)(%3, %4, %8, %12)::Core.Const(Number)
└── return %13 |
When using Julia v1.10-beta2 with
--check-bounds=no
, using Static.jl types together with native types causes a type instability. In fact, it looks likeBase.promote_type
is not able to infer a concrete type at all anymore and just returnsAny
.MWE (with
julia-1.10-beta2 --check=bounds=no
):The error does not occur if I use Julia v1.10 without
--check-bounds=no
or when using Julia v1.9 (with or without--check-bounds=no
). On those cases, we get the following output for@code_warntype
:The issue first appeared when trying to use
PtrArray
from StrideArrays.jl (see also JuliaSIMD/StrideArrays.jl#78). There, the failure to determine a usable promotion type propagates into a regular array access returning values of typeAny
, making the code unusably slow.Thanks to @ranocha for creating a more minimum MWE. Maybe @vchuravy has an idea how to fix this (since he resolved a similar issue for v1.9 and StaticArrays.jl)?
cc @chriselrod
The text was updated successfully, but these errors were encountered: