From 75c2e72c505be06e1f80498d3746a62a4f239237 Mon Sep 17 00:00:00 2001 From: Andrew Keller Date: Fri, 10 Mar 2017 06:36:51 -0800 Subject: [PATCH] Lower `x^n` (literal n) to `Base.literal_pow(^, x, Val{n})` (#20889) Fixes #20882 --- NEWS.md | 4 ++-- base/intfuncs.jl | 25 ++++++++++++------------- base/promotion.jl | 8 +++++--- src/julia-syntax.scm | 2 +- test/numbers.jl | 13 +++++++++++-- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/NEWS.md b/NEWS.md index c3f7bc04ecc4f..5ca7ed2007655 100644 --- a/NEWS.md +++ b/NEWS.md @@ -74,8 +74,8 @@ Language changes `Vector{T} = Array{T,1}` or a `const` assignment. * Experimental feature: `x^n` for integer literals `n` (e.g. `x^3` - or `x^-3`) is now lowered to `x^Val{n}`, to enable compile-time - specialization for literal integer exponents ([#20530]). + or `x^-3`) is now lowered to `Base.literal_pow(^, x, Val{n})`, to enable + compile-time specialization for literal integer exponents ([#20530], [#20889]). Breaking changes ---------------- diff --git a/base/intfuncs.jl b/base/intfuncs.jl index bab711aa566c5..53b46e4f1ff5e 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -195,29 +195,28 @@ end ^(x::Number, p::Integer) = power_by_squaring(x,p) ^(x, p::Integer) = power_by_squaring(x,p) -# x^p for any literal integer p is lowered to x^Val{p}, +# x^p for any literal integer p is lowered to Base.literal_pow(^, x, Val{p}) # to enable compile-time optimizations specialized to p. -# However, we still need a fallback that calls the general ^. -# To avoid ambiguities for methods that dispatch on the -# first argument, we dispatch the fallback via internal_pow. +# However, we still need a fallback that calls the function ^ which may either +# mean Base.^ or something else, depending on context. # We mark these @inline since if the target is marked @inline, # we want to make sure that gets propagated, # even if it is over the inlining threshold. -@inline ^(x, p) = internal_pow(x, p) -@inline internal_pow{p}(x, ::Type{Val{p}}) = x^p +@inline literal_pow{p}(f, x, ::Type{Val{p}}) = f(x,p) # Restrict inlining to hardware-supported arithmetic types, which -# are fast enough to benefit from inlining. This also makes it -# easier to override ^ without having to override the Val method. +# are fast enough to benefit from inlining. const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64} const HWNumber = Union{HWReal, Complex{<:HWReal}, Rational{<:HWReal}} # inference.jl has complicated logic to inline x^2 and x^3 for -# numeric types. In terms of Val we can do it much more simply: -@inline internal_pow(x::HWNumber, ::Type{Val{0}}) = one(x) -@inline internal_pow(x::HWNumber, ::Type{Val{1}}) = x -@inline internal_pow(x::HWNumber, ::Type{Val{2}}) = x*x -@inline internal_pow(x::HWNumber, ::Type{Val{3}}) = x*x*x +# numeric types. In terms of Val we can do it much more simply. +# (The first argument prevents unexpected behavior if a function ^ +# is defined that is not equal to Base.^) +@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{0}}) = one(x) +@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{1}}) = x +@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{2}}) = x*x +@inline literal_pow(::typeof(^), x::HWNumber, ::Type{Val{3}}) = x*x*x # b^p mod m diff --git a/base/promotion.jl b/base/promotion.jl index ed3c6389b2a5b..2f78d66354aec 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -254,9 +254,11 @@ end Exponentiation operator. If `x` is a matrix, computes matrix exponentiation. If `y` is an `Int` literal (e.g. `2` in `x^2` or `-3` in `x^-3`), the Julia code -`x^y` is transformed by the compiler to `x^Val{y}`, to enable compile-time -specialization on the value of the exponent. (As a default fallback, -however, `x^Val{y}` simply calls the `^(x,y)` function.) +`x^y` is transformed by the compiler to `Base.literal_pow(^, x, Val{y})`, to +enable compile-time specialization on the value of the exponent. +(As a default fallback we have `Base.literal_pow(^, x, Val{y}) = ^(x,y)`, +where usually `^ == Base.^` unless `^` has been defined in the calling +namespace.) ```jldoctest julia> 3^5 diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index ea2028a3debb0..b4cb4b580c61b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2057,7 +2057,7 @@ ((and (eq? f '^) (length= e 4) (integer? (cadddr e))) (expand-forms - `(call ^ ,(caddr e) (call (core apply_type) (top Val) ,(cadddr e))))) + `(call (top literal_pow) ^ ,(caddr e) (call (core apply_type) (top Val) ,(cadddr e))))) ((and (eq? f '*) (length= e 4)) (expand-transposed-op diff --git a/test/numbers.jl b/test/numbers.jl index 1dedb11e6a44c..f926085912ba3 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2903,9 +2903,12 @@ end end import Base.^ -immutable PR20530; end +struct PR20530; end +struct PR20889; x; end ^(::PR20530, p::Int) = 1 -^{p}(::PR20530, ::Type{Val{p}}) = 2 +^(t::PR20889, b) = t.x + b +^(t::PR20889, b::Integer) = t.x + b +Base.literal_pow{p}(::typeof(^), ::PR20530, ::Type{Val{p}}) = 2 @testset "literal powers" begin x = PR20530() p = 2 @@ -2923,6 +2926,12 @@ immutable PR20530; end end end end + @test PR20889(2)^3 == 5 +end +module M20889 # do we get the expected behavior without importing Base.^? + struct PR20889; x; end + ^(t::PR20889, b) = t.x + b + Base.Test.@test PR20889(2)^3 == 5 end @testset "iszero" begin