Skip to content

Commit b27a24a

Browse files
authored
bpart: Also partition the export flag (#57405)
Whether or not a binding is exported affects the binding resolution of any downstream modules that `using` the module that defines the binding. As such, it needs to fully participate in the invalidation mechanism, so that code which references bindings whose resolution may have changed get properly invalidated. To do this, move the `exportp` flag from Binding into a separate bitflag set that gets or'd into the BindingPartition `->kind` field. Note that we do not move `publicp` in the same way since it does not affect binding resolution. There is currently no mechanism to un-export a binding, although the system is set up to support this in the future (and Revise may want it). That said, at such a time, we may need to revisit the above decision on `publicp`. Lastly, I will note that this adds a fair number of additional invalidations. Most of these are unnecessary, as changing an export only affects the *downstream* users not the binding itself. I am planning to tackle this as part of a larger future PR that avoids invalidation when this is not required. Fixes #57377
1 parent a28e5ce commit b27a24a

File tree

12 files changed

+181
-110
lines changed

12 files changed

+181
-110
lines changed

base/invalidation.jl

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -113,39 +113,36 @@ function invalidate_method_for_globalref!(gr::GlobalRef, method::Method, invalid
113113
end
114114
end
115115

116-
const BINDING_FLAG_EXPORTP = 0x2
117-
118116
function invalidate_code_for_globalref!(b::Core.Binding, invalidated_bpart::Core.BindingPartition, new_bpart::Union{Core.BindingPartition, Nothing}, new_max_world::UInt)
119117
gr = b.globalref
120-
if is_some_guard(binding_kind(invalidated_bpart))
118+
if !is_some_guard(binding_kind(invalidated_bpart))
121119
# TODO: We may want to invalidate for these anyway, since they have performance implications
122-
return
123-
end
124-
foreach_module_mtable(gr.mod, new_max_world) do mt::Core.MethodTable
125-
for method in MethodList(mt)
126-
invalidate_method_for_globalref!(gr, method, invalidated_bpart, new_max_world)
120+
foreach_module_mtable(gr.mod, new_max_world) do mt::Core.MethodTable
121+
for method in MethodList(mt)
122+
invalidate_method_for_globalref!(gr, method, invalidated_bpart, new_max_world)
123+
end
124+
return true
127125
end
128-
return true
129-
end
130-
if isdefined(b, :backedges)
131-
for edge in b.backedges
132-
if isa(edge, CodeInstance)
133-
ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), edge, new_max_world)
134-
elseif isa(edge, Core.Binding)
135-
isdefined(edge, :partitions) || continue
136-
latest_bpart = edge.partitions
137-
latest_bpart.max_world == typemax(UInt) || continue
138-
is_some_imported(binding_kind(latest_bpart)) || continue
139-
partition_restriction(latest_bpart) === b || continue
140-
invalidate_code_for_globalref!(edge, latest_bpart, nothing, new_max_world)
141-
else
142-
invalidate_method_for_globalref!(gr, edge::Method, invalidated_bpart, new_max_world)
126+
if isdefined(b, :backedges)
127+
for edge in b.backedges
128+
if isa(edge, CodeInstance)
129+
ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), edge, new_max_world)
130+
elseif isa(edge, Core.Binding)
131+
isdefined(edge, :partitions) || continue
132+
latest_bpart = edge.partitions
133+
latest_bpart.max_world == typemax(UInt) || continue
134+
is_some_imported(binding_kind(latest_bpart)) || continue
135+
partition_restriction(latest_bpart) === b || continue
136+
invalidate_code_for_globalref!(edge, latest_bpart, nothing, new_max_world)
137+
else
138+
invalidate_method_for_globalref!(gr, edge::Method, invalidated_bpart, new_max_world)
139+
end
143140
end
144141
end
145142
end
146-
if (b.flags & BINDING_FLAG_EXPORTP) != 0
143+
if (invalidated_bpart.kind & BINDING_FLAG_EXPORTED != 0) || (new_bpart !== nothing && (new_bpart.kind & BINDING_FLAG_EXPORTED != 0))
147144
# This binding was exported - we need to check all modules that `using` us to see if they
148-
# have an implicit binding to us.
145+
# have a binding that is affected by this change.
149146
usings_backedges = ccall(:jl_get_module_usings_backedges, Any, (Any,), gr.mod)
150147
if usings_backedges !== nothing
151148
for user in usings_backedges::Vector{Any}
@@ -154,8 +151,8 @@ function invalidate_code_for_globalref!(b::Core.Binding, invalidated_bpart::Core
154151
isdefined(user_binding, :partitions) || continue
155152
latest_bpart = user_binding.partitions
156153
latest_bpart.max_world == typemax(UInt) || continue
157-
is_some_imported(binding_kind(latest_bpart)) || continue
158-
partition_restriction(latest_bpart) === b || continue
154+
binding_kind(latest_bpart) in (BINDING_KIND_IMPLICIT, BINDING_KIND_FAILED, BINDING_KIND_GUARD) || continue
155+
@atomic :release latest_bpart.max_world = new_max_world
159156
invalidate_code_for_globalref!(convert(Core.Binding, user_binding), latest_bpart, nothing, new_max_world)
160157
end
161158
end

base/runtime_internals.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ const BINDING_KIND_GUARD = 0x8
209209
const BINDING_KIND_UNDEF_CONST = 0x9
210210
const BINDING_KIND_BACKDATED_CONST = 0xa
211211

212+
const BINDING_FLAG_EXPORTED = 0x10
213+
212214
is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST)
213215
is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST)
214216
is_some_imported(kind::UInt8) = (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED)

base/show.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3374,6 +3374,9 @@ function print_partition(io::IO, partition::Core.BindingPartition)
33743374
else
33753375
print(io, max_world)
33763376
end
3377+
if (partition.kind & BINDING_FLAG_EXPORTED) != 0
3378+
print(io, " [exported]")
3379+
end
33773380
print(io, " - ")
33783381
kind = binding_kind(partition)
33793382
if kind == BINDING_KIND_BACKDATED_CONST

src/ast.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ static value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint
178178
jl_sym_t *var = scmsym_to_julia(fl_ctx, args[0]);
179179
jl_binding_t *b = jl_get_module_binding(ctx->module, var, 0);
180180
jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age);
181-
return (bpart != NULL && bpart->kind == BINDING_KIND_GLOBAL) ? fl_ctx->T : fl_ctx->F;
181+
return (bpart != NULL && jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) ? fl_ctx->T : fl_ctx->F;
182182
}
183183

184184
// Used to generate a unique suffix for a given symbol (e.g. variable or type name)

src/codegen.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3114,7 +3114,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
31143114
jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0);
31153115
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
31163116
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
3117-
if (bpart && jl_bkind_is_some_constant(bpart->kind))
3117+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
31183118
return bpart->restriction;
31193119
return NULL;
31203120
}
@@ -3140,7 +3140,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
31403140
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
31413141
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
31423142
jl_value_t *v = NULL;
3143-
if (bpart && jl_bkind_is_some_constant(bpart->kind))
3143+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
31443144
v = bpart->restriction;
31453145
if (v) {
31463146
if (bnd->deprecated)
@@ -3167,7 +3167,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
31673167
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
31683168
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
31693169
jl_value_t *v = NULL;
3170-
if (bpart && jl_bkind_is_some_constant(bpart->kind))
3170+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
31713171
v = bpart->restriction;
31723172
if (v) {
31733173
if (bnd->deprecated)
@@ -3418,14 +3418,14 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
34183418
return emit_globalref_runtime(ctx, bnd, mod, name);
34193419
}
34203420
// bpart was updated in place - this will change with full partition
3421-
if (jl_bkind_is_some_guard(bpart->kind)) {
3421+
if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) {
34223422
// Redo the lookup at runtime
34233423
return emit_globalref_runtime(ctx, bnd, mod, name);
34243424
} else {
34253425
while (true) {
34263426
if (!bpart)
34273427
break;
3428-
if (!jl_bkind_is_some_import(bpart->kind))
3428+
if (!jl_bkind_is_some_import(jl_binding_kind(bpart)))
34293429
break;
34303430
if (bnd->deprecated) {
34313431
cg_bdw(ctx, name, bnd);
@@ -3436,7 +3436,7 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
34363436
break;
34373437
}
34383438
if (bpart) {
3439-
enum jl_partition_kind kind = bpart->kind;
3439+
enum jl_partition_kind kind = jl_binding_kind(bpart);
34403440
if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) {
34413441
jl_value_t *constval = bpart->restriction;
34423442
if (!constval) {
@@ -3447,7 +3447,7 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
34473447
}
34483448
}
34493449
}
3450-
if (!bpart || bpart->kind != BINDING_KIND_GLOBAL) {
3450+
if (!bpart || jl_binding_kind(bpart) != BINDING_KIND_GLOBAL) {
34513451
return emit_globalref_runtime(ctx, bnd, mod, name);
34523452
}
34533453
Value *bp = julia_binding_gv(ctx, bnd);
@@ -3470,7 +3470,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
34703470
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
34713471
Value *bp = julia_binding_gv(ctx, bnd);
34723472
if (bpart) {
3473-
if (bpart->kind == BINDING_KIND_GLOBAL) {
3473+
if (jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) {
34743474
jl_value_t *ty = bpart->restriction;
34753475
if (ty != nullptr) {
34763476
const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!";
@@ -4156,7 +4156,7 @@ static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_
41564156
Value *isnull = NULL;
41574157
jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0);
41584158
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
4159-
enum jl_partition_kind kind = bpart ? bpart->kind : BINDING_KIND_GUARD;
4159+
enum jl_partition_kind kind = bpart ? jl_binding_kind(bpart) : BINDING_KIND_GUARD;
41604160
if (kind == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(kind)) {
41614161
if (jl_get_binding_value_if_const(bnd))
41624162
return mark_julia_const(ctx, jl_true);

src/julia.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,9 @@ enum jl_partition_kind {
706706
BINDING_KIND_IMPLICIT_RECOMPUTE = 0xb
707707
};
708708

709+
// These are flags that get anded into the above
710+
static const uint8_t BINDING_FLAG_EXPORTED = 0x10;
711+
709712
typedef struct __attribute__((aligned(8))) _jl_binding_partition_t {
710713
JL_DATA_TYPE
711714
/* union {
@@ -716,30 +719,30 @@ typedef struct __attribute__((aligned(8))) _jl_binding_partition_t {
716719
* // For ->kind in (BINDING_KIND_IMPLICIT, BINDING_KIND_EXPLICIT, BINDING_KIND_IMPORT)
717720
* jl_binding_t *imported;
718721
* } restriction;
719-
*
720-
* Currently: Low 3 bits hold ->kind on _P64 to avoid needing >8 byte atomics
721-
*
722-
* This field is updated atomically with both kind and restriction
723722
*/
724723
jl_value_t *restriction;
725724
size_t min_world;
726725
_Atomic(size_t) max_world;
727726
_Atomic(struct _jl_binding_partition_t *) next;
728-
enum jl_partition_kind kind;
727+
size_t kind;
729728
} jl_binding_partition_t;
730729

730+
STATIC_INLINE enum jl_partition_kind jl_binding_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT
731+
{
732+
return (enum jl_partition_kind)(bpart->kind & 0xf);
733+
}
734+
731735
typedef struct _jl_binding_t {
732736
JL_DATA_TYPE
733737
jl_globalref_t *globalref; // cached GlobalRef for this binding
734738
_Atomic(jl_value_t*) value;
735739
_Atomic(jl_binding_partition_t*) partitions;
736740
jl_array_t *backedges;
737741
uint8_t did_print_backdate_admonition:1;
738-
uint8_t exportp:1; // `public foo` sets `publicp`, `export foo` sets both `publicp` and `exportp`
739-
uint8_t publicp:1; // exportp without publicp is not allowed.
740-
uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package
741742
uint8_t did_print_implicit_import_admonition:1;
742-
uint8_t padding:2;
743+
uint8_t publicp:1; // `export` is tracked in partitions, but sets this as well
744+
uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package
745+
uint8_t padding:3;
743746
} jl_binding_t;
744747

745748
typedef struct {

src/julia_internal.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,8 @@ JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_RO
915915
JL_DLLEXPORT void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *sym, jl_binding_t *b);
916916
JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b JL_PROPAGATES_ROOT,
917917
jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) JL_GLOBALLY_ROOTED;
918+
JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked2(jl_binding_t *b JL_PROPAGATES_ROOT,
919+
jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, size_t kind, size_t new_world) JL_GLOBALLY_ROOTED;
918920
extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED;
919921
extern htable_t jl_current_modules JL_GLOBALLY_ROOTED;
920922
extern JL_DLLEXPORT jl_module_t *jl_precompile_toplevel_module JL_GLOBALLY_ROOTED;
@@ -952,7 +954,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL
952954
JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_GLOBALLY_ROOTED;
953955

954956
EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT {
955-
return (uint8_t)bpart->kind;
957+
return (uint8_t)(bpart->kind & 0xf);
956958
}
957959

958960
STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT;
@@ -962,7 +964,7 @@ STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_pa
962964
STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT
963965
{
964966
while (1) {
965-
if (!jl_bkind_is_some_import((*bpart)->kind))
967+
if (!jl_bkind_is_some_import(jl_binding_kind(*bpart)))
966968
return;
967969
*bnd = (jl_binding_t*)(*bpart)->restriction;
968970
*bpart = jl_get_binding_partition(*bnd, world);
@@ -974,7 +976,7 @@ STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_pa
974976
while (1) {
975977
if (!(*bpart))
976978
return;
977-
if (!jl_bkind_is_some_import((*bpart)->kind))
979+
if (!jl_bkind_is_some_import(jl_binding_kind(*bpart)))
978980
return;
979981
*bnd = (jl_binding_t*)(*bpart)->restriction;
980982
*bpart = jl_get_binding_partition_all(*bnd, min_world, max_world);

src/method.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,10 +1061,10 @@ JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_module_t *mod, jl_sym_t *name)
10611061
jl_binding_t *b = jl_get_binding_for_method_def(mod, name, new_world);
10621062
jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world);
10631063
jl_value_t *gf = NULL;
1064-
enum jl_partition_kind kind = bpart->kind;
1064+
enum jl_partition_kind kind = jl_binding_kind(bpart);
10651065
if (!jl_bkind_is_some_guard(kind) && kind != BINDING_KIND_DECLARED && kind != BINDING_KIND_IMPLICIT) {
10661066
jl_walk_binding_inplace(&b, &bpart, new_world);
1067-
if (jl_bkind_is_some_constant(bpart->kind)) {
1067+
if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) {
10681068
gf = bpart->restriction;
10691069
JL_GC_PROMISE_ROOTED(gf);
10701070
jl_check_gf(gf, b->globalref->name);

0 commit comments

Comments
 (0)