diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 4db0505e4e38c..5699514c53ebf 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -715,7 +715,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter, isoverlayed(method_table(interp)) && !is_nonoverlayed(result.edge_effects) && return false return f !== nothing && result.edge !== nothing && - is_total_or_error(result.edge_effects) && + is_concrete_eval_eligible(result.edge_effects) && is_all_const_arg(arginfo) end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 96f882ea4a770..e594c233353d9 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -113,13 +113,13 @@ is_nothrow(effects::Effects) = effects.nothrow === ALWAYS_TRUE is_terminates(effects::Effects) = effects.terminates === ALWAYS_TRUE is_nonoverlayed(effects::Effects) = effects.nonoverlayed -is_total_or_error(effects::Effects) = +is_concrete_eval_eligible(effects::Effects) = is_consistent(effects) && is_effect_free(effects) && is_terminates(effects) is_total(effects::Effects) = - is_total_or_error(effects) && + is_concrete_eval_eligible(effects) && is_nothrow(effects) is_removable_if_unused(effects::Effects) = diff --git a/base/expr.jl b/base/expr.jl index de1045d6addfe..7352c7863f8e1 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -337,12 +337,17 @@ end """ @pure ex - @pure(ex) `@pure` gives the compiler a hint for the definition of a pure function, helping for type inference. -This macro is intended for internal compiler use and may be subject to changes. +!!! warning + This macro is intended for internal compiler use and may be subject to changes. + +!!! warning + In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`. + This is because `@assume_effects` allows a finer grained control over Julia's purity + modeling and the effect system enables a wider range of optimizations. """ macro pure(ex) esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex) @@ -350,7 +355,6 @@ end """ @constprop setting ex - @constprop(setting, ex) `@constprop` controls the mode of interprocedural constant propagation for the annotated function. Two `setting`s are supported: @@ -373,11 +377,35 @@ end """ @assume_effects setting... ex - @assume_effects(setting..., ex) `@assume_effects` overrides the compiler's effect modeling for the given method. `ex` must be a method definition or `@ccall` expression. +```jldoctest +julia> Base.@assume_effects :terminates_locally function pow(x) + # this :terminates_locally allows `pow` to be constant-folded + res = 1 + 1 < x < 20 || error("bad pow") + while x > 1 + res *= x + x -= 1 + end + return res + end +pow (generic function with 1 method) + +julia> code_typed() do + pow(12) + end +1-element Vector{Any}: + CodeInfo( +1 ─ return 479001600 +) => Int64 + +julia> Base.@assume_effects :total_may_throw @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any +Vector{Int64} (alias for Array{Int64, 1}) +``` + !!! warning Improper use of this macro causes undefined behavior (including crashes, incorrect answers, or other hard to track bugs). Use with care and only if @@ -512,11 +540,26 @@ This `setting` combines the following other assertions: - `:terminates_globally` and is a convenient shortcut. +--- +# `:total_may_throw` + +This `setting` combines the following other assertions: +- `:consistent` +- `:effect_free` +- `:terminates_globally` +and is a convenient shortcut. + !!! note - `@assume_effects :total` is similar to `@Base.pure` with the primary + This setting is particularly useful since it allows the compiler to evaluate a call of + the applied method when all the call arguments are fully known to be constant, no matter + if the call results in an error or not. + + `@assume_effects :total_may_throw` is similar to [`@pure`](@ref) with the primary distinction that the `:consistent`-cy requirement applies world-age wise rather than globally as described above. However, in particular, a method annotated - `@Base.pure` is always `:total`. + `@pure` should always be `:total` or `:total_may_throw`. + Another advantage is that effects introduced by `@assume_effects` are propagated to + callers interprocedurally while a purity defined by `@pure` is not. """ macro assume_effects(args...) (consistent, effect_free, nothrow, terminates_globally, terminates_locally) = @@ -537,12 +580,14 @@ macro assume_effects(args...) terminates_locally = true elseif setting === :total consistent = effect_free = nothrow = terminates_globally = true + elseif setting === :total_may_throw + consistent = effect_free = terminates_globally = true else throw(ArgumentError("@assume_effects $setting not supported")) end end ex = args[end] - isa(ex, Expr) || throw(ArgumentError("Bad expression `$ex` in @constprop [settings] ex")) + isa(ex, Expr) || throw(ArgumentError("Bad expression `$ex` in `@assume_effects [settings] ex`")) if ex.head === :macrocall && ex.args[1] == Symbol("@ccall") ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride( @@ -725,7 +770,7 @@ end """ @generated f - @generated(f) + `@generated` is used to annotate a function which will be generated. In the body of the generated function, only types of arguments can be read (not the values). The function returns a quoted expression evaluated when the diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index f7f7b5e0e6c53..5c133f0f23fd5 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -1185,11 +1185,11 @@ recur_termination22(x) = x * recur_termination21(x-1) recur_termination21(12) + recur_termination22(12) end -const ___CONST_DICT___ = Dict{Any,Any}(:a => 1, :b => 2) -Base.@assume_effects :consistent :effect_free :terminates_globally consteval( +const ___CONST_DICT___ = Dict{Any,Any}(Symbol(c) => i for (i, c) in enumerate('a':'z')) +Base.@assume_effects :total_may_throw concrete_eval( f, args...; kwargs...) = f(args...; kwargs...) @test fully_eliminated() do - consteval(getindex, ___CONST_DICT___, :a) + concrete_eval(getindex, ___CONST_DICT___, :a) end # https://github.com/JuliaLang/julia/issues/44732