Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Compiler/src/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3499,6 +3499,20 @@ function merge_override_effects!(interp::AbstractInterpreter, effects::Effects,
# It is possible for arguments (GlobalRef/:static_parameter) to throw,
# but these will be recomputed during SSA construction later.
override = decode_statement_effects_override(sv)
if override.consistent
m = sv.linfo.def
if isa(m, Method)
# N.B.: We'd like deleted_world here, but we can't add an appropriate edge at this point.
# However, in order to reach here in the first place, ordinary method lookup would have
# had to add an edge and appropriate invalidation trigger.
valid_worlds = WorldRange(m.primary_world, typemax(Int))
if sv.world.this in valid_worlds
update_valid_age!(sv, valid_worlds)
else
override = EffectsOverride(override, consistent=false)
end
end
end
effects = override_effects(effects, override)
set_curr_ssaflag!(sv, flags_for_effects(effects), IR_FLAGS_EFFECTS)
merge_effects!(interp, sv, effects)
Expand Down
29 changes: 21 additions & 8 deletions Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,17 @@ function cycle_fix_limited(@nospecialize(typ), sv::InferenceState, cycleid::Int)
return typ
end

function adjust_effects(ipo_effects::Effects, def::Method)
function adjust_effects(ipo_effects::Effects, def::Method, world::UInt)
# override the analyzed effects using manually annotated effect settings
override = decode_effects_override(def.purity)
valid_worlds = WorldRange(0, typemax(UInt))
if is_effect_overridden(override, :consistent)
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
# See note on `typemax(Int)` instead of `deleted_world` in adjust_effects!
override_valid_worlds = WorldRange(def.primary_world, typemax(Int))
if world in override_valid_worlds
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
valid_worlds = override_valid_worlds
end
end
if is_effect_overridden(override, :effect_free)
ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
Expand Down Expand Up @@ -400,7 +406,7 @@ function adjust_effects(ipo_effects::Effects, def::Method)
if is_effect_overridden(override, :nortcall)
ipo_effects = Effects(ipo_effects; nortcall=true)
end
return ipo_effects
return (ipo_effects, valid_worlds)
end

function adjust_effects(sv::InferenceState)
Expand Down Expand Up @@ -454,7 +460,8 @@ function adjust_effects(sv::InferenceState)
# override the analyzed effects using manually annotated effect settings
def = sv.linfo.def
if isa(def, Method)
ipo_effects = adjust_effects(ipo_effects, def)
(ipo_effects, valid_worlds) = adjust_effects(ipo_effects, def, sv.world.this)
update_valid_age!(sv, valid_worlds)
end

return ipo_effects
Expand Down Expand Up @@ -492,9 +499,9 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter, cycleid::
end
end
result = me.result
result.valid_worlds = me.world.valid_worlds
result.result = bestguess
ipo_effects = result.ipo_effects = me.ipo_effects = adjust_effects(me)
result.valid_worlds = me.world.valid_worlds
result.exc_result = me.exc_bestguess = refine_exception_type(me.exc_bestguess, ipo_effects)
me.src.rettype = widenconst(ignorelimited(bestguess))
me.src.ssaflags = me.ssaflags
Expand Down Expand Up @@ -957,8 +964,13 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
update_valid_age!(caller, frame.world.valid_worlds)
local isinferred = is_inferred(frame)
local edge = isinferred ? edge_ci : nothing
local effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects
adjust_effects(effects_for_cycle(frame.ipo_effects), method)
local effects, valid_worlds
if isinferred
effects = frame.result.ipo_effects # effects are adjusted already within `finish` for ipo_effects
else
(effects, valid_worlds) = adjust_effects(effects_for_cycle(frame.ipo_effects), method, frame.world.this)
update_valid_age!(caller, valid_worlds)
end
local bestguess = frame.bestguess
local exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
# propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
Expand All @@ -981,7 +993,8 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
# return the current knowledge about this cycle
frame = frame::InferenceState
update_valid_age!(caller, frame.world.valid_worlds)
effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method)
(effects, valid_worlds) = adjust_effects(effects_for_cycle(frame.ipo_effects), method, frame.world.this)
update_valid_age!(caller, valid_worlds)
bestguess = frame.bestguess
exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
return Future(MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects, nothing, edgecycle, edgelimited))
Expand Down
14 changes: 9 additions & 5 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -540,16 +540,20 @@ The `:consistent` setting asserts that for egal (`===`) inputs:
contents) are not egal.

!!! note
The `:consistent`-cy assertion is made world-age wise. More formally, write
``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then this setting requires:
The `:consistent`-cy assertion is made with respect to a particular world range `R`.
More formally, write ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then this setting requires:
```math
∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y)
∀ i ∈ R, j ∈ R, x, y: x ≡ y → fᵢ(x) ≡ fⱼ(y)
```
However, for two world ages ``i``, ``j`` s.t. ``i ≠ j``, we may have ``fᵢ(x) ≢ fⱼ(y)``.

For `@assume_effects`, the range `R` is `m.primary_world:m.deleted_world` of
the annotated or containing method.

For ordinary code instances, `R` is `ci.min_world:ci.max_world`.

A further implication is that `:consistent` functions may not make their
return value dependent on the state of the heap or any other global state
that is not constant for a given world age.
that is not constant over the given world age range.

!!! note
The `:consistent`-cy includes all legal rewrites performed by the optimizer.
Expand Down