Skip to content

Commit 6a3b6e5

Browse files
vtjnashclaude
andcommitted
bindings: implement latestworld retry for PARTITION_KIND_GUARD bindings
Add world counter retry mechanism to allow functions from older world ages to access bindings defined in newer worlds when encountering guard partitions. This better matches old v1.11 and before semantics, avoiding breakage and confusion. Key changes: - Modified jl_get_binding_value_in_world, jl_get_binding_value_seqcst, jl_get_binding_value_depwarn, and jl_boundp to retry with current world counter when kind == PARTITION_KIND_GUARD - Added pure callback check to disable retry when in_pure_callback is true - Removed all PARTITION_KIND_BACKDATED_CONST code and references - Added tests for newly defined bindings and isdefined checks This enables dynamic binding resolution when the lookup would otherwise have failed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 406027e commit 6a3b6e5

File tree

10 files changed

+136
-117
lines changed

10 files changed

+136
-117
lines changed

Compiler/src/Compiler.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc
4444
typename, unsafe_write, write, stdout, stderr
4545

4646
using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer,
47-
PARTITION_KIND_GLOBAL, PARTITION_KIND_UNDEF_CONST, PARTITION_KIND_BACKDATED_CONST, PARTITION_KIND_DECLARED,
47+
PARTITION_KIND_GLOBAL, PARTITION_KIND_UNDEF_CONST, PARTITION_KIND_DECLARED,
4848
PARTITION_FLAG_DEPWARN,
4949
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
5050
EffectsOverride, Filter, Generator, NUM_EFFECTS_OVERRIDES,

Compiler/src/abstractinterpretation.jl

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3644,26 +3644,20 @@ function abstract_eval_partition_load(interp::Union{AbstractInterpreter,Nothing}
36443644
if interp !== nothing && InferenceParams(interp).assume_bindings_static
36453645
return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS)
36463646
else
3647-
# We do not currently assume an invalidation for guard -> defined transitions
3648-
# return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS)
3647+
# this will get the invokelatest binding at runtime
36493648
return RTEffects(Any, UndefVarError, local_getglobal_effects)
36503649
end
36513650
end
36523651

36533652
if is_defined_const_binding(kind)
3654-
if kind == PARTITION_KIND_BACKDATED_CONST
3655-
# Infer this as guard. We do not want a later const definition to retroactively improve
3656-
# inference results in an earlier world.
3657-
return RTEffects(Any, UndefVarError, local_getglobal_effects)
3658-
end
36593653
rt = Const(partition_restriction(partition))
36603654
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL,
36613655
inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE,
36623656
effect_free=isdepwarn ? ALWAYS_FALSE : ALWAYS_TRUE))
36633657
end
36643658

36653659
if kind == PARTITION_KIND_DECLARED
3666-
# Could be replaced by a backdated const which has an effect, so we can't assume it won't.
3660+
# Could be replaced by a const which has an effect, so we can't assume it won't.
36673661
# Besides, we would prefer not to merge the world range for this into the world range for
36683662
# _GLOBAL, because that would pessimize codegen.
36693663
effects = Effects(local_getglobal_effects, effect_free=ALWAYS_FALSE)

base/errorshow.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,8 +1271,6 @@ function UndefVarError_hint(io::IO, ex::UndefVarError)
12711271
"with the module it should come from.")
12721272
elseif is_some_explicit_imported(kind)
12731273
print(io, "\nSuggestion: this global was defined as `$(partition_restriction(bpart).globalref)` but not assigned a value.")
1274-
elseif kind === PARTITION_KIND_BACKDATED_CONST
1275-
print(io, "\nSuggestion: define the const at top-level before running function that uses it (stricter Julia v1.12+ rule).")
12761274
end
12771275

12781276
# Check if binding kind changed between the error's world and current world

base/runtime_internals.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,6 @@ const PARTITION_KIND_FAILED = 0x7
245245
const PARTITION_KIND_DECLARED = 0x8
246246
const PARTITION_KIND_GUARD = 0x9
247247
const PARTITION_KIND_UNDEF_CONST = 0xa
248-
const PARTITION_KIND_BACKDATED_CONST = 0xb
249248

250249
const PARTITION_FLAG_EXPORTED = 0x10
251250
const PARTITION_FLAG_DEPRECATED = 0x20
@@ -254,9 +253,9 @@ const PARTITION_FLAG_DEPWARN = 0x40
254253
const PARTITION_MASK_KIND = 0x0f
255254
const PARTITION_MASK_FLAG = 0xf0
256255

257-
const BINDING_FLAG_ANY_IMPLICIT_EDGES = 0x8
256+
const BINDING_FLAG_ANY_IMPLICIT_EDGES = 0x4
258257

259-
is_defined_const_binding(kind::UInt8) = (kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT || kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_BACKDATED_CONST)
258+
is_defined_const_binding(kind::UInt8) = (kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT || kind == PARTITION_KIND_IMPLICIT_CONST)
260259
is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == PARTITION_KIND_UNDEF_CONST)
261260
is_some_imported(kind::UInt8) = (kind == PARTITION_KIND_IMPLICIT_GLOBAL || kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_EXPLICIT || kind == PARTITION_KIND_IMPORTED)
262261
is_some_implicit(kind::UInt8) = (kind == PARTITION_KIND_IMPLICIT_GLOBAL || kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_GUARD || kind == PARTITION_KIND_FAILED)

base/show.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3306,10 +3306,7 @@ function print_partition(io::IO, partition::Core.BindingPartition)
33063306
end
33073307
print(io, " - ")
33083308
kind = binding_kind(partition)
3309-
if kind == PARTITION_KIND_BACKDATED_CONST
3310-
print(io, "backdated constant binding to ")
3311-
print(io, partition_restriction(partition))
3312-
elseif kind == PARTITION_KIND_CONST
3309+
if kind == PARTITION_KIND_CONST
33133310
print(io, "constant binding to ")
33143311
print(io, partition_restriction(partition))
33153312
elseif kind == PARTITION_KIND_CONST_IMPORT

src/julia.h

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -694,14 +694,6 @@ typedef struct _jl_weakref_t {
694694
//
695695
// We may make this list more permissive in the future.
696696
//
697-
// Finally, PARTITION_KIND_BACKDATED_CONST is a special case, and the only case where we may replace an
698-
// existing partition by a different partition kind in the same world age. As such, it needs special
699-
// support in inference. Any partition kind that may be replaced by a PARTITION_KIND_BACKDATED_CONST
700-
// must be inferred accordingly. PARTITION_KIND_BACKDATED_CONST is intended as a temporary compatibility
701-
// measure. The following kinds may be replaced by PARTITION_KIND_BACKDATED_CONST:
702-
// - PARTITION_KIND_GUARD
703-
// - PARTITION_KIND_FAILED
704-
// - PARTITION_KIND_DECLARED
705697
enum jl_partition_kind {
706698
// Constant: This binding partition is a constant declared using `const _ = ...`
707699
// ->restriction holds the constant value
@@ -740,9 +732,6 @@ enum jl_partition_kind {
740732
// without a value.
741733
// ->restriction is NULL
742734
PARTITION_KIND_UNDEF_CONST = 0xa,
743-
// Backated constant. A constant that was backdated for compatibility. In all other
744-
// ways equivalent to PARTITION_KIND_CONST, but prints a warning on access
745-
PARTITION_KIND_BACKDATED_CONST = 0xb,
746735

747736
// This is not a real binding kind, but can be used to ask for a re-resolution
748737
// of the implicit binding kind
@@ -798,13 +787,12 @@ STATIC_INLINE enum jl_partition_kind jl_binding_kind(jl_binding_partition_t *bpa
798787
}
799788

800789
enum jl_binding_flags {
801-
BINDING_FLAG_DID_PRINT_BACKDATE_ADMONITION = 0x1,
802-
BINDING_FLAG_DID_PRINT_IMPLICIT_IMPORT_ADMONITION = 0x2,
790+
BINDING_FLAG_DID_PRINT_IMPLICIT_IMPORT_ADMONITION = 0x1,
803791
// `export` is tracked in partitions, but sets this as well
804-
BINDING_FLAG_PUBLICP = 0x4,
792+
BINDING_FLAG_PUBLICP = 0x2,
805793
// Set if any methods defined in this module implicitly reference
806794
// this binding. If not, invalidation is optimized.
807-
BINDING_FLAG_ANY_IMPLICIT_EDGES = 0x8
795+
BINDING_FLAG_ANY_IMPLICIT_EDGES = 0x4
808796
};
809797

810798
typedef struct _jl_binding_t {

src/julia_internal.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,11 +1014,11 @@ STATIC_INLINE int jl_bkind_is_some_implicit(enum jl_partition_kind kind) JL_NOTS
10141014
}
10151015

10161016
STATIC_INLINE int jl_bkind_is_some_constant(enum jl_partition_kind kind) JL_NOTSAFEPOINT {
1017-
return kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT || kind == PARTITION_KIND_UNDEF_CONST || kind == PARTITION_KIND_BACKDATED_CONST;
1017+
return kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT || kind == PARTITION_KIND_UNDEF_CONST;
10181018
}
10191019

10201020
STATIC_INLINE int jl_bkind_is_defined_constant(enum jl_partition_kind kind) JL_NOTSAFEPOINT {
1021-
return kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT || kind == PARTITION_KIND_BACKDATED_CONST;
1021+
return kind == PARTITION_KIND_IMPLICIT_CONST || kind == PARTITION_KIND_CONST || kind == PARTITION_KIND_CONST_IMPORT;
10221022
}
10231023

10241024
STATIC_INLINE int jl_bkind_is_real_constant(enum jl_partition_kind kind) JL_NOTSAFEPOINT {

src/module.c

Lines changed: 56 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ struct implicit_search_resolution jl_resolve_implicit_import(jl_binding_t *b, mo
301301
imp_resolution.binding_or_const = tempbpart->restriction;
302302
imp_resolution.debug_only_ultimate_binding = (jl_binding_t*)tempbpart->restriction;
303303
imp_resolution.ultimate_kind = PARTITION_KIND_IMPLICIT_GLOBAL;
304-
} else if (kind == PARTITION_KIND_GLOBAL || kind == PARTITION_KIND_DECLARED || kind == PARTITION_KIND_BACKDATED_CONST) {
304+
} else if (kind == PARTITION_KIND_GLOBAL || kind == PARTITION_KIND_DECLARED) {
305305
imp_resolution.binding_or_const = (jl_value_t *)tempb;
306306
imp_resolution.debug_only_ultimate_binding = tempb;
307307
imp_resolution.ultimate_kind = PARTITION_KIND_IMPLICIT_GLOBAL;
@@ -595,50 +595,6 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(
595595
} else {
596596
new_bpart = jl_replace_binding_locked(b, bpart, val, constant_kind, new_world);
597597
}
598-
int need_backdate = new_world && val;
599-
if (need_backdate) {
600-
// We will backdate as long as this partition was never explicitly
601-
// declared const, global, or imported.
602-
jl_binding_partition_t *prev_bpart = bpart;
603-
for (;;) {
604-
enum jl_partition_kind prev_kind = jl_binding_kind(prev_bpart);
605-
if (jl_bkind_is_some_constant(prev_kind) || prev_kind == PARTITION_KIND_GLOBAL ||
606-
jl_bkind_is_some_import(prev_kind)) {
607-
need_backdate = 0;
608-
break;
609-
}
610-
size_t prev_bpart_min_world = jl_atomic_load_relaxed(&prev_bpart->min_world);
611-
if (prev_bpart_min_world == 0)
612-
break;
613-
prev_bpart = jl_get_binding_partition(b, prev_bpart_min_world - 1);
614-
}
615-
}
616-
// If backdate is required, replace each existing partition by a new one.
617-
// We can't use one binding to cover the entire range, because we need to
618-
// keep the flags partitioned.
619-
if (need_backdate) {
620-
jl_binding_partition_t *prev_bpart = bpart;
621-
jl_binding_partition_t *backdate_bpart = new_binding_partition();
622-
new_prev_bpart = backdate_bpart;
623-
while (1) {
624-
backdate_bpart->kind = (size_t)PARTITION_KIND_BACKDATED_CONST | (prev_bpart->kind & 0xf0);
625-
backdate_bpart->restriction = val;
626-
jl_atomic_store_relaxed(&backdate_bpart->min_world,
627-
jl_atomic_load_relaxed(&prev_bpart->min_world));
628-
jl_gc_wb_fresh(backdate_bpart, val);
629-
jl_atomic_store_relaxed(&backdate_bpart->max_world,
630-
jl_atomic_load_relaxed(&prev_bpart->max_world));
631-
prev_bpart = jl_atomic_load_relaxed(&prev_bpart->next);
632-
if (!prev_bpart)
633-
break;
634-
jl_binding_partition_t *next_prev_bpart = new_binding_partition();
635-
jl_atomic_store_relaxed(&backdate_bpart->next, next_prev_bpart);
636-
jl_gc_wb(backdate_bpart, next_prev_bpart);
637-
backdate_bpart = next_prev_bpart;
638-
}
639-
jl_atomic_store_release(&new_bpart->next, new_prev_bpart);
640-
jl_gc_wb(new_bpart, new_prev_bpart);
641-
}
642598
}
643599
JL_GC_POP();
644600
return new_bpart;
@@ -866,29 +822,6 @@ JL_DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var
866822
return b ? b->globalref->mod : m;
867823
}
868824

869-
static NOINLINE void print_backdate_admonition(jl_binding_t *b) JL_NOTSAFEPOINT
870-
{
871-
jl_safe_printf(
872-
"WARNING: Detected access to binding `%s.%s` in a world prior to its definition world.\n"
873-
" Julia 1.12 has introduced more strict world age semantics for global bindings.\n"
874-
" !!! This code may malfunction under Revise.\n"
875-
" !!! This code will error in future versions of Julia.\n"
876-
"Hint: Add an appropriate `invokelatest` around the access to this binding.\n"
877-
"To make this warning an error, and hence obtain a stack trace, use `julia --depwarn=error`.\n",
878-
jl_symbol_name(b->globalref->mod->name), jl_symbol_name(b->globalref->name));
879-
}
880-
881-
static inline void check_backdated_binding(jl_binding_t *b, enum jl_partition_kind kind) JL_NOTSAFEPOINT
882-
{
883-
if (__unlikely(kind == PARTITION_KIND_BACKDATED_CONST)) {
884-
// We don't want functions that inference executes speculatively to print this warning, so turn those into
885-
// an error for inference purposes.
886-
if (jl_current_task->ptls->in_pure_callback || jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR)
887-
jl_undefined_var_error(b->globalref->name, (jl_value_t*)b->globalref->mod);
888-
if (!(jl_atomic_fetch_or_relaxed(&b->flags, BINDING_FLAG_DID_PRINT_BACKDATE_ADMONITION) & BINDING_FLAG_DID_PRINT_BACKDATE_ADMONITION))
889-
print_backdate_admonition(b);
890-
}
891-
}
892825

893826
JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b)
894827
{
@@ -900,10 +833,19 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_in_world(jl_binding_t *b, size_t w
900833
jl_binding_partition_t *bpart = jl_get_binding_partition(b, world);
901834
jl_walk_binding_inplace(&b, &bpart, world);
902835
enum jl_partition_kind kind = jl_binding_kind(bpart);
836+
if (kind == PARTITION_KIND_GUARD) {
837+
// Retry lookup in current world counter for guard partitions (unless in pure callback)
838+
if (!jl_current_task->ptls->in_pure_callback) {
839+
size_t current_world = jl_atomic_load_acquire(&jl_world_counter);
840+
if (current_world != world) {
841+
return jl_get_binding_value_in_world(b, current_world);
842+
}
843+
}
844+
return NULL;
845+
}
903846
if (jl_bkind_is_some_guard(kind))
904847
return NULL;
905848
if (jl_bkind_is_some_constant(kind)) {
906-
check_backdated_binding(b, kind);
907849
return bpart->restriction;
908850
}
909851
assert(!jl_bkind_is_some_import(kind));
@@ -923,10 +865,19 @@ static jl_value_t *jl_get_binding_value_depwarn(jl_binding_t *b, size_t world)
923865
jl_walk_binding_inplace(&b, &bpart, world);
924866
}
925867
enum jl_partition_kind kind = jl_binding_kind(bpart);
868+
if (kind == PARTITION_KIND_GUARD) {
869+
// Retry lookup in current world counter for guard partitions (unless in pure callback)
870+
if (!jl_current_task->ptls->in_pure_callback) {
871+
size_t current_world = jl_atomic_load_acquire(&jl_world_counter);
872+
if (current_world != world) {
873+
return jl_get_binding_value_depwarn(b, current_world);
874+
}
875+
}
876+
return NULL;
877+
}
926878
if (jl_bkind_is_some_guard(kind))
927879
return NULL;
928880
if (jl_bkind_is_some_constant(kind)) {
929-
check_backdated_binding(b, kind);
930881
return bpart->restriction;
931882
}
932883
assert(!jl_bkind_is_some_import(kind));
@@ -939,10 +890,19 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b)
939890
jl_binding_partition_t *bpart = jl_get_binding_partition(b, world);
940891
jl_walk_binding_inplace(&b, &bpart, world);
941892
enum jl_partition_kind kind = jl_binding_kind(bpart);
893+
if (kind == PARTITION_KIND_GUARD) {
894+
// Retry lookup in current world counter for guard partitions (unless in pure callback)
895+
if (!jl_current_task->ptls->in_pure_callback) {
896+
size_t current_world = jl_atomic_load_acquire(&jl_world_counter);
897+
if (current_world != world) {
898+
return jl_get_binding_value_in_world(b, current_world);
899+
}
900+
}
901+
return NULL;
902+
}
942903
if (jl_bkind_is_some_guard(kind))
943904
return NULL;
944905
if (jl_bkind_is_some_constant(kind)) {
945-
check_backdated_binding(b, kind);
946906
return bpart->restriction;
947907
}
948908
assert(!jl_bkind_is_some_import(kind));
@@ -1478,13 +1438,34 @@ JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import) // u
14781438
jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age);
14791439
}
14801440
enum jl_partition_kind kind = jl_binding_kind(bpart);
1441+
if (kind == PARTITION_KIND_GUARD) {
1442+
// Retry lookup in current world counter for guard partitions (unless in pure callback)
1443+
if (!jl_current_task->ptls->in_pure_callback) {
1444+
size_t current_world = jl_atomic_load_acquire(&jl_world_counter);
1445+
if (current_world != jl_current_task->world_age) {
1446+
jl_binding_partition_t *current_bpart = jl_get_binding_partition(b, current_world);
1447+
if (!current_bpart)
1448+
return 0;
1449+
if (!allow_import) {
1450+
if (!current_bpart || jl_bkind_is_some_import(jl_binding_kind(current_bpart)))
1451+
return 0;
1452+
} else {
1453+
jl_walk_binding_inplace(&b, &current_bpart, current_world);
1454+
}
1455+
enum jl_partition_kind current_kind = jl_binding_kind(current_bpart);
1456+
if (jl_bkind_is_some_guard(current_kind))
1457+
return 0;
1458+
if (jl_bkind_is_defined_constant(current_kind)) {
1459+
return 1;
1460+
}
1461+
return jl_atomic_load(&b->value) != NULL;
1462+
}
1463+
}
1464+
return 0;
1465+
}
14811466
if (jl_bkind_is_some_guard(kind))
14821467
return 0;
14831468
if (jl_bkind_is_defined_constant(kind)) {
1484-
if (__unlikely(kind == PARTITION_KIND_BACKDATED_CONST)) {
1485-
return !(jl_current_task->ptls->in_pure_callback || jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR);
1486-
}
1487-
// N.B.: No backdated admonition for isdefined
14881469
return 1;
14891470
}
14901471
return jl_atomic_load(&b->value) != NULL;

0 commit comments

Comments
 (0)