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

Export at-invoke and make it use Core.Typeof instead of Any #45807

Merged
merged 1 commit into from
Jun 27, 2022
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
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------------------------
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1047,4 +1047,5 @@ export
@goto,
@view,
@views,
@static
@static,
@invoke
45 changes: 30 additions & 15 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation is not particularly helpful, since Core.Typeof is currently undocumented.

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

"""
Expand Down
4 changes: 2 additions & 2 deletions stdlib/LinearAlgebra/src/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion stdlib/LinearAlgebra/src/generic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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},
)
Expand Down
8 changes: 4 additions & 4 deletions test/compiler/AbstractInterpreter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,31 @@ 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
@test Base.infer_effects((Float64,); interp=MTOverlayInterp()) do x
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
@test Base.infer_effects((Float64,)) do x
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
@test Base.return_types((Int,); interp=MTOverlayInterp()) do x
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
Expand Down
4 changes: 2 additions & 2 deletions test/compiler/EscapeAnalysis/EAUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}()
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/compiler/EscapeAnalysis/interprocedural.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 8 additions & 8 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions test/compiler/inline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
20 changes: 12 additions & 8 deletions test/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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

Expand Down