Skip to content

Commit 9ef1777

Browse files
committed
replace @pure annotations in Base with effect settings
This commit replaces `@pure`/`@_pure_meta` annotations used in `Base` with corresponding effect settings (`:total` or `:total_or_throw`). The concrete evaluation mechanism based on the effect system (#43852) has the following benefits over the `@pure`-based optimization: - it can fold cases when consistent exception is thrown - it can handle constant union-split situation as well - effects can be propagated inter-procedurally While revisiting the existing annotations, I removed some unnecessary ones and also added some more hopefully new annotations (mostly for reflection utilities). In theory this should give us some performance benefits, e.g. now we can concrete-evaluate union-spit `typeintersect`: ```julia @test Base.return_types((Union{Int,Nothing},)) do x typeintersect(String, typeof(x)) end |> only === Type{Union{}} ``` Though this commit ends up bigger than I expected -- we should carefully check a benchmark result so that it doesn't come with any regressions.
1 parent 192f3bf commit 9ef1777

17 files changed

+99
-84
lines changed

base/Base.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ include("abstractarraymath.jl")
183183
include("arraymath.jl")
184184

185185
# SIMD loops
186-
@pure sizeof(s::String) = Core.sizeof(s) # needed by gensym as called from simdloop
186+
sizeof(s::String) = Core.sizeof(s) # needed by gensym as called from simdloop
187187
include("simdloop.jl")
188188
using .SimdLoop
189189

base/array.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ size(a::Array{<:Any,N}) where {N} = (@inline; ntuple(M -> size(a, M), Val(N))::D
154154

155155
asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...)
156156

157-
allocatedinline(T::Type) = (@_pure_meta; ccall(:jl_stored_inline, Cint, (Any,), T) != Cint(0))
157+
allocatedinline(T::Type) = (@_total_meta; ccall(:jl_stored_inline, Cint, (Any,), T) != Cint(0))
158158

159159
"""
160160
Base.isbitsunion(::Type{T})

base/broadcast.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Module containing the broadcasting implementation.
88
module Broadcast
99

1010
using .Base.Cartesian
11-
using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin, promote_typejoin_union, @pure,
11+
using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin, promote_typejoin_union,
1212
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias, negate
1313
import .Base: copy, copyto!, axes
1414
export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__, BroadcastFunction
@@ -137,7 +137,7 @@ BroadcastStyle(a::AbstractArrayStyle, ::Style{Tuple}) = a
137137
BroadcastStyle(::A, ::A) where A<:ArrayStyle = A()
138138
BroadcastStyle(::ArrayStyle, ::ArrayStyle) = Unknown()
139139
BroadcastStyle(::A, ::A) where A<:AbstractArrayStyle = A()
140-
Base.@pure function BroadcastStyle(a::A, b::B) where {A<:AbstractArrayStyle{M},B<:AbstractArrayStyle{N}} where {M,N}
140+
Base.@assume_effects :total function BroadcastStyle(a::A, b::B) where {A<:AbstractArrayStyle{M},B<:AbstractArrayStyle{N}} where {M,N}
141141
if Base.typename(A) === Base.typename(B)
142142
return A(Val(max(M, N)))
143143
end

base/c.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,6 @@ macro ccall(expr)
734734
return ccall_macro_lower(:ccall, ccall_macro_parse(expr)...)
735735
end
736736

737-
macro ccall_effects(effects, expr)
737+
macro ccall_effects(effects::UInt8, expr)
738738
return ccall_macro_lower((:ccall, effects), ccall_macro_parse(expr)...)
739739
end

base/compiler/abstractinterpretation.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -1957,7 +1957,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
19571957
end
19581958
cconv = e.args[5]
19591959
if isa(cconv, QuoteNode) && isa(cconv.value, Tuple{Symbol, UInt8})
1960-
effects = cconv.value[2]
1960+
effects = cconv.value[2]::UInt8
19611961
effects = decode_effects_override(effects)
19621962
tristate_merge!(sv, Effects(
19631963
effects.consistent ? ALWAYS_TRUE : TRISTATE_UNKNOWN,

base/deprecated.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,11 @@ getindex(match::Core.MethodMatch, field::Int) =
251251
tuple_type_head(T::Type) = fieldtype(T, 1)
252252
tuple_type_cons(::Type, ::Type{Union{}}) = Union{}
253253
function tuple_type_cons(::Type{S}, ::Type{T}) where T<:Tuple where S
254-
@_pure_meta
254+
@_total_or_throw_meta
255255
Tuple{S, T.parameters...}
256256
end
257257
function parameter_upper_bound(t::UnionAll, idx)
258-
@_pure_meta
258+
@_total_or_throw_meta
259259
return rewrap_unionall((unwrap_unionall(t)::DataType).parameters[idx], t)
260260
end
261261

base/essentials.jl

+20
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,29 @@ macro isdefined(s::Symbol)
151151
return Expr(:escape, Expr(:isdefined, s))
152152
end
153153

154+
# can be used in place of `@pure` (supposed to be used for bootstrapping)
154155
macro _pure_meta()
155156
return Expr(:meta, :pure)
156157
end
158+
# can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping)
159+
macro _total_meta()
160+
return Expr(:meta, Expr(:purity,
161+
#=:consistent=#true,
162+
#=:effect_free=#true,
163+
#=:nothrow=#true,
164+
#=:terminates_globally=#true,
165+
#=:terminates_locally=#false))
166+
end
167+
# can be used in place of `@assume_effects :total_or_throw` (supposed to be used for bootstrapping)
168+
macro _total_or_throw_meta()
169+
Expr(:meta, Expr(:purity,
170+
#=:consistent=#true,
171+
#=:effect_free=#true,
172+
#=:nothrow=#false,
173+
#=:terminates_globally=#true,
174+
#=:terminates_locally=#false))
175+
end
176+
157177
# another version of inlining that propagates an inbounds context
158178
macro _propagate_inbounds_meta()
159179
return Expr(:meta, :inline, :propagate_inbounds)

base/intfuncs.jl

+3-5
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,9 @@ end
5858

5959
# binary GCD (aka Stein's) algorithm
6060
# about 1.7x (2.1x) faster for random Int64s (Int128s)
61-
# Unfortunately, we need to manually annotate this as `@pure` to work around #41694. Since
62-
# this is used in the Rational constructor, constant prop is something we do care about here.
63-
# This does call generic functions, so it might not be completely sound, but since `_gcd` is
64-
# restricted to BitIntegers, it is probably fine in practice.
65-
@pure function _gcd(a::T, b::T) where T<:BitInteger
61+
# Unfortunately, we need to manually annotate this as `@assume_effects :terminates_locally` to work around #41694.
62+
# Since this is used in the Rational constructor, constant folding is something we do care about here.
63+
@assume_effects :terminates_locally function _gcd(a::T, b::T) where T<:BitInteger
6664
za = trailing_zeros(a)
6765
zb = trailing_zeros(b)
6866
k = min(za, zb)

base/irrationals.jl

+6-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
4848
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
4949
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
5050

51-
@pure function Rational{T}(x::AbstractIrrational) where T<:Integer
51+
# XXX this may change `DEFAULT_PRECISION`, thus not effect free
52+
@assume_effects :total function Rational{T}(x::AbstractIrrational) where T<:Integer
5253
o = precision(BigFloat)
5354
p = 256
5455
while true
@@ -64,7 +65,7 @@ Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
6465
end
6566
Rational{BigInt}(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead"))
6667

67-
@pure function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64}
68+
@assume_effects :total function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64}
6869
setprecision(BigFloat, 256) do
6970
T(BigFloat(x)::BigFloat, r)
7071
end
@@ -106,11 +107,11 @@ end
106107
<=(x::AbstractFloat, y::AbstractIrrational) = x < y
107108

108109
# Irrational vs Rational
109-
@pure function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where T
110+
@assume_effects :total function rationalize(::Type{T}, x::AbstractIrrational; tol::Real=0) where T
110111
return rationalize(T, big(x), tol=tol)
111112
end
112-
@pure function lessrational(rx::Rational{<:Integer}, x::AbstractIrrational)
113-
# an @pure version of `<` for determining if the rationalization of
113+
@assume_effects :total function lessrational(rx::Rational{<:Integer}, x::AbstractIrrational)
114+
# an @assume_effects :total version of `<` for determining if the rationalization of
114115
# an irrational number required rounding up or down
115116
return rx < big(x)
116117
end

base/namedtuple.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names
218218
NamedTuple{names}(map(f, map(Tuple, (nt, nts...))...))
219219
end
220220

221-
@pure function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
221+
@assume_effects :total function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
222222
@nospecialize an bn
223223
names = Symbol[an...]
224224
for n in bn
@@ -229,7 +229,7 @@ end
229229
(names...,)
230230
end
231231

232-
@pure function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple})
232+
@assume_effects :total function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple})
233233
@nospecialize names a b
234234
bn = _nt_names(b)
235235
return Tuple{Any[ fieldtype(sym_in(names[n], bn) ? b : a, names[n]) for n in 1:length(names) ]...}
@@ -321,7 +321,7 @@ get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, ke
321321
tail(t::NamedTuple{names}) where names = NamedTuple{tail(names)}(t)
322322
front(t::NamedTuple{names}) where names = NamedTuple{front(names)}(t)
323323

324-
@pure function diff_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
324+
@assume_effects :total function diff_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
325325
@nospecialize an bn
326326
names = Symbol[]
327327
for n in an

base/operators.jl

+4-17
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,8 @@ julia> supertype(Int32)
4040
Signed
4141
```
4242
"""
43-
function supertype(T::DataType)
44-
@_pure_meta
45-
T.super
46-
end
47-
48-
function supertype(T::UnionAll)
49-
@_pure_meta
50-
UnionAll(T.var, supertype(T.body))
51-
end
43+
supertype(T::DataType) = (@_total_meta; T.super)
44+
supertype(T::UnionAll) = (@_total_meta; UnionAll(T.var, supertype(T.body)))
5245

5346
## generic comparison ##
5447

@@ -247,14 +240,8 @@ isunordered(x) = false
247240
isunordered(x::AbstractFloat) = isnan(x)
248241
isunordered(x::Missing) = true
249242

250-
function ==(T::Type, S::Type)
251-
@_pure_meta
252-
return ccall(:jl_types_equal, Cint, (Any, Any), T, S) != 0
253-
end
254-
function !=(T::Type, S::Type)
255-
@_pure_meta
256-
return !(T == S)
257-
end
243+
==(T::Type, S::Type) = (@_total_meta; ccall(:jl_types_equal, Cint, (Any, Any), T, S) != 0)
244+
!=(T::Type, S::Type) = (@_total_meta; !(T == S))
258245
==(T::TypeVar, S::Type) = false
259246
==(T::Type, S::TypeVar) = false
260247

base/promotion.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ they both inherit.
1010
"""
1111
typejoin() = Bottom
1212
typejoin(@nospecialize(t)) = t
13-
typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...)))
13+
typejoin(@nospecialize(t), ts...) = (@_total_meta; typejoin(t, typejoin(ts...)))
1414
function typejoin(@nospecialize(a), @nospecialize(b))
15-
@_pure_meta
15+
@_total_meta
1616
if isa(a, TypeVar)
1717
return typejoin(a.ub, b)
1818
elseif isa(b, TypeVar)
@@ -128,7 +128,7 @@ end
128128
# WARNING: this is wrong for some objects for which subtyping is broken
129129
# (Core.Compiler.isnotbrokensubtype), use only simple types for `b`
130130
function typesplit(@nospecialize(a), @nospecialize(b))
131-
@_pure_meta
131+
@_total_or_throw_meta
132132
if a <: b
133133
return Bottom
134134
end
@@ -180,7 +180,7 @@ function promote_typejoin_union(::Type{T}) where T
180180
end
181181

182182
function typejoin_union_tuple(T::DataType)
183-
@_pure_meta
183+
@_total_or_throw_meta
184184
u = Base.unwrap_unionall(T)
185185
p = (u::DataType).parameters
186186
lr = length(p)::Int

0 commit comments

Comments
 (0)