Skip to content

Commit 8f00a51

Browse files
authored
Always disallow assignments to constants (#57567)
We have historically allowed the following without warning or error: ``` const x = 1 x = 1 ``` As well as ``` const x = 1 x = 2 ``` with a UB warning. In 1.12, we made the second case error, as part of the general binding partition changes, but did not touch the first case, because it did not have the warning. I think this made a reasonable amount of sense, because we essentially treated constants as special kinds of mutable globals (to which assignment happened to be UB). However, in the 1.12+ design, constants and globals are quite sepearate beasts, and I think it makes sense to extend the error to the egal case also, even if it is technically more breaking. In fact, I already thought that's what I did when I implemented the new effect model for global assignment, causing #57566. I can't think of a legitimate reason to keep this special case. For those who want to do binding replacement, the `const` keyword is mandatory, so the assignment is now literally always a semantic no-op or an error. Fixes #57566.
1 parent edf1cd0 commit 8f00a51

File tree

2 files changed

+13
-13
lines changed

2 files changed

+13
-13
lines changed

src/module.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -547,14 +547,19 @@ JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module
547547
jl_binding_deprecation_warning(b);
548548
}
549549
enum jl_partition_kind kind = jl_binding_kind(bpart);
550-
if (kind != BINDING_KIND_GLOBAL && kind != BINDING_KIND_DECLARED && !jl_bkind_is_some_constant(kind)) {
550+
if (kind != BINDING_KIND_GLOBAL && kind != BINDING_KIND_DECLARED) {
551551
if (jl_bkind_is_some_guard(kind)) {
552552
jl_errorf("Global %s.%s does not exist and cannot be assigned.\n"
553553
"Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).\n"
554554
"Hint: Declare it using `global %s` inside `%s` before attempting assignment.",
555555
jl_symbol_name(m->name), jl_symbol_name(s),
556556
jl_symbol_name(s), jl_symbol_name(m->name));
557-
} else {
557+
}
558+
else if (jl_bkind_is_some_constant(kind)) {
559+
jl_errorf("invalid assignment to constant %s.%s. This redefinition may be permitted using the `const` keyword.",
560+
jl_symbol_name(m->name), jl_symbol_name(s));
561+
}
562+
else {
558563
jl_module_t *from = jl_binding_dbgmodule(b, m, s);
559564
if (from == m)
560565
jl_errorf("cannot assign a value to imported variable %s.%s",
@@ -1509,16 +1514,6 @@ jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl
15091514
JL_GC_PUSH1(&rhs); // callee-rooted
15101515
jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age);
15111516
enum jl_partition_kind kind = jl_binding_kind(bpart);
1512-
if (jl_bkind_is_some_constant(kind)) {
1513-
jl_value_t *old = bpart->restriction;
1514-
JL_GC_PROMISE_ROOTED(old);
1515-
if (jl_egal(rhs, old)) {
1516-
JL_GC_POP();
1517-
return NULL;
1518-
}
1519-
jl_errorf("invalid assignment to constant %s.%s. This redefinition may be permitted using the `const` keyword.",
1520-
jl_symbol_name(mod->name), jl_symbol_name(var));
1521-
}
15221517
assert(kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_GLOBAL);
15231518
jl_value_t *old_ty = kind == BINDING_KIND_DECLARED ? (jl_value_t*)jl_any_type : bpart->restriction;
15241519
JL_GC_PROMISE_ROOTED(old_ty);

test/syntax.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3973,8 +3973,13 @@ end
39733973

39743974
# Test trying to define a constant and then trying to assign to the same value
39753975
module AssignConstValueTest
3976+
using Test
39763977
const x = 1
3977-
x = 1
3978+
@test_throws ErrorException @eval x = 1
3979+
@test_throws ErrorException @eval begin
3980+
@Base.Experimental.force_compile
3981+
global x = 1
3982+
end
39783983
end
39793984
@test isconst(AssignConstValueTest, :x)
39803985

0 commit comments

Comments
 (0)