Skip to content

Commit 56aed62

Browse files
authored
Prohibit binding replacement in closed modules during precompile (#57425)
This applies the existing prohibition against introducing new bindings in a closed module to binding replacement as well (for the same reason - the change won't be persisted after reload). It is pretty hard to even reach this point, since `eval` into closed modules is already prohibited and there's no surface syntax for cross-module declaration, but it is technically reachable from lowered IR. Further, in the future we may make all of these builtins, which would make it easier. Thus, be consistent now and fully disallow binding replacement in closed modules during precompile.
1 parent 18262ff commit 56aed62

File tree

4 files changed

+43
-5
lines changed

4 files changed

+43
-5
lines changed

src/julia_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,9 @@ _Atomic(jl_value_t*) *jl_table_peek_bp(jl_genericmemory_t *a, jl_value_t *key) J
11851185

11861186
JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t*);
11871187

1188+
JL_DLLEXPORT jl_module_t *jl_new_module__(jl_sym_t *name, jl_module_t *parent);
11881189
JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_using_core, uint8_t self_name);
1190+
JL_DLLEXPORT void jl_add_default_names(jl_module_t *m, uint8_t default_using_core, uint8_t self_name);
11891191
JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *module);
11901192
JL_DLLEXPORT jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES_ROOT, size_t world, int mt_cache);
11911193
jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_svec_t *sp) JL_PROPAGATES_ROOT;

src/module.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b, size_t min
221221
return bpart;
222222
}
223223

224-
JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_using_core, uint8_t self_name)
224+
JL_DLLEXPORT jl_module_t *jl_new_module__(jl_sym_t *name, jl_module_t *parent)
225225
{
226226
jl_task_t *ct = jl_current_task;
227227
const jl_uuid_t uuid_zero = {0, 0};
@@ -253,7 +253,11 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui
253253
jl_atomic_store_relaxed(&m->bindings, jl_emptysvec);
254254
jl_atomic_store_relaxed(&m->bindingkeyset, (jl_genericmemory_t*)jl_an_empty_memory_any);
255255
arraylist_new(&m->usings, 0);
256-
JL_GC_PUSH1(&m);
256+
return m;
257+
}
258+
259+
JL_DLLEXPORT void jl_add_default_names(jl_module_t *m, uint8_t default_using_core, uint8_t self_name)
260+
{
257261
if (jl_core_module) {
258262
// Bootstrap: Before jl_core_module is defined, we don't have enough infrastructure
259263
// for bindings, so Core itself gets special handling in jltypes.c
@@ -262,14 +266,22 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui
262266
}
263267
if (self_name) {
264268
// export own name, so "using Foo" makes "Foo" itself visible
265-
jl_set_const(m, name, (jl_value_t*)m);
266-
jl_module_public(m, name, 1);
269+
jl_set_const(m, m->name, (jl_value_t*)m);
270+
jl_module_public(m, m->name, 1);
267271
}
268272
}
273+
}
274+
275+
JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, uint8_t default_using_core, uint8_t self_name)
276+
{
277+
jl_module_t *m = jl_new_module__(name, parent);
278+
JL_GC_PUSH1(&m);
279+
jl_add_default_names(m, default_using_core, self_name);
269280
JL_GC_POP();
270281
return m;
271282
}
272283

284+
273285
// Precondition: world_counter_lock is held
274286
JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(
275287
jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val,
@@ -1273,6 +1285,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b,
12731285
JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked2(jl_binding_t *b,
12741286
jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, size_t kind, size_t new_world)
12751287
{
1288+
check_safe_newbinding(b->globalref->mod, b->globalref->name);
12761289
assert(jl_atomic_load_relaxed(&b->partitions) == old_bpart);
12771290
jl_atomic_store_release(&old_bpart->max_world, new_world-1);
12781291
jl_binding_partition_t *new_bpart = new_binding_partition();

src/toplevel.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,15 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex
138138
// If we have `Base`, don't also try to import `Core` - the `Base` exports are a superset.
139139
// While we allow multiple imports of the same binding from different modules, various error printing
140140
// performs reflection on which module a binding came from and we'd prefer users see "Base" here.
141-
jl_module_t *newm = jl_new_module_(name, is_parent__toplevel__ ? NULL : parent_module, std_imports && jl_base_module != NULL ? 0 : 1, 1);
141+
jl_module_t *newm = jl_new_module__(name, is_parent__toplevel__ ? NULL : parent_module);
142142
jl_value_t *form = (jl_value_t*)newm;
143143
JL_GC_PUSH1(&form);
144144
JL_LOCK(&jl_modules_mutex);
145145
ptrhash_put(&jl_current_modules, (void*)newm, (void*)((uintptr_t)HT_NOTFOUND + 1));
146146
JL_UNLOCK(&jl_modules_mutex);
147147

148+
jl_add_default_names(newm, std_imports && jl_base_module != NULL ? 0 : 1, 1);
149+
148150
jl_module_t *old_toplevel_module = jl_precompile_toplevel_module;
149151

150152
// copy parent environment info into submodule

test/precompile.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,4 +2311,25 @@ precompile_test_harness("llvmcall validation") do load_path
23112311
end
23122312
end
23132313

2314+
precompile_test_harness("BindingReplaceDisallow") do load_path
2315+
write(joinpath(load_path, "BindingReplaceDisallow.jl"),
2316+
"""
2317+
module BindingReplaceDisallow
2318+
const sinreplace = try
2319+
eval(Expr(:block,
2320+
Expr(:const, GlobalRef(Base, :sin), 1),
2321+
nothing))
2322+
catch ex
2323+
ex isa ErrorException || rethrow()
2324+
ex
2325+
end
2326+
end
2327+
""")
2328+
ji, ofile = Base.compilecache(Base.PkgId("BindingReplaceDisallow"))
2329+
@eval using BindingReplaceDisallow
2330+
invokelatest() do
2331+
@test BindingReplaceDisallow.sinreplace.msg == "Creating a new global in closed module `Base` (`sin`) breaks incremental compilation because the side effects will not be permanent."
2332+
end
2333+
end
2334+
23142335
finish_precompile_test!()

0 commit comments

Comments
 (0)