Skip to content

Commit 804c89f

Browse files
committed
Lower const x = ... to new builtin Core.setconst!
Adds a new builtin, Core.setconst!, with the following signature: setconst!(module::Module, name::Symbol, [x]) The lowered Expr(:const, :name, value) form has been removed, saving some duplicated effort in the interpreter and code generation. Expr(:const, ...) is now always lowered to the builtin. The single-argument form of const, having no surface syntax, was previously only available through eval(Expr(:const, :foo)). It can now also be used by omitting the value argument to setconst!.
1 parent fb31b3c commit 804c89f

File tree

13 files changed

+101
-100
lines changed

13 files changed

+101
-100
lines changed

Compiler/src/ssair/ir.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ function is_relevant_expr(e::Expr)
581581
:foreigncall, :isdefined, :copyast,
582582
:throw_undef_if_not,
583583
:cfunction, :method, :pop_exception,
584-
:leave, :const, :globaldecl,
584+
:leave, :globaldecl,
585585
:new_opaque_closure)
586586
end
587587

Compiler/src/validation.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}(
99
:(&) => 1:1,
1010
:(=) => 2:2,
1111
:method => 1:4,
12-
:const => 1:2,
1312
:new => 1:typemax(Int),
1413
:splatnew => 2:2,
1514
:the_exception => 0:0,
@@ -149,7 +148,7 @@ function validate_code!(errors::Vector{InvalidCodeError}, c::CodeInfo, is_top_le
149148
elseif head === :call || head === :invoke || x.head === :invoke_modify ||
150149
head === :gc_preserve_end || head === :meta ||
151150
head === :inbounds || head === :foreigncall || head === :cfunction ||
152-
head === :const || head === :leave || head === :pop_exception ||
151+
head === :leave || head === :pop_exception ||
153152
head === :method || head === :global || head === :static_parameter ||
154153
head === :new || head === :splatnew || head === :thunk || head === :loopinfo ||
155154
head === :throw_undef_if_not || head === :code_coverage_effect || head === :inline || head === :noinline

base/docs/basedocs.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2753,6 +2753,38 @@ See also [`setpropertyonce!`](@ref Base.setpropertyonce!) and [`setglobal!`](@re
27532753
"""
27542754
setglobalonce!
27552755

2756+
"""
2757+
setconst!(module::Module, name::Symbol, [x])
2758+
2759+
Create or replace the constant `name` in `module` with the new value `x`. When
2760+
replacing, `x` does not need to have the same type as the original constant.
2761+
2762+
When `x` is not given, `name` becomes an undefined constant; it cannot be read
2763+
or written to, but can be redefined.
2764+
2765+
Note that this function does not update the world age of the current task:
2766+
```
2767+
julia> begin
2768+
const x = 1
2769+
println(x)
2770+
const x = 2
2771+
println(x)
2772+
Core.setconst!(Main, :x, 3)
2773+
println(x)
2774+
end
2775+
1
2776+
2
2777+
2
2778+
```
2779+
2780+
!!! compat "Julia 1.12"
2781+
This function requires Julia 1.12 or later. Redefining constants on earlier
2782+
versions of Julia is unpredictable.
2783+
2784+
See also [`const`](@ref).
2785+
"""
2786+
Core.setconst!
2787+
27562788
"""
27572789
typeof(x)
27582790

doc/src/base/base.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ Core.modifyglobal!
492492
Core.swapglobal!
493493
Core.setglobalonce!
494494
Core.replaceglobal!
495+
Core.setconst!
495496
```
496497

497498
## Documentation

src/builtin_proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ DECLARE_BUILTIN(modifyglobal);
6161
DECLARE_BUILTIN(nfields);
6262
DECLARE_BUILTIN(replacefield);
6363
DECLARE_BUILTIN(replaceglobal);
64+
DECLARE_BUILTIN(setconst);
6465
DECLARE_BUILTIN(setfield);
6566
DECLARE_BUILTIN(setfieldonce);
6667
DECLARE_BUILTIN(setglobal);

src/builtins.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,17 @@ JL_CALLABLE(jl_f_setglobalonce)
14831483
return old == NULL ? jl_true : jl_false;
14841484
}
14851485

1486-
1486+
JL_CALLABLE(jl_f_setconst)
1487+
{
1488+
JL_NARGS(setconst!, 2, 3);
1489+
JL_TYPECHK(setconst!, module, args[0]);
1490+
if (nargs == 3)
1491+
JL_TYPECHK(setconst!, symbol, args[1]);
1492+
jl_binding_t *b = jl_get_module_binding((jl_module_t *)args[0], (jl_sym_t *)args[1], 1);
1493+
jl_value_t *val = nargs == 3 ? args[2] : NULL;
1494+
jl_declare_constant_val(b, (jl_module_t *)args[0], (jl_sym_t *)args[1], val);
1495+
return args[2];
1496+
}
14871497

14881498
// apply_type -----------------------------------------------------------------
14891499

@@ -2486,6 +2496,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
24862496
jl_builtin_replaceglobal = add_builtin_func("replaceglobal!", jl_f_replaceglobal);
24872497
jl_builtin_modifyglobal = add_builtin_func("modifyglobal!", jl_f_modifyglobal);
24882498
jl_builtin_setglobalonce = add_builtin_func("setglobalonce!", jl_f_setglobalonce);
2499+
add_builtin_func("setconst!", jl_f_setconst);
24892500

24902501
// memory primitives
24912502
jl_builtin_memorynew = add_builtin_func("memorynew", jl_f_memorynew);

src/codegen.cpp

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -890,15 +890,6 @@ static const auto jlcheckassignonce_func = new JuliaFunction<>{
890890
{T_pjlvalue, T_pjlvalue, T_pjlvalue, PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); },
891891
nullptr,
892892
};
893-
static const auto jldeclareconstval_func = new JuliaFunction<>{
894-
XSTR(jl_declare_constant_val),
895-
[](LLVMContext &C) {
896-
auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C);
897-
auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C);
898-
return FunctionType::get(getVoidTy(C),
899-
{T_pjlvalue, T_pjlvalue, T_pjlvalue, T_prjlvalue}, false); },
900-
nullptr,
901-
};
902893
static const auto jldeclareglobal_func = new JuliaFunction<>{
903894
XSTR(jl_declare_global),
904895
[](LLVMContext &C) {
@@ -6450,26 +6441,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
64506441
jl_method_type);
64516442
return meth;
64526443
}
6453-
else if (head == jl_const_sym) {
6454-
assert(nargs <= 2);
6455-
jl_sym_t *sym = (jl_sym_t*)args[0];
6456-
jl_module_t *mod = ctx.module;
6457-
if (jl_is_globalref(sym)) {
6458-
mod = jl_globalref_mod(sym);
6459-
sym = jl_globalref_name(sym);
6460-
}
6461-
if (jl_is_symbol(sym)) {
6462-
jl_binding_t *bnd = jl_get_module_binding(mod, sym, 1);
6463-
if (nargs == 2) {
6464-
jl_cgval_t rhs = emit_expr(ctx, args[1]);
6465-
ctx.builder.CreateCall(prepare_call(jldeclareconstval_func),
6466-
{ julia_binding_gv(ctx, bnd), literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, rhs) });
6467-
} else {
6468-
ctx.builder.CreateCall(prepare_call(jldeclareconstval_func),
6469-
{ julia_binding_gv(ctx, bnd), literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), ConstantPointerNull::get(cast<PointerType>(ctx.types().T_prjlvalue)) });
6470-
}
6471-
}
6472-
}
64736444
else if (head == jl_globaldecl_sym) {
64746445
assert(nargs <= 2 && nargs >= 1);
64756446
jl_sym_t *sym = (jl_sym_t*)args[0];

src/interpreter.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -642,12 +642,6 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
642642
jl_declare_global(s->module, jl_exprarg(stmt, 0), val, 1);
643643
s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing;
644644
}
645-
else if (head == jl_const_sym) {
646-
jl_value_t *val = jl_expr_nargs(stmt) == 1 ? NULL : eval_value(jl_exprarg(stmt, 1), s);
647-
s->locals[jl_source_nslots(s->src) + s->ip] = val; // temporarily root
648-
jl_eval_const_decl(s->module, jl_exprarg(stmt, 0), val);
649-
s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing;
650-
}
651645
else if (head == jl_latestworld_sym) {
652646
ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
653647
}

src/jlfrontend.scm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
(and (pair? e)
141141
(or (memq (car e) '(toplevel line module export public
142142
error incomplete))
143-
(and (memq (car e) '(global const)) (every symbol? (cdr e))))))
143+
(and (memq (car e) '(global)) (every symbol? (cdr e))))))
144144

145145
(define *in-expand* #f)
146146

src/julia-syntax.scm

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,16 +1460,22 @@
14601460
(if (length= e 3)
14611461
`(const ,(cadr e) ,(expand-forms (caddr e)))
14621462
(let ((arg (cadr e)))
1463-
(case (car arg)
1464-
((global) (let ((asgn (cadr arg)))
1465-
(check-assignment asgn)
1466-
`(block
1467-
,.(map (lambda (v) `(global ,v))
1468-
(lhs-bound-names (cadr asgn)))
1469-
,(expand-assignment asgn #t))))
1470-
((=) (check-assignment arg)
1471-
(expand-assignment arg #t))
1472-
(else (error "expected assignment after \"const\""))))))
1463+
(cond
1464+
((symbol? arg)
1465+
;; Undefined constant: Expr(:const, :a) (not available in surface syntax)
1466+
`(block ,e (latestworld)))
1467+
((eq? (car arg) 'global)
1468+
(let ((asgn (cadr arg)))
1469+
(check-assignment asgn)
1470+
`(block
1471+
,.(map (lambda (v) `(global ,v))
1472+
(lhs-bound-names (cadr asgn)))
1473+
,(expand-assignment asgn #t))))
1474+
((eq? (car arg) '=)
1475+
(check-assignment arg)
1476+
(expand-assignment arg #t))
1477+
(else
1478+
(error "expected assignment after \"const\""))))))
14731479

14741480
(define (expand-atomic-decl e)
14751481
(error "unimplemented or unsupported atomic declaration"))
@@ -3043,7 +3049,8 @@
30433049
(set! vars (cons (cadr e) vars)))
30443050
((= const)
30453051
(let ((v (decl-var (cadr e))))
3046-
(find-assigned-vars- (caddr e))
3052+
(unless (and (eq? (car e) 'const) (null? (cddr e)))
3053+
(find-assigned-vars- (caddr e)))
30473054
(if (or (ssavalue? v) (globalref? v) (underscore-symbol? v))
30483055
'()
30493056
(set! vars (cons v vars)))))
@@ -3460,7 +3467,8 @@
34603467
(vinfo:set-sa! vi #f)
34613468
(vinfo:set-sa! vi #t))
34623469
(vinfo:set-asgn! vi #t))))
3463-
(analyze-vars (caddr e) env captvars sp tab))
3470+
(unless (null? (cddr e))
3471+
(analyze-vars (caddr e) env captvars sp tab)))
34643472
((call)
34653473
(let ((vi (get tab (cadr e) #f)))
34663474
(if vi
@@ -4070,8 +4078,6 @@ f(x) = yt(x)
40704078
'(null)
40714079
`(newvar ,(cadr e))))))
40724080
((const)
4073-
;; Check we've expanded surface `const` (1 argument form)
4074-
(assert (and (length= e 3)))
40754081
(when (globalref? (cadr e))
40764082
(put! globals (cadr e) #f))
40774083
e)
@@ -4642,10 +4648,15 @@ f(x) = yt(x)
46424648
(list cnd))))))
46434649
tests))
46444650
(define (emit-assignment-or-setglobal lhs rhs (op '=))
4645-
;; (const (globalref _ _) _) does not use setglobal!
4646-
(if (and (globalref? lhs) (eq? op '=))
4647-
(emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))
4648-
(emit `(,op ,lhs ,rhs))))
4651+
;; (= (globalref _ _) _) => setglobal!
4652+
;; (const (globalref _ _) _) => setconst!
4653+
(cond ((and (globalref? lhs) (eq? op '=))
4654+
(emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs)))
4655+
((and (globalref? lhs) (eq? op 'const))
4656+
(emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs)))
4657+
(else
4658+
(assert (eq? op '=))
4659+
(emit `(= ,lhs ,rhs)))))
46494660
(define (emit-assignment lhs rhs (op '=))
46504661
(if rhs
46514662
(if (valid-ir-rvalue? lhs rhs)
@@ -4726,21 +4737,26 @@ f(x) = yt(x)
47264737
(when (pair? (cadr lam))
47274738
(error (string "`global const` declaration not allowed inside function" (format-loc current-loc)))))
47284739
(let ((lhs (cadr e)))
4729-
(if (and (symbol? lhs) (underscore-symbol? lhs))
4730-
(compile (caddr e) break-labels value tail)
4731-
(let* ((rhs (compile (caddr e) break-labels #t #f))
4732-
(lhs (if (and arg-map (symbol? lhs))
4733-
(get arg-map lhs lhs)
4734-
lhs)))
4735-
(if (and value rhs)
4736-
(let ((rr (if (or (atom? rhs) (ssavalue? rhs) (eq? (car rhs) 'null))
4737-
rhs (make-ssavalue))))
4738-
(if (not (eq? rr rhs))
4739-
(emit `(= ,rr ,rhs)))
4740-
(emit-assignment-or-setglobal lhs rr (car e))
4741-
(if tail (emit-return tail rr))
4742-
rr)
4743-
(emit-assignment lhs rhs (car e)))))))
4740+
(cond ((and (symbol? lhs) (underscore-symbol? lhs))
4741+
(compile (caddr e) break-labels value tail))
4742+
((and (eq? (car e) 'const) (null? (cddr e)) (globalref? (cadr e)))
4743+
;; No RHS - make undefined constant
4744+
(let ((lhs (cadr e)))
4745+
(emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs))))))
4746+
(else
4747+
(let* ((rhs (compile (caddr e) break-labels #t #f))
4748+
(lhs (if (and arg-map (symbol? lhs))
4749+
(get arg-map lhs lhs)
4750+
lhs)))
4751+
(if (and value rhs)
4752+
(let ((rr (if (or (atom? rhs) (ssavalue? rhs) (eq? (car rhs) 'null))
4753+
rhs (make-ssavalue))))
4754+
(if (not (eq? rr rhs))
4755+
(emit `(= ,rr ,rhs)))
4756+
(emit-assignment-or-setglobal lhs rr (car e))
4757+
(if tail (emit-return tail rr))
4758+
rr)
4759+
(emit-assignment lhs rhs (car e))))))))
47444760
((block)
47454761
(let* ((last-fname filename)
47464762
(fnm (first-non-meta e))

0 commit comments

Comments
 (0)