Skip to content

Commit a549929

Browse files
authored
Export at-invoke and make it use Core.Typeof instead of Any (#45807)
The macro was introduced in Julia 1.7 but was not exported. Previously, when an argument's type was unspecified, the type used was `Any`. This doesn't play well with types passed as arguments: for example, `x % T` has different meanings for `T` a type or a value, so if `T` is left untyped in `at-invoke rem(x::S, T)`, the method to call is ambiguous. On the other hand, if the macro expands `rem(x::S, T)` to use `Core.Typeof(T)`, the resulting expression will interpret `T` as a type and the likelihood of method ambiguities is significantly decreased.
1 parent c686e4a commit a549929

File tree

12 files changed

+73
-50
lines changed

12 files changed

+73
-50
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Language changes
2121
binary minus falls back to addition `-(x, y) = x + (-y)`, and, at the most generic level,
2222
left- and right-division fall back to multiplication with the inverse from left and right,
2323
respectively, as stated in the docstring. ([#44564])
24+
* The `@invoke` macro introduced in 1.7 is now exported. Additionally, it now uses `Core.Typeof(x)`
25+
rather than `Any` when a type annotation is omitted for an argument `x` so that types passed
26+
as arguments are handled correctly. ([#45807])
2427

2528
Compiler/Runtime improvements
2629
-----------------------------

base/compiler/ssair/show.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ length(s::String) = Base.length(s)
1010
end
1111

1212
import Base: show_unquoted
13-
using Base: printstyled, with_output_color, prec_decl
13+
using Base: printstyled, with_output_color, prec_decl, @invoke
1414

1515
function Base.show(io::IO, cfg::CFG)
1616
for (idx, block) in enumerate(cfg.blocks)
@@ -817,7 +817,7 @@ function Base.show(io::IO, t::TriState)
817817
if s !== nothing
818818
printstyled(io, s; color = tristate_color(t))
819819
else # unknown state, redirect to the fallback printing
820-
Base.@invoke show(io::IO, t::Any)
820+
@invoke show(io::IO, t::Any)
821821
end
822822
end
823823

base/exports.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,4 +1047,5 @@ export
10471047
@goto,
10481048
@view,
10491049
@views,
1050-
@static
1050+
@static,
1051+
@invoke

base/reflection.jl

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,30 +1855,45 @@ hasproperty(x, s::Symbol) = s in propertynames(x)
18551855
"""
18561856
@invoke f(arg::T, ...; kwargs...)
18571857
1858-
Provides a convenient way to call [`invoke`](@ref);
1859-
`@invoke f(arg1::T1, arg2::T2; kwargs...)` will be expanded into `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)`.
1860-
When an argument's type annotation is omitted, it's specified as `Any` argument, e.g.
1861-
`@invoke f(arg1::T, arg2)` will be expanded into `invoke(f, Tuple{T,Any}, arg1, arg2)`.
1858+
Provides a convenient way to call [`invoke`](@ref) by expanding
1859+
`@invoke f(arg1::T1, arg2::T2; kwargs...)` to `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)`.
1860+
When an argument's type annotation is omitted, it's replaced with `Core.Typeof` that argument.
1861+
To invoke a method where an argument is untyped or explicitly typed as `Any`, annotate the
1862+
argument with `::Any`.
1863+
1864+
# Examples
1865+
1866+
```jldoctest
1867+
julia> @macroexpand @invoke f(x::T, y)
1868+
:(Core.invoke(f, Tuple{T, Core.Typeof(y)}, x, y))
1869+
1870+
julia> @invoke 420::Integer % Unsigned
1871+
0x00000000000001a4
1872+
```
18621873
18631874
!!! compat "Julia 1.7"
18641875
This macro requires Julia 1.7 or later.
1876+
1877+
!!! compat "Julia 1.9"
1878+
This macro is exported as of Julia 1.9.
18651879
"""
18661880
macro invoke(ex)
18671881
f, args, kwargs = destructure_callex(ex)
1868-
newargs, newargtypes = Any[], Any[]
1869-
for i = 1:length(args)
1870-
x = args[i]
1871-
if isexpr(x, :(::))
1872-
a = x.args[1]
1873-
t = x.args[2]
1882+
types = Expr(:curly, :Tuple)
1883+
out = Expr(:call, GlobalRef(Core, :invoke))
1884+
isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...))
1885+
push!(out.args, f)
1886+
push!(out.args, types)
1887+
for arg in args
1888+
if isexpr(arg, :(::))
1889+
push!(out.args, arg.args[1])
1890+
push!(types.args, arg.args[2])
18741891
else
1875-
a = x
1876-
t = GlobalRef(Core, :Any)
1892+
push!(out.args, arg)
1893+
push!(types.args, Expr(:call, GlobalRef(Core, :Typeof), arg))
18771894
end
1878-
push!(newargs, a)
1879-
push!(newargtypes, t)
18801895
end
1881-
return esc(:($(GlobalRef(Core, :invoke))($(f), Tuple{$(newargtypes...)}, $(newargs...); $(kwargs...))))
1896+
return esc(out)
18821897
end
18831898

18841899
"""

stdlib/LinearAlgebra/src/diagonal.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,8 +756,8 @@ end
756756
/(u::AdjointAbsVec, D::Diagonal) = adjoint(adjoint(D) \ u.parent)
757757
/(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) \ u.parent)
758758
# disambiguation methods: Call unoptimized version for user defined AbstractTriangular.
759-
*(A::AbstractTriangular, D::Diagonal) = Base.@invoke *(A::AbstractMatrix, D::Diagonal)
760-
*(D::Diagonal, A::AbstractTriangular) = Base.@invoke *(D::Diagonal, A::AbstractMatrix)
759+
*(A::AbstractTriangular, D::Diagonal) = @invoke *(A::AbstractMatrix, D::Diagonal)
760+
*(D::Diagonal, A::AbstractTriangular) = @invoke *(D::Diagonal, A::AbstractMatrix)
761761

762762
dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y)
763763

stdlib/LinearAlgebra/src/generic.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1501,7 +1501,7 @@ function axpy!(α::Number,
15011501
y::StridedVecLike{T}, ry::AbstractRange{<:Integer},
15021502
) where {T<:BlasFloat}
15031503
if Base.has_offset_axes(rx, ry)
1504-
return Base.@invoke axpy!(α,
1504+
return @invoke axpy!(α,
15051505
x::AbstractArray, rx::AbstractArray{<:Integer},
15061506
y::AbstractArray, ry::AbstractArray{<:Integer},
15071507
)

test/compiler/AbstractInterpreter.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,31 @@ strangesin(x) = sin(x)
4949
strangesin(x)
5050
end |> only === Union{Float64,Nothing}
5151
@test Base.return_types((Any,); interp=MTOverlayInterp()) do x
52-
Base.@invoke strangesin(x::Float64)
52+
@invoke strangesin(x::Float64)
5353
end |> only === Union{Float64,Nothing}
5454

5555
# effect analysis should figure out that the overlayed method is used
5656
@test Base.infer_effects((Float64,); interp=MTOverlayInterp()) do x
5757
strangesin(x)
5858
end |> !Core.Compiler.is_nonoverlayed
5959
@test Base.infer_effects((Any,); interp=MTOverlayInterp()) do x
60-
Base.@invoke strangesin(x::Float64)
60+
@invoke strangesin(x::Float64)
6161
end |> !Core.Compiler.is_nonoverlayed
6262

6363
# but it should never apply for the native compilation
6464
@test Base.infer_effects((Float64,)) do x
6565
strangesin(x)
6666
end |> Core.Compiler.is_nonoverlayed
6767
@test Base.infer_effects((Any,)) do x
68-
Base.@invoke strangesin(x::Float64)
68+
@invoke strangesin(x::Float64)
6969
end |> Core.Compiler.is_nonoverlayed
7070

7171
# fallback to the internal method table
7272
@test Base.return_types((Int,); interp=MTOverlayInterp()) do x
7373
cos(x)
7474
end |> only === Float64
7575
@test Base.return_types((Any,); interp=MTOverlayInterp()) do x
76-
Base.@invoke cos(x::Float64)
76+
@invoke cos(x::Float64)
7777
end |> only === Float64
7878

7979
# not fully covered overlay method match

test/compiler/EscapeAnalysis/EAUtils.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ function CC.cache_result!(interp::EscapeAnalyzer, caller::InferenceResult)
156156
if haskey(interp.cache, caller)
157157
GLOBAL_ESCAPE_CACHE[caller.linfo] = interp.cache[caller]
158158
end
159-
return Base.@invoke CC.cache_result!(interp::AbstractInterpreter, caller::InferenceResult)
159+
return @invoke CC.cache_result!(interp::AbstractInterpreter, caller::InferenceResult)
160160
end
161161

162162
const GLOBAL_ESCAPE_CACHE = IdDict{MethodInstance,EscapeCache}()
@@ -276,7 +276,7 @@ end
276276
function Base.show(io::IO, x::EscapeInfo)
277277
name, color = get_name_color(x)
278278
if isnothing(name)
279-
Base.@invoke show(io::IO, x::Any)
279+
@invoke show(io::IO, x::Any)
280280
else
281281
printstyled(io, name; color)
282282
end

test/compiler/EscapeAnalysis/interprocedural.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ let result = code_escapes((SafeRef{String},); optimize=false) do x
7979
end
8080
# InvokeCallInfo
8181
let result = code_escapes((SafeRef{String},); optimize=false) do x
82-
return Base.@invoke noescape(x::Any)
82+
return @invoke noescape(x::Any)
8383
end
8484
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
8585
end
8686
let result = code_escapes((SafeRef{String},); optimize=false) do x
87-
return Base.@invoke conditional_escape!(false::Any, x::Any)
87+
return @invoke conditional_escape!(false::Any, x::Any)
8888
end
8989
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
9090
end

test/compiler/inference.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,7 +2129,7 @@ end
21292129
# `InterConditional` handling: `abstract_invoke`
21302130
ispositive(a) = isa(a, Int) && a > 0
21312131
@test Base.return_types((Any,)) do a
2132-
if Base.@invoke ispositive(a::Any)
2132+
if @invoke ispositive(a::Any)
21332133
return a
21342134
end
21352135
return 0
@@ -2297,7 +2297,7 @@ end
22972297

22982298
# work with `invoke`
22992299
@test Base.return_types((Any,Any)) do x, y
2300-
Base.@invoke ifelselike(isa(x, Int), x, y::Int)
2300+
@invoke ifelselike(isa(x, Int), x::Any, y::Int)
23012301
end |> only == Int
23022302

23032303
# don't be confused with vararg method
@@ -3766,16 +3766,16 @@ end
37663766
f(a::Number, sym::Bool) = sym ? Number : :number
37673767
end
37683768
@test (@eval m Base.return_types((Any,)) do a
3769-
Base.@invoke f(a::Any, true::Bool)
3769+
@invoke f(a::Any, true::Bool)
37703770
end) == Any[Type{Any}]
37713771
@test (@eval m Base.return_types((Any,)) do a
3772-
Base.@invoke f(a::Number, true::Bool)
3772+
@invoke f(a::Number, true::Bool)
37733773
end) == Any[Type{Number}]
37743774
@test (@eval m Base.return_types((Any,)) do a
3775-
Base.@invoke f(a::Any, false::Bool)
3775+
@invoke f(a::Any, false::Bool)
37763776
end) == Any[Symbol]
37773777
@test (@eval m Base.return_types((Any,)) do a
3778-
Base.@invoke f(a::Number, false::Bool)
3778+
@invoke f(a::Number, false::Bool)
37793779
end) == Any[Symbol]
37803780

37813781
# https://github.com/JuliaLang/julia/issues/41024
@@ -3790,7 +3790,7 @@ end
37903790
abstract type AbstractInterfaceExtended <: AbstractInterface end
37913791
Base.getproperty(x::AbstractInterfaceExtended, sym::Symbol) =
37923792
sym === :y ? getfield(x, sym)::Rational{Int} :
3793-
return Base.@invoke getproperty(x::AbstractInterface, sym::Symbol)
3793+
return @invoke getproperty(x::AbstractInterface, sym::Symbol)
37943794
end
37953795
@test (@eval m Base.return_types((AbstractInterfaceExtended,)) do x
37963796
x.x
@@ -4110,7 +4110,7 @@ end
41104110
# https://github.com/JuliaLang/julia/issues/44763
41114111
global x44763::Int = 0
41124112
increase_x44763!(n) = (global x44763; x44763 += n)
4113-
invoke44763(x) = Base.@invoke increase_x44763!(x)
4113+
invoke44763(x) = @invoke increase_x44763!(x)
41144114
@test Base.return_types() do
41154115
invoke44763(42)
41164116
end |> only === Int

0 commit comments

Comments
 (0)