Skip to content

Commit

Permalink
deprecate @pure and make it just alias to `@assume_effects :foldabl…
Browse files Browse the repository at this point in the history
…e` (JuliaLang#48682)

Co-authored-by: Oscar Smith <[email protected]>
Co-authored-by: Valentin Churavy <[email protected]>
  • Loading branch information
3 people committed Feb 16, 2023
1 parent fe6307d commit 2ce9060
Show file tree
Hide file tree
Showing 23 changed files with 49 additions and 189 deletions.
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Language changes

Compiler/Runtime improvements
-----------------------------

* The `@pure` macro is now deprecated. Use `Base.@assume_effects :foldable` instead ([#48682]).

Command-line option changes
---------------------------
Expand Down
40 changes: 1 addition & 39 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,11 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
all_effects = EFFECTS_TOTAL
if !matches.nonoverlayed
# currently we don't have a good way to execute the overlayed method definition,
# so we should give up pure/concrete eval when any of the matched methods is overlayed
# so we should give up concrete eval when any of the matched methods is overlayed
f = nothing
all_effects = Effects(all_effects; nonoverlayed=false)
end

# try pure-evaluation
val = pure_eval_call(interp, f, applicable, arginfo)
val !== nothing && return CallMeta(val, all_effects, MethodResultPure(info)) # TODO: add some sort of edge(s)

𝕃ₚ = ipo_lattice(interp)
for i in 1:napplicable
match = applicable[i]::MethodMatch
Expand Down Expand Up @@ -788,40 +784,6 @@ struct MethodCallResult
end
end

function pure_eval_eligible(interp::AbstractInterpreter,
@nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo)
# XXX we need to check that this pure function doesn't call any overlayed method
return f !== nothing &&
length(applicable) == 1 &&
is_method_pure(applicable[1]::MethodMatch) &&
is_all_const_arg(arginfo, #=start=#2)
end

function is_method_pure(method::Method, @nospecialize(sig), sparams::SimpleVector)
if isdefined(method, :generator)
method.generator.expand_early || return false
mi = specialize_method(method, sig, sparams)
isa(mi, MethodInstance) || return false
staged = get_staged(mi)
(staged isa CodeInfo && (staged::CodeInfo).pure) || return false
return true
end
return method.pure
end
is_method_pure(match::MethodMatch) = is_method_pure(match.method, match.spec_types, match.sparams)

function pure_eval_call(interp::AbstractInterpreter,
@nospecialize(f), applicable::Vector{Any}, arginfo::ArgInfo)
pure_eval_eligible(interp, f, applicable, arginfo) || return nothing
args = collect_const_args(arginfo, #=start=#2)
value = try
Core._apply_pure(f, args)
catch
return nothing
end
return Const(value)
end

# - true: eligible for concrete evaluation
# - false: eligible for semi-concrete evaluation
# - nothing: not eligible for either of it
Expand Down
1 change: 1 addition & 0 deletions base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ function compute_frameinfo(ir::IRCode, call_resolved::Bool)
check_effect_free!(ir, idx, stmt, inst[:type], 𝕃ₒ)
end
if callinfo !== nothing && isexpr(stmt, :call)
# TODO: pass effects here
callinfo[idx] = resolve_call(ir, stmt, inst[:info])
elseif isexpr(stmt, :enter)
@assert idx nstmts "try/catch inside new_nodes unsupported"
Expand Down
1 change: 1 addition & 0 deletions base/compiler/ssair/EscapeAnalysis/interprocedural.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct EACallInfo
end

function resolve_call(ir::IRCode, stmt::Expr, @nospecialize(info::CallInfo))
# TODO: if effect free, return true
sig = call_sig(ir, stmt)
if sig === nothing
return missing
Expand Down
1 change: 0 additions & 1 deletion base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,6 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance)
tree.linetable = LineInfoNode[LineInfoNode(method.module, method.name, method.file, method.line, Int32(0))]
tree.inferred = true
tree.ssaflags = UInt8[0]
tree.pure = true
set_inlineable!(tree, true)
tree.parent = mi
tree.rettype = Core.Typeof(rettype_const)
Expand Down
28 changes: 28 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,31 @@ end
end

# END 1.9 deprecations

# BEGIN 1.10 deprecations

"""
@pure ex
`@pure` gives the compiler a hint for the definition of a pure function,
helping for type inference.
!!! 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.
!!! note
In Julia 1.10 this is deprecated in favor of [`@assume_effects`](@ref).
Specifically, `@assume_effects :foldable` provides similar guarentees.
"""
macro pure(ex)
f, l = __source__.file, __source__.line
@warn "`Base.@pure ex` at $f:$l is deprecated, use `Base.@assume_effects :foldable ex` instead."
return esc(:(Base.@assume_effects :foldable $ex))
end

# END 1.10 deprecations
4 changes: 0 additions & 4 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,6 @@ function _is_internal(__module__)
return false
end

# can be used in place of `@pure` (supposed to be used for bootstrapping)
macro _pure_meta()
return _is_internal(__module__) && Expr(:meta, :pure)
end
# can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping)
macro _total_meta()
return _is_internal(__module__) && Expr(:meta, Expr(:purity,
Expand Down
27 changes: 0 additions & 27 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -339,23 +339,6 @@ macro noinline(x)
return annotate_meta_def_or_block(x, :noinline)
end

"""
@pure ex
`@pure` gives the compiler a hint for the definition of a pure function,
helping for type inference.
!!! 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)
end

"""
@constprop setting [ex]
Expand Down Expand Up @@ -703,16 +686,6 @@ the following other `setting`s:
Effect names may be prefixed by `!` to indicate that the effect should be removed
from an earlier meta effect. For example, `:total !:nothrow` indicates that while
the call is generally total, it may however throw.
---
## Comparison to `@pure`
`@assume_effects :foldable` 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
`@pure` should always be at least `:foldable`.
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...)
lastex = args[end]
Expand Down
1 change: 0 additions & 1 deletion doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ Base.@label
Base.@simd
Base.@polly
Base.@generated
Base.@pure
Base.@assume_effects
Base.@deprecate
```
Expand Down
7 changes: 1 addition & 6 deletions doc/src/devdocs/ast.md
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ A unique'd container describing the shared metadata for a single method.
Pointers to non-AST things that have been interpolated into the AST, required by
compression of the AST, type-inference, or the generation of native code.

* `nargs`, `isva`, `called`, `isstaged`, `pure`
* `nargs`, `isva`, `called`, `is_for_opaque_closure`,

Descriptive bit-fields for the source code of this Method.

Expand Down Expand Up @@ -759,11 +759,6 @@ Boolean properties:
Whether this should propagate `@inbounds` when inlined for the purpose of eliding
`@boundscheck` blocks.

* `pure`

Whether this is known to be a pure function of its arguments, without respect to the
state of the method caches or other mutable global state.


`UInt8` settings:

Expand Down
2 changes: 0 additions & 2 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ JL_DLLEXPORT jl_sym_t *jl_boundscheck_sym;
JL_DLLEXPORT jl_sym_t *jl_inbounds_sym;
JL_DLLEXPORT jl_sym_t *jl_copyast_sym;
JL_DLLEXPORT jl_sym_t *jl_cfunction_sym;
JL_DLLEXPORT jl_sym_t *jl_pure_sym;
JL_DLLEXPORT jl_sym_t *jl_loopinfo_sym;
JL_DLLEXPORT jl_sym_t *jl_meta_sym;
JL_DLLEXPORT jl_sym_t *jl_inert_sym;
Expand Down Expand Up @@ -328,7 +327,6 @@ void jl_init_common_symbols(void)
jl_newvar_sym = jl_symbol("newvar");
jl_copyast_sym = jl_symbol("copyast");
jl_loopinfo_sym = jl_symbol("loopinfo");
jl_pure_sym = jl_symbol("pure");
jl_meta_sym = jl_symbol("meta");
jl_list_sym = jl_symbol("list");
jl_unused_sym = jl_symbol("#unused#");
Expand Down
16 changes: 2 additions & 14 deletions src/ircode.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,13 +434,12 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal)
}
}

static jl_code_info_flags_t code_info_flags(uint8_t inferred, uint8_t propagate_inbounds, uint8_t pure,
static jl_code_info_flags_t code_info_flags(uint8_t inferred, uint8_t propagate_inbounds,
uint8_t has_fcall, uint8_t inlining, uint8_t constprop)
{
jl_code_info_flags_t flags;
flags.bits.inferred = inferred;
flags.bits.propagate_inbounds = propagate_inbounds;
flags.bits.pure = pure;
flags.bits.has_fcall = has_fcall;
flags.bits.inlining = inlining;
flags.bits.constprop = constprop;
Expand Down Expand Up @@ -781,7 +780,7 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
1
};

jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds, code->pure,
jl_code_info_flags_t flags = code_info_flags(code->inferred, code->propagate_inbounds,
code->has_fcall, code->inlining, code->constprop);
write_uint8(s.s, flags.packed);
write_uint8(s.s, code->purity.bits);
Expand Down Expand Up @@ -880,7 +879,6 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
code->constprop = flags.bits.constprop;
code->inferred = flags.bits.inferred;
code->propagate_inbounds = flags.bits.propagate_inbounds;
code->pure = flags.bits.pure;
code->has_fcall = flags.bits.has_fcall;
code->purity.bits = read_uint8(s.s);
code->inlining_cost = read_uint16(s.s);
Expand Down Expand Up @@ -958,16 +956,6 @@ JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data)
return flags.bits.inlining;
}

JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data)
{
if (jl_is_code_info(data))
return ((jl_code_info_t*)data)->pure;
assert(jl_typeis(data, jl_array_uint8_type));
jl_code_info_flags_t flags;
flags.packed = ((uint8_t*)data->data)[0];
return flags.bits.pure;
}

JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data)
{
if (jl_is_code_info(data))
Expand Down
1 change: 0 additions & 1 deletion src/jl_exported_funcs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,6 @@
XX(jl_ios_fd) \
XX(jl_ios_get_nbyte_int) \
XX(jl_ir_flag_inferred) \
XX(jl_ir_flag_pure) \
XX(jl_ir_flag_has_fcall) \
XX(jl_ir_flag_inlining) \
XX(jl_ir_inlining_cost) \
Expand Down
12 changes: 4 additions & 8 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2433,7 +2433,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_code_info_type =
jl_new_datatype(jl_symbol("CodeInfo"), core,
jl_any_type, jl_emptysvec,
jl_perm_symsvec(22,
jl_perm_symsvec(21,
"code",
"codelocs",
"ssavaluetypes",
Expand All @@ -2450,13 +2450,12 @@ void jl_init_types(void) JL_GC_DISABLED
"max_world",
"inferred",
"propagate_inbounds",
"pure",
"has_fcall",
"inlining",
"constprop",
"purity",
"inlining_cost"),
jl_svec(22,
jl_svec(21,
jl_array_any_type,
jl_array_int32_type,
jl_any_type,
Expand All @@ -2474,7 +2473,6 @@ void jl_init_types(void) JL_GC_DISABLED
jl_bool_type,
jl_bool_type,
jl_bool_type,
jl_bool_type,
jl_uint8_type,
jl_uint8_type,
jl_uint8_type,
Expand All @@ -2485,7 +2483,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_method_type =
jl_new_datatype(jl_symbol("Method"), core,
jl_any_type, jl_emptysvec,
jl_perm_symsvec(29,
jl_perm_symsvec(28,
"name",
"module",
"file",
Expand All @@ -2511,11 +2509,10 @@ void jl_init_types(void) JL_GC_DISABLED
"nospecialize",
"nkw",
"isva",
"pure",
"is_for_opaque_closure",
"constprop",
"purity"),
jl_svec(29,
jl_svec(28,
jl_symbol_type,
jl_module_type,
jl_symbol_type,
Expand All @@ -2542,7 +2539,6 @@ void jl_init_types(void) JL_GC_DISABLED
jl_int32_type,
jl_bool_type,
jl_bool_type,
jl_bool_type,
jl_uint8_type,
jl_uint8_type),
jl_emptysvec,
Expand Down
3 changes: 0 additions & 3 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ typedef struct _jl_code_info_t {
// various boolean properties:
uint8_t inferred;
uint8_t propagate_inbounds;
uint8_t pure;
uint8_t has_fcall;
// uint8 settings
uint8_t inlining; // 0 = default; 1 = @inline; 2 = @noinline
Expand Down Expand Up @@ -342,7 +341,6 @@ typedef struct _jl_method_t {
// of another method.
// various boolean properties
uint8_t isva;
uint8_t pure;
uint8_t is_for_opaque_closure;
// uint8 settings
uint8_t constprop; // 0x00 = use heuristic; 0x01 = aggressive; 0x02 = none
Expand Down Expand Up @@ -1843,7 +1841,6 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code);
JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data);
JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) JL_NOTSAFEPOINT;
JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data) JL_NOTSAFEPOINT;
JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data) JL_NOTSAFEPOINT;
JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data) JL_NOTSAFEPOINT;
JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_array_t *data) JL_NOTSAFEPOINT;
JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data) JL_NOTSAFEPOINT;
Expand Down
2 changes: 0 additions & 2 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,6 @@ STATIC_INLINE jl_value_t *undefref_check(jl_datatype_t *dt, jl_value_t *v) JL_NO
typedef struct {
uint8_t inferred:1;
uint8_t propagate_inbounds:1;
uint8_t pure:1;
uint8_t has_fcall:1;
uint8_t inlining:2; // 0 = use heuristic; 1 = aggressive; 2 = none
uint8_t constprop:2; // 0 = use heuristic; 1 = aggressive; 2 = none
Expand Down Expand Up @@ -1570,7 +1569,6 @@ extern JL_DLLEXPORT jl_sym_t *jl_boundscheck_sym;
extern JL_DLLEXPORT jl_sym_t *jl_inbounds_sym;
extern JL_DLLEXPORT jl_sym_t *jl_copyast_sym;
extern JL_DLLEXPORT jl_sym_t *jl_cfunction_sym;
extern JL_DLLEXPORT jl_sym_t *jl_pure_sym;
extern JL_DLLEXPORT jl_sym_t *jl_loopinfo_sym;
extern JL_DLLEXPORT jl_sym_t *jl_meta_sym;
extern JL_DLLEXPORT jl_sym_t *jl_inert_sym;
Expand Down
Loading

0 comments on commit 2ce9060

Please sign in to comment.