diff --git a/NEWS.md b/NEWS.md index c08fe50152fe29..434e38078b01ca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -21,6 +21,9 @@ Language changes binary minus falls back to addition `-(x, y) = x + (-y)`, and, at the most generic level, left- and right-division fall back to multiplication with the inverse from left and right, respectively, as stated in the docstring. ([#44564]) +* The `@invoke` macro introduced in 1.7 is now exported. Additionally, it now uses `Core.Typeof(x)` + rather than `Any` when a type annotation is omitted for an argument `x` so that types passed + as arguments are handled correctly. ([#45807]) Compiler/Runtime improvements ----------------------------- diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 0f8954f6b31310..51abf2a228de3a 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -10,7 +10,7 @@ length(s::String) = Base.length(s) end import Base: show_unquoted -using Base: printstyled, with_output_color, prec_decl +using Base: printstyled, with_output_color, prec_decl, @invoke function Base.show(io::IO, cfg::CFG) for (idx, block) in enumerate(cfg.blocks) @@ -817,7 +817,7 @@ function Base.show(io::IO, t::TriState) if s !== nothing printstyled(io, s; color = tristate_color(t)) else # unknown state, redirect to the fallback printing - Base.@invoke show(io::IO, t::Any) + @invoke show(io::IO, t::Any) end end diff --git a/base/exports.jl b/base/exports.jl index 304d48d24bdcd9..428e6894bbafea 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1047,4 +1047,5 @@ export @goto, @view, @views, - @static + @static, + @invoke diff --git a/base/reflection.jl b/base/reflection.jl index a725e2d59916a7..644714c8440cb5 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1855,30 +1855,45 @@ hasproperty(x, s::Symbol) = s in propertynames(x) """ @invoke f(arg::T, ...; kwargs...) -Provides a convenient way to call [`invoke`](@ref); -`@invoke f(arg1::T1, arg2::T2; kwargs...)` will be expanded into `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)`. -When an argument's type annotation is omitted, it's specified as `Any` argument, e.g. -`@invoke f(arg1::T, arg2)` will be expanded into `invoke(f, Tuple{T,Any}, arg1, arg2)`. +Provides a convenient way to call [`invoke`](@ref) by expanding +`@invoke f(arg1::T1, arg2::T2; kwargs...)` to `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)`. +When an argument's type annotation is omitted, it's replaced with `Core.Typeof` that argument. +To invoke a method where an argument is untyped or explicitly typed as `Any`, annotate the +argument with `::Any`. + +# Examples + +```jldoctest +julia> @macroexpand @invoke f(x::T, y) +:(Core.invoke(f, Tuple{T, Core.Typeof(y)}, x, y)) + +julia> @invoke 420::Integer % Unsigned +0x00000000000001a4 +``` !!! compat "Julia 1.7" This macro requires Julia 1.7 or later. + +!!! compat "Julia 1.9" + This macro is exported as of Julia 1.9. """ macro invoke(ex) f, args, kwargs = destructure_callex(ex) - newargs, newargtypes = Any[], Any[] - for i = 1:length(args) - x = args[i] - if isexpr(x, :(::)) - a = x.args[1] - t = x.args[2] + types = Expr(:curly, :Tuple) + out = Expr(:call, GlobalRef(Core, :invoke)) + isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) + push!(out.args, f) + push!(out.args, types) + for arg in args + if isexpr(arg, :(::)) + push!(out.args, arg.args[1]) + push!(types.args, arg.args[2]) else - a = x - t = GlobalRef(Core, :Any) + push!(out.args, arg) + push!(types.args, Expr(:call, GlobalRef(Core, :Typeof), arg)) end - push!(newargs, a) - push!(newargtypes, t) end - return esc(:($(GlobalRef(Core, :invoke))($(f), Tuple{$(newargtypes...)}, $(newargs...); $(kwargs...)))) + return esc(out) end """ diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index f4e6d427dceebf..fab0f36660c469 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -756,8 +756,8 @@ end /(u::AdjointAbsVec, D::Diagonal) = adjoint(adjoint(D) \ u.parent) /(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) \ u.parent) # disambiguation methods: Call unoptimized version for user defined AbstractTriangular. -*(A::AbstractTriangular, D::Diagonal) = Base.@invoke *(A::AbstractMatrix, D::Diagonal) -*(D::Diagonal, A::AbstractTriangular) = Base.@invoke *(D::Diagonal, A::AbstractMatrix) +*(A::AbstractTriangular, D::Diagonal) = @invoke *(A::AbstractMatrix, D::Diagonal) +*(D::Diagonal, A::AbstractTriangular) = @invoke *(D::Diagonal, A::AbstractMatrix) dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 7ff99c75db7714..36ced82eb6c7db 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1501,7 +1501,7 @@ function axpy!(α::Number, y::StridedVecLike{T}, ry::AbstractRange{<:Integer}, ) where {T<:BlasFloat} if Base.has_offset_axes(rx, ry) - return Base.@invoke axpy!(α, + return @invoke axpy!(α, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractArray, ry::AbstractArray{<:Integer}, ) diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 9d1be42891042f..74775b8e772130 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -49,7 +49,7 @@ strangesin(x) = sin(x) strangesin(x) end |> only === Union{Float64,Nothing} @test Base.return_types((Any,); interp=MTOverlayInterp()) do x - Base.@invoke strangesin(x::Float64) + @invoke strangesin(x::Float64) end |> only === Union{Float64,Nothing} # effect analysis should figure out that the overlayed method is used @@ -57,7 +57,7 @@ end |> only === Union{Float64,Nothing} strangesin(x) end |> !Core.Compiler.is_nonoverlayed @test Base.infer_effects((Any,); interp=MTOverlayInterp()) do x - Base.@invoke strangesin(x::Float64) + @invoke strangesin(x::Float64) end |> !Core.Compiler.is_nonoverlayed # but it should never apply for the native compilation @@ -65,7 +65,7 @@ end |> !Core.Compiler.is_nonoverlayed strangesin(x) end |> Core.Compiler.is_nonoverlayed @test Base.infer_effects((Any,)) do x - Base.@invoke strangesin(x::Float64) + @invoke strangesin(x::Float64) end |> Core.Compiler.is_nonoverlayed # fallback to the internal method table @@ -73,7 +73,7 @@ end |> Core.Compiler.is_nonoverlayed cos(x) end |> only === Float64 @test Base.return_types((Any,); interp=MTOverlayInterp()) do x - Base.@invoke cos(x::Float64) + @invoke cos(x::Float64) end |> only === Float64 # not fully covered overlay method match diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index f06f5e0ef89835..f71cc203877339 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -156,7 +156,7 @@ function CC.cache_result!(interp::EscapeAnalyzer, caller::InferenceResult) if haskey(interp.cache, caller) GLOBAL_ESCAPE_CACHE[caller.linfo] = interp.cache[caller] end - return Base.@invoke CC.cache_result!(interp::AbstractInterpreter, caller::InferenceResult) + return @invoke CC.cache_result!(interp::AbstractInterpreter, caller::InferenceResult) end const GLOBAL_ESCAPE_CACHE = IdDict{MethodInstance,EscapeCache}() @@ -276,7 +276,7 @@ end function Base.show(io::IO, x::EscapeInfo) name, color = get_name_color(x) if isnothing(name) - Base.@invoke show(io::IO, x::Any) + @invoke show(io::IO, x::Any) else printstyled(io, name; color) end diff --git a/test/compiler/EscapeAnalysis/interprocedural.jl b/test/compiler/EscapeAnalysis/interprocedural.jl index 42a2505e03c087..756e5489ed6376 100644 --- a/test/compiler/EscapeAnalysis/interprocedural.jl +++ b/test/compiler/EscapeAnalysis/interprocedural.jl @@ -79,12 +79,12 @@ let result = code_escapes((SafeRef{String},); optimize=false) do x end # InvokeCallInfo let result = code_escapes((SafeRef{String},); optimize=false) do x - return Base.@invoke noescape(x::Any) + return @invoke noescape(x::Any) end @test has_no_escape(ignore_argescape(result.state[Argument(2)])) end let result = code_escapes((SafeRef{String},); optimize=false) do x - return Base.@invoke conditional_escape!(false::Any, x::Any) + return @invoke conditional_escape!(false::Any, x::Any) end @test has_no_escape(ignore_argescape(result.state[Argument(2)])) end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 262e6eb6448db2..8fc63f42ada877 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2129,7 +2129,7 @@ end # `InterConditional` handling: `abstract_invoke` ispositive(a) = isa(a, Int) && a > 0 @test Base.return_types((Any,)) do a - if Base.@invoke ispositive(a::Any) + if @invoke ispositive(a::Any) return a end return 0 @@ -2297,7 +2297,7 @@ end # work with `invoke` @test Base.return_types((Any,Any)) do x, y - Base.@invoke ifelselike(isa(x, Int), x, y::Int) + @invoke ifelselike(isa(x, Int), x::Any, y::Int) end |> only == Int # don't be confused with vararg method @@ -3766,16 +3766,16 @@ end f(a::Number, sym::Bool) = sym ? Number : :number end @test (@eval m Base.return_types((Any,)) do a - Base.@invoke f(a::Any, true::Bool) + @invoke f(a::Any, true::Bool) end) == Any[Type{Any}] @test (@eval m Base.return_types((Any,)) do a - Base.@invoke f(a::Number, true::Bool) + @invoke f(a::Number, true::Bool) end) == Any[Type{Number}] @test (@eval m Base.return_types((Any,)) do a - Base.@invoke f(a::Any, false::Bool) + @invoke f(a::Any, false::Bool) end) == Any[Symbol] @test (@eval m Base.return_types((Any,)) do a - Base.@invoke f(a::Number, false::Bool) + @invoke f(a::Number, false::Bool) end) == Any[Symbol] # https://github.com/JuliaLang/julia/issues/41024 @@ -3790,7 +3790,7 @@ end abstract type AbstractInterfaceExtended <: AbstractInterface end Base.getproperty(x::AbstractInterfaceExtended, sym::Symbol) = sym === :y ? getfield(x, sym)::Rational{Int} : - return Base.@invoke getproperty(x::AbstractInterface, sym::Symbol) + return @invoke getproperty(x::AbstractInterface, sym::Symbol) end @test (@eval m Base.return_types((AbstractInterfaceExtended,)) do x x.x @@ -4110,7 +4110,7 @@ end # https://github.com/JuliaLang/julia/issues/44763 global x44763::Int = 0 increase_x44763!(n) = (global x44763; x44763 += n) -invoke44763(x) = Base.@invoke increase_x44763!(x) +invoke44763(x) = @invoke increase_x44763!(x) @test Base.return_types() do invoke44763(42) end |> only === Int diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 5a9cb1733be77e..044ba03bacf32d 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -992,7 +992,7 @@ Base.@constprop :aggressive function conditional_escape!(cnd, x) return nothing end @test fully_eliminated((String,)) do x - Base.@invoke conditional_escape!(false::Any, x::Any) + @invoke conditional_escape!(false::Any, x::Any) end @testset "strides for ReshapedArray (PR#44027)" begin @@ -1066,12 +1066,12 @@ let src = code_typed1() do @test count(isnew, src.code) == 1 end let src = code_typed1() do - Base.@invoke FooTheRef(nothing::Any) + @invoke FooTheRef(nothing::Any) end @test count(isnew, src.code) == 1 end let src = code_typed1() do - Base.@invoke FooTheRef(0::Any) + @invoke FooTheRef(0::Any) end @test count(isnew, src.code) == 1 end @@ -1084,11 +1084,11 @@ end nothing end @test fully_eliminated() do - Base.@invoke FooTheRef(nothing::Any) + @invoke FooTheRef(nothing::Any) nothing end @test fully_eliminated() do - Base.@invoke FooTheRef(0::Any) + @invoke FooTheRef(0::Any) nothing end diff --git a/test/misc.jl b/test/misc.jl index c8153eef9ec3ae..6f0e6457be7eae 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -896,7 +896,7 @@ end # test against `invoke` doc example let f(x::Real) = x^2 - f(x::Integer) = 1 + Base.@invoke f(x::Real) + f(x::Integer) = 1 + @invoke f(x::Real) @test f(2) == 5 end @@ -908,17 +908,22 @@ end _f2(_) = Real @test f1(1) === Integer @test f2(1) === Integer - @test Base.@invoke(f1(1::Real)) === Real - @test Base.@invoke(f2(1::Real)) === Integer + @test @invoke(f1(1::Real)) === Real + @test @invoke(f2(1::Real)) === Integer end - # when argment's type annotation is omitted, it should be specified as `Any` + # when argment's type annotation is omitted, it should be specified as `Core.Typeof(x)` let f(_) = Any f(x::Integer) = Integer @test f(1) === Integer - @test Base.@invoke(f(1::Any)) === Any - @test Base.@invoke(f(1)) === Any + @test @invoke(f(1::Any)) === Any + @test @invoke(f(1)) === Integer + + 😎(x, y) = 1 + 😎(x, ::Type{Int}) = 2 + # Without `Core.Typeof`, the first method would be called + @test @invoke(😎(1, Int)) == 2 end # handle keyword arguments correctly @@ -927,8 +932,7 @@ end f(::Integer; kwargs...) = error("don't call me") @test_throws Exception f(1; kw1 = 1, kw2 = 2) - @test 3 == Base.@invoke f(1::Any; kw1 = 1, kw2 = 2) - @test 3 == Base.@invoke f(1; kw1 = 1, kw2 = 2) + @test 3 == @invoke f(1::Any; kw1 = 1, kw2 = 2) end end