Skip to content

Commit e73ff73

Browse files
committed
add new :total_may_throw utility setting for @assume_effects
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, no matter if the call results in an error or not.
1 parent cf649a7 commit e73ff73

File tree

4 files changed

+59
-14
lines changed

4 files changed

+59
-14
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
715715
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.edge_effects) && return false
716716
return f !== nothing &&
717717
result.edge !== nothing &&
718-
is_total_or_error(result.edge_effects) &&
718+
is_concrete_eval_eligible(result.edge_effects) &&
719719
is_all_const_arg(arginfo)
720720
end
721721

base/compiler/types.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ is_nothrow(effects::Effects) = effects.nothrow === ALWAYS_TRUE
113113
is_terminates(effects::Effects) = effects.terminates === ALWAYS_TRUE
114114
is_nonoverlayed(effects::Effects) = effects.nonoverlayed
115115

116-
is_total_or_error(effects::Effects) =
116+
is_concrete_eval_eligible(effects::Effects) =
117117
is_consistent(effects) &&
118118
is_effect_free(effects) &&
119119
is_terminates(effects)
120120

121121
is_total(effects::Effects) =
122-
is_total_or_error(effects) &&
122+
is_concrete_eval_eligible(effects) &&
123123
is_nothrow(effects)
124124

125125
is_removable_if_unused(effects::Effects) =

base/expr.jl

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -337,20 +337,24 @@ end
337337

338338
"""
339339
@pure ex
340-
@pure(ex)
341340
342341
`@pure` gives the compiler a hint for the definition of a pure function,
343342
helping for type inference.
344343
345-
This macro is intended for internal compiler use and may be subject to changes.
344+
!!! warning
345+
This macro is intended for internal compiler use and may be subject to changes.
346+
347+
!!! warning
348+
In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`.
349+
This is because `@assume_effects` allows a finer grained control over Julia's purity
350+
modeling and the effect system enables a wider range of optimizations.
346351
"""
347352
macro pure(ex)
348353
esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex)
349354
end
350355

351356
"""
352357
@constprop setting ex
353-
@constprop(setting, ex)
354358
355359
`@constprop` controls the mode of interprocedural constant propagation for the
356360
annotated function. Two `setting`s are supported:
@@ -373,11 +377,35 @@ end
373377

374378
"""
375379
@assume_effects setting... ex
376-
@assume_effects(setting..., ex)
377380
378381
`@assume_effects` overrides the compiler's effect modeling for the given method.
379382
`ex` must be a method definition or `@ccall` expression.
380383
384+
```jldoctest
385+
julia> Base.@assume_effects :terminates_locally function pow(x)
386+
# this :terminates_locally allows `pow` to be constant-folded
387+
res = 1
388+
1 < x < 20 || error("bad pow")
389+
while x > 1
390+
res *= x
391+
x -= 1
392+
end
393+
return res
394+
end
395+
pow (generic function with 1 method)
396+
397+
julia> code_typed() do
398+
pow(12)
399+
end
400+
1-element Vector{Any}:
401+
CodeInfo(
402+
1 ─ return 479001600
403+
) => Int64
404+
405+
julia> Base.@assume_effects :total_may_throw @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any
406+
Vector{Int64} (alias for Array{Int64, 1})
407+
```
408+
381409
!!! warning
382410
Improper use of this macro causes undefined behavior (including crashes,
383411
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:
512540
- `:terminates_globally`
513541
and is a convenient shortcut.
514542
543+
---
544+
# `:total_may_throw`
545+
546+
This `setting` combines the following other assertions:
547+
- `:consistent`
548+
- `:effect_free`
549+
- `:terminates_globally`
550+
and is a convenient shortcut.
551+
515552
!!! note
516-
`@assume_effects :total` is similar to `@Base.pure` with the primary
553+
This setting is particularly useful since it allows the compiler to evaluate a call of
554+
the applied method when all the call arguments are fully known to be constant, no matter
555+
if the call results in an error or not.
556+
557+
`@assume_effects :total_may_throw` is similar to [`@pure`](@ref) with the primary
517558
distinction that the `:consistent`-cy requirement applies world-age wise rather
518559
than globally as described above. However, in particular, a method annotated
519-
`@Base.pure` is always `:total`.
560+
`@pure` should always be `:total` or `:total_may_throw`.
561+
Another advantage is that effects introduced by `@assume_effects` are propagated to
562+
callers interprocedurally while a purity defined by `@pure` is not.
520563
"""
521564
macro assume_effects(args...)
522565
(consistent, effect_free, nothrow, terminates_globally, terminates_locally) =
@@ -537,12 +580,14 @@ macro assume_effects(args...)
537580
terminates_locally = true
538581
elseif setting === :total
539582
consistent = effect_free = nothrow = terminates_globally = true
583+
elseif setting === :total_may_throw
584+
consistent = effect_free = terminates_globally = true
540585
else
541586
throw(ArgumentError("@assume_effects $setting not supported"))
542587
end
543588
end
544589
ex = args[end]
545-
isa(ex, Expr) || throw(ArgumentError("Bad expression `$ex` in @constprop [settings] ex"))
590+
isa(ex, Expr) || throw(ArgumentError("Bad expression `$ex` in `@assume_effects [settings] ex`"))
546591
if ex.head === :macrocall && ex.args[1] == Symbol("@ccall")
547592
ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
548593
insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride(
@@ -725,7 +770,7 @@ end
725770

726771
"""
727772
@generated f
728-
@generated(f)
773+
729774
`@generated` is used to annotate a function which will be generated.
730775
In the body of the generated function, only types of arguments can be read
731776
(not the values). The function returns a quoted expression evaluated when the

test/compiler/inline.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,11 +1185,11 @@ recur_termination22(x) = x * recur_termination21(x-1)
11851185
recur_termination21(12) + recur_termination22(12)
11861186
end
11871187

1188-
const ___CONST_DICT___ = Dict{Any,Any}(:a => 1, :b => 2)
1189-
Base.@assume_effects :consistent :effect_free :terminates_globally consteval(
1188+
const ___CONST_DICT___ = Dict{Any,Any}(Symbol(c) => i for (i, c) in enumerate('a':'z'))
1189+
Base.@assume_effects :total_may_throw concrete_eval(
11901190
f, args...; kwargs...) = f(args...; kwargs...)
11911191
@test fully_eliminated() do
1192-
consteval(getindex, ___CONST_DICT___, :a)
1192+
concrete_eval(getindex, ___CONST_DICT___, :a)
11931193
end
11941194

11951195
# https://github.com/JuliaLang/julia/issues/44732

0 commit comments

Comments
 (0)