Revision f442e4230a41d0d40f81346c1707f946f743cbcf authored by Jeff Bezanson on 09 June 2021, 02:51:34 UTC, committed by GitHub on 09 June 2021, 02:51:34 UTC
This PR implements a way to keep tables of methods that are not part of the internal method table, but still participate in the special support we have for keeping tables of methods, in particular unification through precompilation and efficient lookup. The intended design use case is to allow for method overlay tables for various non-CPU backends (e.g. GPU and TPU). These backends would like to modify basic function like `sin` to perform better on the device in question (or in the case of TPU to define them over non-LLVM intrinsics). It is worth noting that this PR only gives the ability to keep these tables of methods. It assigns no particular meaning to them and the runtime (and regular inference) do not look at them. They are designed as an implementation detail for external compilers and similar tools. # Demo ```julia julia> using Base.Experimental: @overlay, @MethodTable julia> @MethodTable(mt) # 0 methods: julia> @overlay mt function sin(x::Float64) 1 end julia> @overlay mt function cos(x::Float64) 1 end julia> mt # 2 methods: [1] cos(x::Float64) in Main at REPL[5]:1 [2] sin(x::Float64) in Main at REPL[4]:1 julia> Base._methods_by_ftype(Tuple{typeof(sin), Float64}, mt, 1, typemax(UInt)) 1-element Vector{Any}: Core.MethodMatch(Tuple{typeof(sin), Float64}, svec(), sin(x::Float64) in Main at REPL[4]:1, true) julia> Base._methods_by_ftype(Tuple{typeof(sin), Float64}, 1, typemax(UInt)) 1-element Vector{Any}: Core.MethodMatch(Tuple{typeof(sin), Float64}, svec(Float64), sin(x::T) where T<:Union{Float32, Float64} in Base.Math at special/trig.jl:29, true) ``` Co-authored-by: Tim Besard <tim.besard@gmail.com> Co-authored-by: Julian P Samaroo <jpsamaroo@jpsamaroo.me> Co-authored-by: Keno Fischer <keno@juliacomputing.com>
1 parent 0e3276c
operators.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license
using Random: randstring
@testset "ifelse" begin
@test ifelse(true, 1, 2) == 1
@test ifelse(false, 1, 2) == 2
let s = Set()
ifelse(true, push!(s, 1), push!(s, 2))
@test s == Set([1, 2])
end
let s = Set()
true ? push!(s, 1) : push!(s, 2)
false ? push!(s, 3) : push!(s, 4)
@test s == Set([1, 4])
end
let B = [true true false]
@test ifelse.(B, 1, 2) == [1 1 2]
@test ifelse.(B, 1, [2 3 4]) == [1 1 4]
@test ifelse.(B, [2 3 4], 1) == [2 3 1]
@test ifelse.(B, [2 3 4], [5 6 7]) == [2 3 7]
end
end
@testset "operations on Pairs" begin
@test reverse(Pair(1,2)) == Pair(2,1)
@test reverse(Pair("13","24")) == Pair("24","13")
@test typeof(reverse(Pair{String,Int64}("a",1))) == Pair{Int64,String}
@test convert(Pair{Float64,Float64}, 17 => 4711) === (17.0 => 4711.0)
@test convert(Pair{Int,Float64}, 17 => 4711) === (17 => 4711.0)
@test convert(Pair{Float64,Int}, 17 => 4711) === (17.0 => 4711)
@test convert(Pair{Any,Any}, 17 => 4711) === Pair{Any,Any}(17, 4711)
@test convert(Pair{Number,Number}, 17 => 4711) === Pair{Number,Number}(17, 4711)
@test promote(1=>1, 2=>2.0) === (1=>1.0, 2=>2.0)
@test promote(1=>1, 2.0=>2) === (1.0=>1, 2.0=>2)
@test promote(1=>1.0, 2.0=>2) === (1.0=>1.0, 2.0=>2.0)
@test promote(1=>1, :b=>2.0) === (Pair{Any,Float64}(1,1.0),Pair{Any,Float64}(:b,2.0))
@test isa([:a=>1, :b=>2], Vector{Pair{Symbol,Int}})
@test isa([:a=>1, :b=>2.0], Vector{Pair{Symbol,Float64}})
@test isa(["a"=>1, :b=>2.0], Vector{Pair{Any,Float64}})
p = 1=>:foo
@test first(p) == 1
@test last(p) == :foo
@test first(reverse(p)) == :foo
@test last(reverse(p)) == 1
@test lastindex(p) == 2
@test p[lastindex(p)] == p[end] == p[2] == :foo
end
# Infix `isa`
@test 1 isa Integer
@test (|)(2) == 2
@test xor(2) == 2
@test (⊻)(2) == 2
@test_throws MethodError min(Set([1]), Set([2]))
@test_throws MethodError max(Set([1]), Set([2]))
@test_throws MethodError minmax(Set([1]), Set([2]))
# Test if isless (not <) is used by min, max, minmax
# and commutativity.
struct TO23094
x::Int
end
Base.isless(a::TO23094, b::TO23094) = isless(a.x, b.x)
Base.isequal(a::TO23094, b::TO23094) = isequal(a.x, b.x)
import Base.<
<(a::TO23094, b::TO23094) = error("< should not be called")
@test isequal(min(TO23094(1), TO23094(2)), TO23094(1))
@test isequal(min(TO23094(2), TO23094(1)), TO23094(1))
@test isequal(max(TO23094(1), TO23094(2)), TO23094(2))
@test isequal(max(TO23094(2), TO23094(1)), TO23094(2))
@test isequal(minmax(TO23094(1), TO23094(2))[1], TO23094(1))
@test isequal(minmax(TO23094(1), TO23094(2))[2], TO23094(2))
@test isequal(minmax(TO23094(2), TO23094(1))[1], TO23094(1))
@test isequal(minmax(TO23094(2), TO23094(1))[2], TO23094(2))
@test isless('a','b')
@testset "isgreater" begin
# isgreater should be compatible with min.
min1(a, b) = Base.isgreater(a, b) ? b : a
# min promotes numerical arguments to the same type, but our quick min1
# doesn't, so use float test values instead of ints.
values = (1.0, 5.0, NaN, missing, Inf)
for a in values, b in values
@test min(a, b) === min1(a, b)
@test min((a,), (b,)) === min1((a,), (b,))
@test all(min([a], [b]) .=== min1([a], [b]))
end
end
@testset "isunordered" begin
@test isunordered(NaN)
@test isunordered(NaN32)
@test isunordered(missing)
@test !isunordered(1)
@test !isunordered([NaN, 1])
@test !isunordered([1.0, missing])
end
@testset "vectorized comparisons between numbers" begin
@test 1 .!= 2
@test 1 .== 1
@test 1 .< 2
@test 1 .<= 2
end
# issue #13144: max() with 4 or more array arguments
let xs = [[i:i+4;] for i in 1:10]
for n in 2:10
@test max.(xs[1:n]...) == [n:n+4;]
end
end
# issue #19714
struct T19714 <: Integer end
Base.float(::T19714) = 19714.0
Base.:/(::T19714, ::T19714) = T19714()
Base.convert(::Type{T19714}, ::Int) = T19714()
Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714
@test T19714()/1 === 1/T19714() === T19714()
# pr #17155 and #33568
@testset "function composition" begin
@test (uppercase∘(x->string(x,base=16)))(239487) == "3A77F"
@test ∘(x -> x-2, x -> x-3, x -> x+5)(7) == 7
fs = [x -> x[1:2], uppercase, lowercase]
@test ∘(fs...)("ABC") == "AB"
# Like +() and *() we leave ∘() undefined.
# While `∘() = identity` is a reasonable definition for functions, this
# would cause headaches for composition of user defined morphisms.
# See also #34251
@test_throws(MethodError, ∘())
@test ∘(x -> (x, 1))(0) === (0, 1)
@test ∘(x -> (x, 2), x -> (x, 1))(0) === ((0, 1), 2)
@test ∘(x -> (x, 3), x -> (x, 2), x->(x,1))(0) === (((0, 1), 2), 3)
@test ∘(x -> (x, 4), x -> (x, 3), x->(x,2), x-> (x, 1))(0) === ((((0, 1), 2), 3), 4)
# test that user defined functors only need to overload the two arg version
struct FreeMagma
word
end
Base.:(∘)(a::FreeMagma, b::FreeMagma) = FreeMagma((a.word, b.word))
@test ∘(FreeMagma(1)) === FreeMagma(1)
@test ∘(FreeMagma(1), FreeMagma(2)) === FreeMagma((1,2))
@test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3)) === FreeMagma(((1,2), 3))
@test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3), FreeMagma(4)) === FreeMagma((((1,2), 3), 4))
@test fieldtypes(typeof(Float64 ∘ Int)) == (Type{Float64}, Type{Int})
@test repr(uppercase ∘ first) == "uppercase ∘ first"
@test sprint(show, "text/plain", uppercase ∘ first) == "uppercase ∘ first"
# test keyword ags in composition
function kwf(a;b,c); a + b + c; end
@test (abs2 ∘ kwf)(1,b=2,c=3) == 36
end
@testset "function negation" begin
str = randstring(20)
@test filter(!isuppercase, str) == replace(str, r"[A-Z]" => "")
@test filter(!islowercase, str) == replace(str, r"[a-z]" => "")
end
# issue #19891
@testset "chained comparison" begin
B = 0 .< [1 -1 5] .< 3
@test B == [true false false]
B = 3 .> [1 -1 5] .> 0
@test B == [true false false]
end
struct TypeWrapper
t::Type
end
Base.:(<)(x::TypeWrapper, y::TypeWrapper) = (x.t <: y.t) & (x.t != y.t)
@testset "poset" begin
# Real
# / \
# Int Float64
# \ /
# Union{}
@test TypeWrapper(Int) <= TypeWrapper(Int)
@test TypeWrapper(Int) <= TypeWrapper(Real)
@test !(TypeWrapper(Int) <= TypeWrapper(Float64))
end
# issue #20355
@testset "mod1, fld1" begin
for T in [Int8, Int16, Int32, Int64],
x in T[typemin(T); typemin(T) + 1; -10:10; typemax(T)-1; typemax(T)],
y in T[typemin(T); typemin(T) + 1; -10:-1; 1:10; typemax(T)-1; typemax(T)]
m = mod1(x, y)
@test mod(x, y) == mod(m, y)
if y > 0
@test 0 < m <= y
else
@test y <= m < 0
end
if x == typemin(T) && y == -1
@test_throws DivideError fld1(x, y)
else
f = fld1(x, y)
@test (f - 1) * y + m == x
end
end
for T in [UInt8, UInt16, UInt32, UInt64],
x in T[0:10; typemax(T)-1; typemax(T)],
y in T[1:10; typemax(T)-1; typemax(T)]
m = mod1(x, y)
@test mod(x, y) == mod(m, y)
@test 0 < m <= y
f = fld1(x, y)
@test (f - 1) * y + m == x
end
for T in [Float32, Float64, Rational{Int64}],
x in T[k // 4 for k in -10:10],
y in T[k // 4 for k in [-10:-1; 1:10]]
m = mod1(x, y)
@test mod(x, y) == mod(m, y)
if y > 0
@test 0 < m <= y
else
@test y <= m < 0
end
f = fld1(x, y)
@test (f - 1) * y + m == x
end
@test fldmod1(4.0, 3) == fldmod1(4, 3)
end
@testset "Fix12" begin
x = 9
y = 7.0
fx = Base.Fix1(/, x)
fy = Base.Fix2(/, y)
@test fx(y) == x / y
@test fy(x) == x / y
end
@testset "curried comparisons" begin
eql5 = (==)(5)
neq5 = (!=)(5)
gte5 = (>=)(5)
lte5 = (<=)(5)
gt5 = (>)(5)
lt5 = (<)(5)
@test eql5(5) && !eql5(0)
@test neq5(6) && !neq5(5)
@test gte5(5) && gte5(6)
@test lte5(5) && lte5(4)
@test gt5(6) && !gt5(5)
@test lt5(4) && !lt5(5)
end
@testset "ni" begin
@test ∋([1,5,10,11], 5)
@test !∋([1,10,11], 5)
@test ∋(5)([5,1])
@test !∋(42)([0,1,100])
@test ∌(0)(1:10)
@test ∋(0)(-2:2)
end
@test [Base.afoldl(+, 1:i...) for i = 1:40] == [i * (i + 1) ÷ 2 for i = 1:40]
@testset "Returns" begin
@test @inferred(Returns(1)() ) === 1
@test @inferred(Returns(1)(23) ) === 1
@test @inferred(Returns("a")(2,3)) == "a"
@test @inferred(Returns(1)(x=1, y=2)) === 1
@test @inferred(Returns(Int)()) === Int
@test @inferred(Returns(Returns(1))()) === Returns(1)
f = @inferred Returns(Int)
@inferred f(1,2)
val = [1,2,3]
@test Returns(val)(1) === val
@test sprint(show, Returns(1.0)) == "Returns{Float64}(1.0)"
end
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...