From ff14380f1eabcf31d60312a86a945b85331f252c Mon Sep 17 00:00:00 2001 From: oscarddssmith Date: Wed, 8 Feb 2023 09:03:11 -0500 Subject: [PATCH 1/3] deprecate @pure and make it just @assume_effects :foldable --- base/compiler/abstractinterpretation.jl | 4 ++-- base/compiler/ssair/inlining.jl | 2 +- base/compiler/stmtinfo.jl | 3 +-- base/deprecated.jl | 19 +++++++++++++++++ base/expr.jl | 27 ------------------------- test/compiler/contextual.jl | 12 ----------- test/compiler/inference.jl | 25 ++--------------------- test/compiler/inline.jl | 12 ----------- 8 files changed, 25 insertions(+), 79 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 1bc2af92699be..cf4078d6f7ccf 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2038,13 +2038,13 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), elseif la == 2 && (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && istopfunction(f, :length) - # mark length(::SimpleVector) as @pure + # mark length(::SimpleVector) as total return CallMeta(Const(length(svecval)), EFFECTS_TOTAL, MethodResultPure()) elseif la == 3 && (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && (a3 = argtypes[3]; isa(a3, Const)) && (idx = a3.val; isa(idx, Int)) && istopfunction(f, :getindex) - # mark getindex(::SimpleVector, i::Int) as @pure + # mark getindex(::SimpleVector, i::Int) as total if 1 <= idx <= length(svecval) && isassigned(svecval, idx) return CallMeta(Const(getindex(svecval, idx)), EFFECTS_TOTAL, MethodResultPure()) end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index efb30c05811d0..c54f84e9bc3f7 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1655,7 +1655,7 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) continue end - # Check whether this call was @pure and evaluates to a constant + # Check whether this call was total and evaluates to a constant if info isa MethodResultPure inline_const_if_inlineable!(ir[SSAValue(idx)]) && continue info = info.info diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 23f8c3aba908e..8b3a6d175cd96 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -95,8 +95,7 @@ getresult_impl(info::ConstCallInfo, idx::Int) = info.results[idx] info::MethodResultPure <: CallInfo This struct represents a method result constant was proven to be -effect-free, including being no-throw (typically because the value was computed -by calling an `@pure` function). +effect-free, including being no-throw """ struct MethodResultPure <: CallInfo info::CallInfo diff --git a/base/deprecated.jl b/base/deprecated.jl index 79ae852ff22b1..cb2b9f43e898b 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -362,3 +362,22 @@ end end # END 1.9 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. +""" +@deprecate +macro pure(ex) + esc(isa(ex, Expr) ? :(Base.@assume_effects :foldable $ex) : ex) +end diff --git a/base/expr.jl b/base/expr.jl index 0e6d73c9722d1..46e89bf64da8a 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -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] @@ -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] diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index 79285f62b0947..ba13f175c674b 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -119,23 +119,11 @@ f() = 2 # Test that MiniCassette is at least somewhat capable by overdubbing gcd @test overdub(Ctx(), gcd, 10, 20) === gcd(10, 20) -# Test that pure propagates for Cassette -Base.@pure isbitstype(T) = Base.isbitstype(T) -f31012(T) = Val(isbitstype(T)) -@test @inferred(overdub(Ctx(), f31012, Int64)) == Val(true) - @generated bar(::Val{align}) where {align} = :(42) foo(i) = i+bar(Val(1)) @test @inferred(overdub(Ctx(), foo, 1)) == 43 -# Check that misbehaving pure functions propagate their error -Base.@pure func1() = 42 -Base.@pure func2() = (this_is_an_exception; func1()) -func3() = func2() -@test_throws UndefVarError func3() - - # overlay method tables # ===================== diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 04de79292e4c6..ff6191b682e70 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -563,27 +563,6 @@ f18450() = ifelse(true, Tuple{Vararg{Int}}, Tuple{Vararg}) # issue #18569 @test !Core.Compiler.isconstType(Type{Tuple}) -# ensure pure attribute applies correctly to all signatures of fpure -Base.@pure function fpure(a=rand(); b=rand()) - # use the `rand` function since it is known to be `@inline` - # but would be too big to inline - return a + b + rand() -end -gpure() = fpure() -gpure(x::Irrational) = fpure(x) -@test which(fpure, ()).pure -@test which(fpure, (typeof(pi),)).pure -@test !which(gpure, ()).pure -@test !which(gpure, (typeof(pi),)).pure -@test code_typed(gpure, ())[1][1].pure -@test code_typed(gpure, (typeof(π),))[1][1].pure -@test gpure() == gpure() == gpure() -@test gpure(π) == gpure(π) == gpure(π) - -# Make sure @pure works for functions using the new syntax -Base.@pure (fpure2(x::T) where T) = T -@test which(fpure2, (Int64,)).pure - # issue #10880 function cat10880(a, b) Tuple{a.parameters..., b.parameters...} @@ -4716,8 +4695,8 @@ end |> only === Type{Float64} global it_count47688 = 0 struct CountsIterate47688{N}; end function Base.iterate(::CountsIterate47688{N}, n=0) where N - global it_count47688 += 1 - n <= N ? (n, n+1) : nothing + global it_count47688 += 1 + n <= N ? (n, n+1) : nothing end foo47688() = tuple(CountsIterate47688{5}()...) bar47688() = foo47688() diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index cfcfc7228b3ed..28713ea857c07 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -360,18 +360,6 @@ struct RealConstrained{T <: Real}; end @test !fully_eliminated(x->(RealConstrained{x}; nothing), Tuple{Int}) @test !fully_eliminated(x->(RealConstrained{x}; nothing), Tuple{Type{Vector{T}} where T}) -# Check that pure functions with non-inlineable results still get deleted -struct Big - x::NTuple{1024, Int} -end -Base.@pure Big() = Big(ntuple(identity, 1024)) -function pure_elim_full() - Big() - nothing -end - -@test fully_eliminated(pure_elim_full, Tuple{}) - # Union splitting of convert f_convert_missing(x) = convert(Int64, x) let ci = code_typed(f_convert_missing, Tuple{Union{Int64, Missing}})[1][1], From 88a2814b9f814879c6071c3ca2ff6a74af59cfa5 Mon Sep 17 00:00:00 2001 From: oscarddssmith Date: Wed, 8 Feb 2023 16:40:33 -0500 Subject: [PATCH 2/3] remove abstractinterpretation of pure? --- base/compiler/abstractinterpretation.jl | 119 +++++------------- .../ssair/EscapeAnalysis/EscapeAnalysis.jl | 1 + .../ssair/EscapeAnalysis/interprocedural.jl | 7 +- base/compiler/ssair/inlining.jl | 6 - base/compiler/stmtinfo.jl | 16 +-- base/compiler/tfuncs.jl | 2 +- base/deprecated.jl | 6 +- 7 files changed, 43 insertions(+), 114 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index cf4078d6f7ccf..d2fe0d34f8040 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -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 @@ -117,69 +113,34 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), break end this_rt = Bottom - splitunions = false - # TODO: this used to trigger a bug in inference recursion detection, and is unmaintained now - # sigtuple = unwrap_unionall(sig)::DataType - # splitunions = 1 < unionsplitcost(sigtuple.parameters) * napplicable <= InferenceParams(interp).max_union_splitting - if splitunions - splitsigs = switchtupleunion(sig) - for sig_n in splitsigs - result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, si, sv) - (; rt, edge, effects) = result - this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] - this_arginfo = ArgInfo(fargs, this_argtypes) - const_call_result = abstract_call_method_with_const_args(interp, - result, f, this_arginfo, si, match, sv) - const_result = nothing - if const_call_result !== nothing - if const_call_result.rt ⊑ₚ rt - rt = const_call_result.rt - (; effects, const_result, edge) = const_call_result - else - add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") - end - end - all_effects = merge_effects(all_effects, effects) - push!(const_results, const_result) - any_const_result |= const_result !== nothing - edge === nothing || push!(edges, edge) - this_rt = tmerge(this_rt, rt) - if bail_out_call(interp, this_rt, sv) - break - end - end - this_conditional = ignorelimited(this_rt) - this_rt = widenwrappedconditional(this_rt) - else - result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, si, sv) - (; rt, edge, effects) = result - this_conditional = ignorelimited(rt) - this_rt = widenwrappedconditional(rt) - # try constant propagation with argtypes for this match - # this is in preparation for inlining, or improving the return result - this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] - this_arginfo = ArgInfo(fargs, this_argtypes) - const_call_result = abstract_call_method_with_const_args(interp, - result, f, this_arginfo, si, match, sv) - const_result = nothing - if const_call_result !== nothing - this_const_conditional = ignorelimited(const_call_result.rt) - this_const_rt = widenwrappedconditional(const_call_result.rt) - # return type of const-prop' inference can be wider than that of non const-prop' inference - # e.g. in cases when there are cycles but cached result is still accurate - if this_const_rt ⊑ₚ this_rt - this_conditional = this_const_conditional - this_rt = this_const_rt - (; effects, const_result, edge) = const_call_result - else - add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") - end + result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, si, sv) + (; rt, edge, effects) = result + this_conditional = ignorelimited(rt) + this_rt = widenwrappedconditional(rt) + # try constant propagation with argtypes for this match + # this is in preparation for inlining, or improving the return result + this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] + this_arginfo = ArgInfo(fargs, this_argtypes) + const_call_result = abstract_call_method_with_const_args(interp, + result, f, this_arginfo, si, match, sv) + const_result = nothing + if const_call_result !== nothing + this_const_conditional = ignorelimited(const_call_result.rt) + this_const_rt = widenwrappedconditional(const_call_result.rt) + # return type of const-prop' inference can be wider than that of non const-prop' inference + # e.g. in cases when there are cycles but cached result is still accurate + if this_const_rt ⊑ₚ this_rt + this_conditional = this_const_conditional + this_rt = this_const_rt + (; effects, const_result, edge) = const_call_result + else + add_remark!(interp, sv, "[constprop] Discarded because the result was wider than inference") end - all_effects = merge_effects(all_effects, effects) - push!(const_results, const_result) - any_const_result |= const_result !== nothing - edge === nothing || push!(edges, edge) end + all_effects = merge_effects(all_effects, effects) + push!(const_results, const_result) + any_const_result |= const_result !== nothing + edge === nothing || push!(edges, edge) @assert !(this_conditional isa Conditional || this_rt isa MustAlias) "invalid lattice element returned from inter-procedural context" seen += 1 rettype = tmerge(𝕃ₚ, rettype, this_rt) @@ -788,15 +749,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 @@ -810,11 +762,6 @@ function is_method_pure(method::Method, @nospecialize(sig), sparams::SimpleVecto 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 - return _pure_eval_call(f, arginfo) -end function _pure_eval_call(@nospecialize(f), arginfo::ArgInfo) args = collect_const_args(arginfo, #=start=#2) value = try @@ -2022,7 +1969,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), if isa(rty, Conditional) return CallMeta(Conditional(rty.slot, rty.elsetype, rty.thentype), EFFECTS_TOTAL, NoCallInfo()) # swap if-else elseif isa(rty, Const) - return CallMeta(Const(rty.val === false), EFFECTS_TOTAL, MethodResultPure()) + return CallMeta(Const(rty.val === false), EFFECTS_TOTAL, NoCallInfo()) end return CallMeta(rty, EFFECTS_TOTAL, NoCallInfo()) elseif la == 3 && istopfunction(f, :(>:)) @@ -2038,22 +1985,20 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), elseif la == 2 && (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && istopfunction(f, :length) - # mark length(::SimpleVector) as total - return CallMeta(Const(length(svecval)), EFFECTS_TOTAL, MethodResultPure()) + return CallMeta(Const(length(svecval)), EFFECTS_TOTAL, NoCallInfo()) elseif la == 3 && (a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) && (a3 = argtypes[3]; isa(a3, Const)) && (idx = a3.val; isa(idx, Int)) && istopfunction(f, :getindex) - # mark getindex(::SimpleVector, i::Int) as total if 1 <= idx <= length(svecval) && isassigned(svecval, idx) - return CallMeta(Const(getindex(svecval, idx)), EFFECTS_TOTAL, MethodResultPure()) + return CallMeta(Const(getindex(svecval, idx)), EFFECTS_TOTAL, NoCallInfo()) end elseif la == 2 && istopfunction(f, :typename) - return CallMeta(typename_static(argtypes[2]), EFFECTS_TOTAL, MethodResultPure()) + return CallMeta(typename_static(argtypes[2]), EFFECTS_TOTAL, NoCallInfo()) elseif la == 3 && istopfunction(f, :typejoin) if is_all_const_arg(arginfo, #=start=#2) val = _pure_eval_call(f, arginfo) - return CallMeta(val === nothing ? Type : val, EFFECTS_TOTAL, MethodResultPure()) + return CallMeta(val === nothing ? Type : val, EFFECTS_TOTAL, NoCallInfo()) end end atype = argtypes_to_type(argtypes) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index 729e9a9a49b94..8bc173add6eaa 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -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" diff --git a/base/compiler/ssair/EscapeAnalysis/interprocedural.jl b/base/compiler/ssair/EscapeAnalysis/interprocedural.jl index d87b0edaf295e..0bbca8699416d 100644 --- a/base/compiler/ssair/EscapeAnalysis/interprocedural.jl +++ b/base/compiler/ssair/EscapeAnalysis/interprocedural.jl @@ -2,7 +2,7 @@ import Core.Compiler: MethodInstance, InferenceResult, Signature, ConstPropResult, ConcreteResult, - SemiConcreteResult, CallInfo, NoCallInfo, MethodResultPure, MethodMatchInfo, + SemiConcreteResult, CallInfo, NoCallInfo, MethodMatchInfo, UnionSplitInfo, ConstCallInfo, InvokeCallInfo, call_sig, argtypes_to_type, is_builtin, is_return_type, istopfunction, validate_sparams, specialize_method, invoke_rewrite @@ -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 @@ -35,9 +36,7 @@ function resolve_call(ir::IRCode, stmt::Expr, @nospecialize(info::CallInfo)) elseif is_return_type(f) return true end - if info isa MethodResultPure - return true - elseif info === NoCallInfo + if info === NoCallInfo return missing end # TODO handle OpaqueClosureCallInfo diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index c54f84e9bc3f7..94ef83562b6b3 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1559,7 +1559,6 @@ end function handle_modifyfield!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyFieldInfo, state::InliningState) info = info.info - info isa MethodResultPure && (info = info.info) info isa ConstCallInfo && (info = info.call) info isa MethodMatchInfo || return nothing length(info.results) == 1 || return nothing @@ -1655,11 +1654,6 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) continue end - # Check whether this call was total and evaluates to a constant - if info isa MethodResultPure - inline_const_if_inlineable!(ir[SSAValue(idx)]) && continue - info = info.info - end if info === NoCallInfo() # Inference determined this couldn't be analyzed. Don't question it. continue diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 8b3a6d175cd96..815582f2a230f 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -62,7 +62,7 @@ end struct ConcreteResult mi::MethodInstance - effects::Effects + effects::Effectsz result ConcreteResult(mi::MethodInstance, effects::Effects) = new(mi, effects) ConcreteResult(mi::MethodInstance, effects::Effects, @nospecialize val) = new(mi, effects, val) @@ -91,20 +91,6 @@ nsplit_impl(info::ConstCallInfo) = nsplit(info.call) getsplit_impl(info::ConstCallInfo, idx::Int) = getsplit(info.call, idx) getresult_impl(info::ConstCallInfo, idx::Int) = info.results[idx] -""" - info::MethodResultPure <: CallInfo - -This struct represents a method result constant was proven to be -effect-free, including being no-throw -""" -struct MethodResultPure <: CallInfo - info::CallInfo -end -let instance = MethodResultPure(NoCallInfo()) - global MethodResultPure - MethodResultPure() = instance -end - """ ainfo::AbstractIterationInfo diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 71dcd0ff97ea0..ff668996e00a7 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2489,7 +2489,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s else call = abstract_call(interp, ArgInfo(nothing, argtypes_vec), si, sv, -1) end - info = verbose_stmt_info(interp) ? MethodResultPure(ReturnTypeCallInfo(call.info)) : MethodResultPure() + info = verbose_stmt_info(interp) ? ReturnTypeCallInfo(call.info) : NoCallInfo() rt = widenslotwrapper(call.rt) if isa(rt, Const) # output was computed to be constant diff --git a/base/deprecated.jl b/base/deprecated.jl index cb2b9f43e898b..7bf48db3814b9 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -376,8 +376,12 @@ helping for type inference. 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 :total` provides similar guarentees. """ @deprecate macro pure(ex) - esc(isa(ex, Expr) ? :(Base.@assume_effects :foldable $ex) : ex) + esc(isa(ex, Expr) ? :(Base.@assume_effects :total $ex) : ex) end From caac516ce2e2e484c398730b0f1391ea3bd3ab08 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Wed, 8 Feb 2023 17:09:50 -0500 Subject: [PATCH 3/3] Update base/compiler/stmtinfo.jl Co-authored-by: Valentin Churavy --- base/compiler/stmtinfo.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 815582f2a230f..855ded303770b 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -62,7 +62,7 @@ end struct ConcreteResult mi::MethodInstance - effects::Effectsz + effects::Effects result ConcreteResult(mi::MethodInstance, effects::Effects) = new(mi, effects) ConcreteResult(mi::MethodInstance, effects::Effects, @nospecialize val) = new(mi, effects, val)