Force inference of convert(::Type{T}, ::T) where T via typeasserts#46573
Force inference of convert(::Type{T}, ::T) where T via typeasserts#46573KristofferC merged 3 commits intoJuliaLang:masterfrom
convert(::Type{T}, ::T) where T via typeasserts#46573Conversation
aviatesk
left a comment
There was a problem hiding this comment.
Or alternatively, we can add type constraint on the method signatures, e.g. first(r::OneTo{T}) where {T<:Integer} = oneunit(T).
And I guess we should do this automatically in inference, i.e. restrict method signatures using struct type information?
The issue with restricting the definition above to e.g.
This would be lovely :) |
That is impossible: |
I of course agree that it is impossible to have a E.g., I see a method instance for |
|
Would this work instead for it: diff --git a/base/number.jl b/base/number.jl
index 7436655bfa..77f9eb79fa 100644
--- a/base/number.jl
+++ b/base/number.jl
@@ -4,7 +4,7 @@
# Numbers are convertible
convert(::Type{T}, x::T) where {T<:Number} = x
-convert(::Type{T}, x::Number) where {T<:Number} = T(x)
+convert(::Type{T}, x::Number) where {T<:Number} = T(x)::T
"""
isinteger(x) -> BoolThe output looks correct to me for inference of that function for the general case without needing this PR's specific change |
|
No, we can see those are different: |
|
Ah, makes sense: thanks for clarifying. Your Is a test for this, or? |
|
|
|
I think it may be worth doing all of them at once, WDYT? $ git grep -n 'convert(.\+\<T('
base/Enums.jl:20:Base.cconvert(::Type{T}, x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(x)
base/array.jl:613:convert(::Type{T}, a::AbstractArray) where {T<:Array} = a isa T ? a : T(a)
base/baseext.jl:19:convert(::Type{T}, arg) where {T<:VecElement} = T(arg)
base/bitarray.jl:580:convert(T::Type{<:BitArray}, a::AbstractArray) = a isa T ? a : T(a)
base/char.jl:184:convert(::Type{T}, x::Number) where {T<:AbstractChar} = T(x)
base/char.jl:185:convert(::Type{T}, x::AbstractChar) where {T<:Number} = T(x)
base/char.jl:186:convert(::Type{T}, c::AbstractChar) where {T<:AbstractChar} = T(c)
base/number.jl:7:convert(::Type{T}, x::Number) where {T<:Number} = T(x)::T
base/pointer.jl:23:convert(::Type{T}, x::Ptr) where {T<:Integer} = T(UInt(x))
base/range.jl:255:convert(::Type{T}, r::AbstractRange) where {T<:AbstractRange} = r isa T ? r : T(r)
base/set.jl:551:convert(::Type{T}, s::AbstractSet) where {T<:AbstractSet} = T(s)
base/strings/basic.jl:232:convert(::Type{T}, s::AbstractString) where {T<:AbstractString} = T(s)::T
base/twiceprecision.jl:273:convert(::Type{T}, x::TwicePrecision) where {T<:Number} = T(x)
doc/src/manual/conversion-and-promotion.md:114:Note that the behavior of `convert(T, x)` appears to be nearly identical to `T(x)`.
doc/src/manual/conversion-and-promotion.md:184:convert(::Type{T}, x::Number) where {T<:Number} = T(x)
stdlib/Dates/src/periods.jl:437: @eval Base.convert(::Type{$T}, x::$Tc) = $T(divexact(value(x), $N))
stdlib/Dates/test/periods.jl:286: Base.convert(::Type{T}, b::Beat) where {T<:Dates.Millisecond} = T(Dates.toms(b))
stdlib/LinearAlgebra/src/bidiag.jl:203:convert(T::Type{<:Bidiagonal}, m::AbstractMatrix) = m isa T ? m : T(m)
stdlib/LinearAlgebra/src/factorization.jl:57:convert(::Type{T}, f::Factorization) where {T<:Factorization} = T(f)
stdlib/LinearAlgebra/src/factorization.jl:59:convert(::Type{T}, f::Factorization) where {T<:AbstractArray} = T(f)
stdlib/LinearAlgebra/src/givens.jl:47:convert(::Type{T}, r::AbstractRotation) where {T<:AbstractRotation} = T(r)
stdlib/LinearAlgebra/src/special.jl:72:convert(T::Type{<:LowerTriangular}, m::Union{LowerTriangular,UnitLowerTriangular}) = m isa T ? m : T(m)
stdlib/LinearAlgebra/src/special.jl:73:convert(T::Type{<:UpperTriangular}, m::Union{UpperTriangular,UnitUpperTriangular}) = m isa T ? m : T(m)
stdlib/LinearAlgebra/src/symmetric.jl:195:convert(T::Type{<:Symmetric}, m::Union{Symmetric,Hermitian}) = m isa T ? m : T(m)
stdlib/LinearAlgebra/src/symmetric.jl:196:convert(T::Type{<:Hermitian}, m::Union{Symmetric,Hermitian}) = m isa T ? m : T(m)
stdlib/LinearAlgebra/test/svd.jl:137: @test convert(Array, usv) ≈ T(asym)
stdlib/SharedArrays/src/SharedArrays.jl:377:convert(T::Type{<:SharedArray}, a::Array) = T(a)
test/atomics.jl:292:Base.convert(T::Type{<:UndefComplex}, S) = T()
test/int.jl:103: @test convert(Unsigned, T(3.0)) === UInt(3)
test/sorting.jl:476: Base.convert(::Type{T_30763{$T}}, n::Integer) = T_30763{$T}($T(n))
test/testhelpers/Furlongs.jl:28:Base.convert(::Type{T}, y::Number) where {T<:Furlong{0}} = T(y)
test/testhelpers/Furlongs.jl:33:Base.convert(::Type{T}, y::Furlong) where {T<:Furlong{0}} = T(y)
test/testhelpers/Furlongs.jl:36:Base.convert(::Type{T}, y::Furlong) where {T<:Furlong} = T(y)
test/testhelpers/OffsetArrays.jl:450:Base.convert(::Type{T}, M::AbstractArray) where {T<:OffsetArray} = M isa T ? M : T(M) |
|
(or even more broadly, it might apply to any of these methods, or even to any convert method except for Tuples) |
Okay, that's a much bigger change, yeah 😮. I went ahead and did that in bf242fb, going through them one by one via julia> ms = methods(convert, Tuple{Type{T},T} where T)
julia> for (i,m) in enumerate(ms)
fn = String(m.file)
fn = replace(fn, "/cache/build/default-amdci5-5/julialang/julia-master/usr/share/julia/stdlib/v1.9/"=>"/stdlib/")
b = "/home/tchr/dev/julia" # hardcoded path
if !startswith(fn, "/stdlib/")
fn = "/base/"*fn
end
edit(b * fn, m.line)
endThere were two entries in SparseArrays that I didn't look at since they are no longer in Base. This change seems related to the discussion in #42372, so I figured I'd cross-link that. It's not my understanding that this change actually makes a decision on the proposal there, since this only covers |
last(::OneTo{<:Any})convert(::Type{T}, T) where T via typeasserts
convert(::Type{T}, T) where T via typeassertsconvert(::Type{T}, ::T) where T via typeasserts
|
There are test-failures related to the new test which I don't fully understand. Instead of always returning Any[Integer, Integer]
Any[Union{}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong, Any, Any, Any, Main.Furlongs.Furlong, Main.Furlongs.Furlong, Union{}, Union{}, Union{}, Main.Quaternions.Quaternion, Number, Number]
Any[Union{}, Union{}, Union{}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong, Any, Any, Any, Main.Furlongs.Furlong, Main.Furlongs.Furlong, Number, Union{}, Number]
Any[Main.Quaternions.Quaternion, Number, Number]
Any[Main.Test76Main_LinearAlgebra_generic.TestGeneric.MyDual, Union{}, Union{}, Main.Quaternions.Quaternion, Union{}, Union{}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong, Any, Any, Any, Main.Furlongs.Furlong, Main.Furlongs.Furlong, Number, Number]
Any[Main.Test67Main_LinearAlgebra_generic.TestGeneric.MyDual, Union{}, Main.Quaternions.Quaternion, Union{}, Union{}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong{0}, Main.Furlongs.Furlong, Any, Any, Any, Main.Furlongs.Furlong, Main.Furlongs.Furlong, Number, Union{}, Number]It seems like different |
|
Those come from some of the testhelper files. That does make this test much less reliable than desired. Let's just drop it? |
|
Oki, done. |
|
Gentle bump for this: anything else needed here? |
Duplicates improvement made to Base's Enums: JuliaLang/julia#46573
I was looking at invalidations in StaticArrays and this place came up.
The situation is that there exist some callers of
lastwithOneTo{Any}which gets inferred asAny. An example isBase.to_shape(::OneTo{<:Any})(which in turn gets called by some specializations ofsimilar, which get invalidated by StaticArrays).But the allowable typevars in
OneTo{T}areT<:Integer, so we can constrain the result of the access to the field values toIntegerrather thanAny. This e.g. letsBase.to_shape(::OneTo{<:Any})infer asInt64instead ofAny.I figured I might as well do the same thing for
first(::OneTo)while I was at it.This seems to cut out roughly 260 invalidations from StaticArrays (~about 50% of current invalidations).