Skip to content

Commit 67eb453

Browse files
KenoKristofferC
authored andcommitted
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 (cherry picked from commit b27a24a)
1 parent 9002fe5 commit 67eb453

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
@@ -3368,6 +3368,9 @@ function print_partition(io::IO, partition::Core.BindingPartition)
33683368
else
33693369
print(io, max_world)
33703370
end
3371+
if (partition.kind & BINDING_FLAG_EXPORTED) != 0
3372+
print(io, " [exported]")
3373+
end
33713374
print(io, " - ")
33723375
kind = binding_kind(partition)
33733376
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
@@ -3139,7 +3139,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
31393139
jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0);
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);
3142-
if (bpart && jl_bkind_is_some_constant(bpart->kind))
3142+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
31433143
return bpart->restriction;
31443144
return NULL;
31453145
}
@@ -3165,7 +3165,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
31653165
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
31663166
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
31673167
jl_value_t *v = NULL;
3168-
if (bpart && jl_bkind_is_some_constant(bpart->kind))
3168+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
31693169
v = bpart->restriction;
31703170
if (v) {
31713171
if (bnd->deprecated)
@@ -3192,7 +3192,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
31923192
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
31933193
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
31943194
jl_value_t *v = NULL;
3195-
if (bpart && jl_bkind_is_some_constant(bpart->kind))
3195+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
31963196
v = bpart->restriction;
31973197
if (v) {
31983198
if (bnd->deprecated)
@@ -3443,14 +3443,14 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
34433443
return emit_globalref_runtime(ctx, bnd, mod, name);
34443444
}
34453445
// bpart was updated in place - this will change with full partition
3446-
if (jl_bkind_is_some_guard(bpart->kind)) {
3446+
if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) {
34473447
// Redo the lookup at runtime
34483448
return emit_globalref_runtime(ctx, bnd, mod, name);
34493449
} else {
34503450
while (true) {
34513451
if (!bpart)
34523452
break;
3453-
if (!jl_bkind_is_some_import(bpart->kind))
3453+
if (!jl_bkind_is_some_import(jl_binding_kind(bpart)))
34543454
break;
34553455
if (bnd->deprecated) {
34563456
cg_bdw(ctx, name, bnd);
@@ -3461,7 +3461,7 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
34613461
break;
34623462
}
34633463
if (bpart) {
3464-
enum jl_partition_kind kind = bpart->kind;
3464+
enum jl_partition_kind kind = jl_binding_kind(bpart);
34653465
if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) {
34663466
jl_value_t *constval = bpart->restriction;
34673467
if (!constval) {
@@ -3472,7 +3472,7 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
34723472
}
34733473
}
34743474
}
3475-
if (!bpart || bpart->kind != BINDING_KIND_GLOBAL) {
3475+
if (!bpart || jl_binding_kind(bpart) != BINDING_KIND_GLOBAL) {
34763476
return emit_globalref_runtime(ctx, bnd, mod, name);
34773477
}
34783478
Value *bp = julia_binding_gv(ctx, bnd);
@@ -3495,7 +3495,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
34953495
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
34963496
Value *bp = julia_binding_gv(ctx, bnd);
34973497
if (bpart) {
3498-
if (bpart->kind == BINDING_KIND_GLOBAL) {
3498+
if (jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) {
34993499
jl_value_t *ty = bpart->restriction;
35003500
if (ty != nullptr) {
35013501
const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!";
@@ -4181,7 +4181,7 @@ static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_
41814181
Value *isnull = NULL;
41824182
jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0);
41834183
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
4184-
enum jl_partition_kind kind = bpart ? bpart->kind : BINDING_KIND_GUARD;
4184+
enum jl_partition_kind kind = bpart ? jl_binding_kind(bpart) : BINDING_KIND_GUARD;
41854185
if (kind == BINDING_KIND_GLOBAL || jl_bkind_is_some_constant(kind)) {
41864186
if (jl_get_binding_value_if_const(bnd))
41874187
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)