Skip to content

Commit bdb3f1c

Browse files
authored
bpart: Give a warning when accessing a backdated const binding (JuliaLang#57133)
This implements the strategy proposed in JuliaLang#57102 (comment). Example: ``` julia> function foo(i) eval(:(const x = $i)) x end foo (generic function with 1 method) julia> foo(1) WARNING: Detected access to binding Main.x in a world prior to its definition world. Julia 1.12 has introduced more strict world age semantics for global bindings. !!! This code may malfunction under Revise. !!! This code will error in future versions of Julia. Hint: Add an appropriate `invokelatest` around the access to this binding. 1 ``` The warning is triggered once per binding to avoid spamming for repeated access.
1 parent 19a10f6 commit bdb3f1c

File tree

5 files changed

+18
-7
lines changed

5 files changed

+18
-7
lines changed

src/Compiler.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc
4949

5050
using Base
5151
using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer,
52-
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
52+
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST,
53+
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
5354
EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES,
5455
OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME,
5556
_array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any,

src/abstractinterpretation.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,6 +3524,11 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co
35243524
end
35253525

35263526
if is_defined_const_binding(kind)
3527+
if kind == BINDING_KIND_BACKDATED_CONST
3528+
# Infer this as guard. We do not want a later const definition to retroactively improve
3529+
# inference results in an earlier world.
3530+
return RTEffects(Any, UndefVarError, generic_getglobal_effects)
3531+
end
35273532
rt = Const(partition_restriction(partition))
35283533
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE))
35293534
end

src/ssair/slot2ssa.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,6 @@ function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode
137137
return nothing
138138
end
139139
op[] = x
140-
elseif isa(val, GlobalRef) && !(isdefined(val.mod, val.name) && isconst(val.mod, val.name))
141-
typ = typ_for_val(val, ci, ir, idx, Any[])
142-
new_inst = NewInstruction(val, typ)
143-
op[] = NewSSAValue(insert_node!(ir, idx, new_inst).id - length(ir.stmts))
144140
elseif isexpr(val, :static_parameter)
145141
ty = typ_for_val(val, ci, ir, idx, Any[])
146142
if isa(ty, Const)

src/ssair/verify.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
121121
if mi !== nothing
122122
push!(error_args, "\n", " Method instance: ", mi)
123123
end
124-
error(error_args...)
124+
invokelatest(error, error_args...)
125125
end
126126
# Verify CFG graph. Must be well formed to construct domtree
127127
if !(length(ir.cfg.blocks) - 1 <= length(ir.cfg.index) <= length(ir.cfg.blocks))
@@ -380,6 +380,15 @@ function verify_ir(ir::IRCode, print::Bool=true,
380380
# undefined GlobalRef is OK in isdefined
381381
continue
382382
end
383+
elseif stmt.head === :throw_undef_if_not
384+
if length(stmt.args) > 3
385+
@verify_error "malformed throw_undef_if_not"
386+
raise_error()
387+
end
388+
if stmt.args[1] isa GlobalRef
389+
# undefined GlobalRef is OK in throw_undef_if_not
390+
continue
391+
end
383392
elseif stmt.head === :gc_preserve_end
384393
# We allow gc_preserve_end tokens to span across try/catch
385394
# blocks, which isn't allowed for regular SSA values, so

test/invalidation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ let mi = Base.method_instance(basic_caller, (Float64,))
5555
end
5656

5757
# this redefinition below should invalidate the cache
58-
const BASIC_CALLER_WORLD = Base.get_world_counter()
58+
const BASIC_CALLER_WORLD = Base.get_world_counter()+1
5959
basic_callee(x) = x, x
6060
@test !isdefined(Base.method_instance(basic_callee, (Float64,)), :cache)
6161
let mi = Base.method_instance(basic_caller, (Float64,))

0 commit comments

Comments
 (0)