From 298a7da928a436631bdaa3c3802fc87d8d7b8c26 Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Wed, 16 Apr 2025 16:28:43 -0700 Subject: [PATCH 1/7] 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!. --- Compiler/src/ssair/ir.jl | 2 +- Compiler/src/validation.jl | 3 +- base/docs/basedocs.jl | 32 +++++++++++++++ doc/src/base/base.md | 1 + src/builtin_proto.h | 1 + src/builtins.c | 12 +++++- src/codegen.cpp | 29 -------------- src/interpreter.c | 6 --- src/jlfrontend.scm | 2 +- src/julia-syntax.scm | 82 +++++++++++++++++++++++--------------- src/julia_internal.h | 1 - src/toplevel.c | 27 +------------ 12 files changed, 99 insertions(+), 99 deletions(-) diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index 8a317d2d8cc0d..41cf007a298b4 100644 --- a/Compiler/src/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -581,7 +581,7 @@ function is_relevant_expr(e::Expr) :foreigncall, :isdefined, :copyast, :throw_undef_if_not, :cfunction, :method, :pop_exception, - :leave, :const, :globaldecl, + :leave, :globaldecl, :new_opaque_closure) end diff --git a/Compiler/src/validation.jl b/Compiler/src/validation.jl index 4f9362e97b30d..818cc0f5070ad 100644 --- a/Compiler/src/validation.jl +++ b/Compiler/src/validation.jl @@ -9,7 +9,6 @@ const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}( :(&) => 1:1, :(=) => 2:2, :method => 1:4, - :const => 1:2, :new => 1:typemax(Int), :splatnew => 2:2, :the_exception => 0:0, @@ -149,7 +148,7 @@ function validate_code!(errors::Vector{InvalidCodeError}, c::CodeInfo, is_top_le elseif head === :call || head === :invoke || x.head === :invoke_modify || head === :gc_preserve_end || head === :meta || head === :inbounds || head === :foreigncall || head === :cfunction || - head === :const || head === :leave || head === :pop_exception || + head === :leave || head === :pop_exception || head === :method || head === :global || head === :static_parameter || head === :new || head === :splatnew || head === :thunk || head === :loopinfo || head === :throw_undef_if_not || head === :code_coverage_effect || head === :inline || head === :noinline diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index cf8a087b76489..9dbdddef29b7d 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2753,6 +2753,38 @@ See also [`setpropertyonce!`](@ref Base.setpropertyonce!) and [`setglobal!`](@re """ setglobalonce! +""" + setconst!(module::Module, name::Symbol, [x]) + +Create or replace the constant `name` in `module` with the new value `x`. When +replacing, `x` does not need to have the same type as the original constant. + +When `x` is not given, `name` becomes an undefined constant; it cannot be read +or written to, but can be redefined. + +Note that this function does not update the world age of the current task: +``` +julia> begin + const x = 1 + println(x) + const x = 2 + println(x) + Core.setconst!(Main, :x, 3) + println(x) + end +1 +2 +2 +``` + +!!! compat "Julia 1.12" + This function requires Julia 1.12 or later. Redefining constants on earlier + versions of Julia is unpredictable. + +See also [`const`](@ref). +""" +Core.setconst! + """ typeof(x) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 695f6db46e848..1bad6d17d6e6b 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -493,6 +493,7 @@ Core.modifyglobal! Core.swapglobal! Core.setglobalonce! Core.replaceglobal! +Core.setconst! ``` ## Documentation diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 586d948f722c1..ab8dd306b4bbb 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -58,6 +58,7 @@ extern "C" { XX(opaque_closure_call,"opaque_closure_call") \ XX(replacefield,"replacefield!") \ XX(replaceglobal,"replaceglobal!") \ + XX(setconst,"setconst!") \ XX(setfield,"setfield!") \ XX(setfieldonce,"setfieldonce!") \ XX(setglobal,"setglobal!") \ diff --git a/src/builtins.c b/src/builtins.c index e3a0380182e15..9f6de2d625c4d 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1504,7 +1504,17 @@ JL_CALLABLE(jl_f_setglobalonce) return old == NULL ? jl_true : jl_false; } - +JL_CALLABLE(jl_f_setconst) +{ + JL_NARGS(setconst!, 2, 3); + JL_TYPECHK(setconst!, module, args[0]); + if (nargs == 3) + JL_TYPECHK(setconst!, symbol, args[1]); + jl_binding_t *b = jl_get_module_binding((jl_module_t *)args[0], (jl_sym_t *)args[1], 1); + jl_value_t *val = nargs == 3 ? args[2] : NULL; + jl_declare_constant_val(b, (jl_module_t *)args[0], (jl_sym_t *)args[1], val); + return args[2]; +} // apply_type ----------------------------------------------------------------- diff --git a/src/codegen.cpp b/src/codegen.cpp index a4817aa0e9bc8..29f7629fe581c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -881,15 +881,6 @@ static const auto jlcheckassignonce_func = new JuliaFunction<>{ {T_pjlvalue, T_pjlvalue, T_pjlvalue, PointerType::get(JuliaType::get_jlvalue_ty(C), AddressSpace::CalleeRooted)}, false); }, nullptr, }; -static const auto jldeclareconstval_func = new JuliaFunction<>{ - XSTR(jl_declare_constant_val), - [](LLVMContext &C) { - auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); - auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); - return FunctionType::get(getVoidTy(C), - {T_pjlvalue, T_pjlvalue, T_pjlvalue, T_prjlvalue}, false); }, - nullptr, -}; static const auto jldeclareglobal_func = new JuliaFunction<>{ XSTR(jl_declare_global), [](LLVMContext &C) { @@ -6405,26 +6396,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ jl_method_type); return meth; } - else if (head == jl_const_sym) { - assert(nargs <= 2); - jl_sym_t *sym = (jl_sym_t*)args[0]; - jl_module_t *mod = ctx.module; - if (jl_is_globalref(sym)) { - mod = jl_globalref_mod(sym); - sym = jl_globalref_name(sym); - } - if (jl_is_symbol(sym)) { - jl_binding_t *bnd = jl_get_module_binding(mod, sym, 1); - if (nargs == 2) { - jl_cgval_t rhs = emit_expr(ctx, args[1]); - ctx.builder.CreateCall(prepare_call(jldeclareconstval_func), - { julia_binding_gv(ctx, bnd), literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, rhs) }); - } else { - ctx.builder.CreateCall(prepare_call(jldeclareconstval_func), - { julia_binding_gv(ctx, bnd), literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), ConstantPointerNull::get(cast(ctx.types().T_prjlvalue)) }); - } - } - } else if (head == jl_globaldecl_sym) { assert(nargs <= 2 && nargs >= 1); jl_sym_t *sym = (jl_sym_t*)args[0]; diff --git a/src/interpreter.c b/src/interpreter.c index 2f7f9f8947576..bc8de67f96ab7 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -642,12 +642,6 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, jl_declare_global(s->module, jl_exprarg(stmt, 0), val, 1); s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing; } - else if (head == jl_const_sym) { - jl_value_t *val = jl_expr_nargs(stmt) == 1 ? NULL : eval_value(jl_exprarg(stmt, 1), s); - s->locals[jl_source_nslots(s->src) + s->ip] = val; // temporarily root - jl_eval_const_decl(s->module, jl_exprarg(stmt, 0), val); - s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing; - } else if (head == jl_latestworld_sym) { ct->world_age = jl_atomic_load_acquire(&jl_world_counter); } diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index a678d481eab1f..2b092d4e4cbe1 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -140,7 +140,7 @@ (and (pair? e) (or (memq (car e) '(toplevel line module export public error incomplete)) - (and (memq (car e) '(global const)) (every symbol? (cdr e)))))) + (and (memq (car e) '(global)) (every symbol? (cdr e)))))) (define *in-lowering* #f) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4fa6dc2e14a70..11860d02daa7d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1460,16 +1460,22 @@ (if (length= e 3) `(const ,(cadr e) ,(expand-forms (caddr e))) (let ((arg (cadr e))) - (case (car arg) - ((global) (let ((asgn (cadr arg))) - (check-assignment asgn) - `(block - ,.(map (lambda (v) `(global ,v)) - (lhs-bound-names (cadr asgn))) - ,(expand-assignment asgn #t)))) - ((=) (check-assignment arg) - (expand-assignment arg #t)) - (else (error "expected assignment after \"const\"")))))) + (cond + ((symbol? arg) + ;; Undefined constant: Expr(:const, :a) (not available in surface syntax) + `(block ,e (latestworld))) + ((eq? (car arg) 'global) + (let ((asgn (cadr arg))) + (check-assignment asgn) + `(block + ,.(map (lambda (v) `(global ,v)) + (lhs-bound-names (cadr asgn))) + ,(expand-assignment asgn #t)))) + ((eq? (car arg) '=) + (check-assignment arg) + (expand-assignment arg #t)) + (else + (error "expected assignment after \"const\"")))))) (define (expand-atomic-decl e) (error "unimplemented or unsupported atomic declaration")) @@ -3043,7 +3049,8 @@ (set! vars (cons (cadr e) vars))) ((= const) (let ((v (decl-var (cadr e)))) - (find-assigned-vars- (caddr e)) + (unless (and (eq? (car e) 'const) (null? (cddr e))) + (find-assigned-vars- (caddr e))) (if (or (ssavalue? v) (globalref? v) (underscore-symbol? v)) '() (set! vars (cons v vars))))) @@ -3460,7 +3467,8 @@ (vinfo:set-sa! vi #f) (vinfo:set-sa! vi #t)) (vinfo:set-asgn! vi #t)))) - (analyze-vars (caddr e) env captvars sp tab)) + (unless (null? (cddr e)) + (analyze-vars (caddr e) env captvars sp tab))) ((call) (let ((vi (get tab (cadr e) #f))) (if vi @@ -4070,8 +4078,6 @@ f(x) = yt(x) '(null) `(newvar ,(cadr e)))))) ((const) - ;; Check we've expanded surface `const` (1 argument form) - (assert (and (length= e 3))) (when (globalref? (cadr e)) (put! globals (cadr e) #f)) e) @@ -4642,10 +4648,15 @@ f(x) = yt(x) (list cnd)))))) tests)) (define (emit-assignment-or-setglobal lhs rhs (op '=)) - ;; (const (globalref _ _) _) does not use setglobal! - (if (and (globalref? lhs) (eq? op '=)) - (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs)) - (emit `(,op ,lhs ,rhs)))) + ;; (= (globalref _ _) _) => setglobal! + ;; (const (globalref _ _) _) => setconst! + (cond ((and (globalref? lhs) (eq? op '=)) + (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) + ((and (globalref? lhs) (eq? op 'const)) + (emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) + (else + (assert (eq? op '=)) + (emit `(= ,lhs ,rhs))))) (define (emit-assignment lhs rhs (op '=)) (if rhs (if (valid-ir-rvalue? lhs rhs) @@ -4726,21 +4737,26 @@ f(x) = yt(x) (when (pair? (cadr lam)) (error (string "`global const` declaration not allowed inside function" (format-loc current-loc))))) (let ((lhs (cadr e))) - (if (and (symbol? lhs) (underscore-symbol? lhs)) - (compile (caddr e) break-labels value tail) - (let* ((rhs (compile (caddr e) break-labels #t #f)) - (lhs (if (and arg-map (symbol? lhs)) - (get arg-map lhs lhs) - lhs))) - (if (and value rhs) - (let ((rr (if (or (atom? rhs) (ssavalue? rhs) (eq? (car rhs) 'null)) - rhs (make-ssavalue)))) - (if (not (eq? rr rhs)) - (emit `(= ,rr ,rhs))) - (emit-assignment-or-setglobal lhs rr (car e)) - (if tail (emit-return tail rr)) - rr) - (emit-assignment lhs rhs (car e))))))) + (cond ((and (symbol? lhs) (underscore-symbol? lhs)) + (compile (caddr e) break-labels value tail)) + ((and (eq? (car e) 'const) (null? (cddr e)) (globalref? (cadr e))) + ;; No RHS - make undefined constant + (let ((lhs (cadr e))) + (emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs)))))) + (else + (let* ((rhs (compile (caddr e) break-labels #t #f)) + (lhs (if (and arg-map (symbol? lhs)) + (get arg-map lhs lhs) + lhs))) + (if (and value rhs) + (let ((rr (if (or (atom? rhs) (ssavalue? rhs) (eq? (car rhs) 'null)) + rhs (make-ssavalue)))) + (if (not (eq? rr rhs)) + (emit `(= ,rr ,rhs))) + (emit-assignment-or-setglobal lhs rr (car e)) + (if tail (emit-return tail rr)) + rr) + (emit-assignment lhs rhs (car e)))))))) ((block) (let* ((last-fname filename) (fnm (first-non-meta e)) diff --git a/src/julia_internal.h b/src/julia_internal.h index 5d418ceb2051c..9b462dae4997c 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -862,7 +862,6 @@ jl_array_t *jl_get_loaded_modules(void); JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree); int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; -JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val); void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty); void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong); diff --git a/src/toplevel.c b/src/toplevel.c index 64a559ac774e6..743fb427a1324 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -386,7 +386,7 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int // might still need to be optimized. return; } - else if (head == jl_const_sym || head == jl_copyast_sym) { + else if (head == jl_copyast_sym) { // Note: `copyast` is included here since it indicates the presence of // `quote` and probably `eval`. *has_defs = 1; @@ -587,7 +587,6 @@ int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT ((jl_expr_t*)e)->head == jl_thunk_sym || ((jl_expr_t*)e)->head == jl_global_sym || ((jl_expr_t*)e)->head == jl_globaldecl_sym || - ((jl_expr_t*)e)->head == jl_const_sym || ((jl_expr_t*)e)->head == jl_toplevel_sym || ((jl_expr_t*)e)->head == jl_error_sym || ((jl_expr_t*)e)->head == jl_incomplete_sym); @@ -605,7 +604,7 @@ int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT head == jl_method_sym) { return 0; } - if (head == jl_global_sym || head == jl_const_sym) { + if (head == jl_global_sym) { size_t i, l = jl_array_nrows(ex->args); for (i = 0; i < l; i++) { jl_value_t *a = jl_exprarg(ex, i); @@ -741,23 +740,6 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b, jl return jl_declare_constant_val2(b, mod, var, val, val ? PARTITION_KIND_CONST : PARTITION_KIND_UNDEF_CONST); } -JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val) -{ - jl_module_t *gm; - jl_sym_t *gs; - if (jl_is_globalref(arg)) { - gm = jl_globalref_mod(arg); - gs = jl_globalref_name(arg); - } - else { - assert(jl_is_symbol(arg)); - gm = m; - gs = (jl_sym_t*)arg; - } - jl_binding_t *b = jl_get_module_binding(gm, gs, 1); - jl_declare_constant_val(b, gm, gs, val); -} - JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno) { jl_task_t *ct = jl_current_task; @@ -960,11 +942,6 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val JL_GC_POP(); return jl_nothing; } - else if (head == jl_const_sym) { - jl_eval_const_decl(m, jl_exprarg(ex, 0), NULL); - JL_GC_POP(); - return jl_nothing; - } else if (head == jl_toplevel_sym) { jl_value_t *res = jl_nothing; int i; From 15fb356d06798e436f6cef404e84e5b21da11d89 Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Fri, 25 Apr 2025 13:08:47 -0700 Subject: [PATCH 2/7] Lowering: emit Core.setglobal! instead of Base.setglobal! --- src/julia-syntax.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 11860d02daa7d..dd4d70f568cc4 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4651,7 +4651,7 @@ f(x) = yt(x) ;; (= (globalref _ _) _) => setglobal! ;; (const (globalref _ _) _) => setconst! (cond ((and (globalref? lhs) (eq? op '=)) - (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) + (emit `(call (core setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) ((and (globalref? lhs) (eq? op 'const)) (emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) (else From b2676b5ce36817fb93ce685b570d38608d7c4aa7 Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Fri, 25 Apr 2025 13:38:02 -0700 Subject: [PATCH 3/7] Mark :latestworld expressions as has_defs --- src/toplevel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/toplevel.c b/src/toplevel.c index 743fb427a1324..79009dacd0c65 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -386,6 +386,14 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int // might still need to be optimized. return; } + else if (head == jl_latestworld_sym) { + // Detects: + // const + // using, import + // export (public is not lowered) + // global, globaldecl + *has_defs = 1; + } else if (head == jl_copyast_sym) { // Note: `copyast` is included here since it indicates the presence of // `quote` and probably `eval`. From 3b1e579d44f0a7db7931b0cdea615f26e201dcf1 Mon Sep 17 00:00:00 2001 From: Sam Schweigel <33556084+xal-0@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:21:10 -0700 Subject: [PATCH 4/7] Apply suggestions from code review (docs) Co-authored-by: Jameson Nash --- base/docs/basedocs.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 9dbdddef29b7d..525b2ad369ef4 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2762,7 +2762,7 @@ replacing, `x` does not need to have the same type as the original constant. When `x` is not given, `name` becomes an undefined constant; it cannot be read or written to, but can be redefined. -Note that this function does not update the world age of the current task: +Unlike the syntax `const`, calling this function does not insert `Core.@latestworld` to update the world age of the current frame: ``` julia> begin const x = 1 @@ -2771,10 +2771,13 @@ julia> begin println(x) Core.setconst!(Main, :x, 3) println(x) + Core.@latestworld + println(x) end 1 2 2 +3 ``` !!! compat "Julia 1.12" From b11c9621672b8fcf3873cde8d84805f40bb26b4e Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Tue, 29 Apr 2025 10:06:25 -0700 Subject: [PATCH 5/7] Rename Core.setconst! to Core.declare_const Core.declare_const doesn't mutate state and so shouldn't end with !. This also brings it in line with potential future builtins like Core.declare_global. --- base/docs/basedocs.jl | 6 +++--- doc/src/base/base.md | 2 +- src/builtin_proto.h | 2 +- src/builtins.c | 8 ++++---- src/julia-syntax.scm | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 525b2ad369ef4..76527d835848d 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2754,7 +2754,7 @@ See also [`setpropertyonce!`](@ref Base.setpropertyonce!) and [`setglobal!`](@re setglobalonce! """ - setconst!(module::Module, name::Symbol, [x]) + declare_const(module::Module, name::Symbol, [x]) Create or replace the constant `name` in `module` with the new value `x`. When replacing, `x` does not need to have the same type as the original constant. @@ -2769,7 +2769,7 @@ julia> begin println(x) const x = 2 println(x) - Core.setconst!(Main, :x, 3) + Core.declare_const(Main, :x, 3) println(x) Core.@latestworld println(x) @@ -2786,7 +2786,7 @@ julia> begin See also [`const`](@ref). """ -Core.setconst! +Core.declare_const """ typeof(x) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 1bad6d17d6e6b..3305f08b1338c 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -493,7 +493,7 @@ Core.modifyglobal! Core.swapglobal! Core.setglobalonce! Core.replaceglobal! -Core.setconst! +Core.declare_const ``` ## Documentation diff --git a/src/builtin_proto.h b/src/builtin_proto.h index ab8dd306b4bbb..fb761abcaef81 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -58,7 +58,7 @@ extern "C" { XX(opaque_closure_call,"opaque_closure_call") \ XX(replacefield,"replacefield!") \ XX(replaceglobal,"replaceglobal!") \ - XX(setconst,"setconst!") \ + XX(declare_const,"declare_const") \ XX(setfield,"setfield!") \ XX(setfieldonce,"setfieldonce!") \ XX(setglobal,"setglobal!") \ diff --git a/src/builtins.c b/src/builtins.c index 9f6de2d625c4d..590af60bf6dc4 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1504,12 +1504,12 @@ JL_CALLABLE(jl_f_setglobalonce) return old == NULL ? jl_true : jl_false; } -JL_CALLABLE(jl_f_setconst) +JL_CALLABLE(jl_f_declare_const) { - JL_NARGS(setconst!, 2, 3); - JL_TYPECHK(setconst!, module, args[0]); + JL_NARGS(declare_const, 2, 3); + JL_TYPECHK(declare_const, module, args[0]); if (nargs == 3) - JL_TYPECHK(setconst!, symbol, args[1]); + JL_TYPECHK(declare_const, symbol, args[1]); jl_binding_t *b = jl_get_module_binding((jl_module_t *)args[0], (jl_sym_t *)args[1], 1); jl_value_t *val = nargs == 3 ? args[2] : NULL; jl_declare_constant_val(b, (jl_module_t *)args[0], (jl_sym_t *)args[1], val); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index dd4d70f568cc4..3cf7d8a53d5cb 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4649,11 +4649,11 @@ f(x) = yt(x) tests)) (define (emit-assignment-or-setglobal lhs rhs (op '=)) ;; (= (globalref _ _) _) => setglobal! - ;; (const (globalref _ _) _) => setconst! + ;; (const (globalref _ _) _) => declare_const (cond ((and (globalref? lhs) (eq? op '=)) (emit `(call (core setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) ((and (globalref? lhs) (eq? op 'const)) - (emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) + (emit `(call (core declare_const) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) (else (assert (eq? op '=)) (emit `(= ,lhs ,rhs))))) @@ -4742,7 +4742,7 @@ f(x) = yt(x) ((and (eq? (car e) 'const) (null? (cddr e)) (globalref? (cadr e))) ;; No RHS - make undefined constant (let ((lhs (cadr e))) - (emit `(call (core setconst!) ,(cadr lhs) (inert ,(caddr lhs)))))) + (emit `(call (core declare_const) ,(cadr lhs) (inert ,(caddr lhs)))))) (else (let* ((rhs (compile (caddr e) break-labels #t #f)) (lhs (if (and arg-map (symbol? lhs)) From 1086cf7de70f5f37dba6185114bf6d8d1126b2bd Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Mon, 29 Sep 2025 11:35:40 -0700 Subject: [PATCH 6/7] Don't return garbage from Core.declare_const with 2 arguments --- src/builtins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builtins.c b/src/builtins.c index 5eee721837bc8..f20f8082bba8c 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1521,7 +1521,7 @@ JL_CALLABLE(jl_f_declare_const) jl_binding_t *b = jl_get_module_binding((jl_module_t *)args[0], (jl_sym_t *)args[1], 1); jl_value_t *val = nargs == 3 ? args[2] : NULL; jl_declare_constant_val(b, (jl_module_t *)args[0], (jl_sym_t *)args[1], val); - return args[2]; + return nargs > 2 ? args[2] : jl_nothing; } // import, using -------------------------------------------------------------- From d9577b35ddfd9ff1e0ce56020986484079f3f1d5 Mon Sep 17 00:00:00 2001 From: Sam Schweigel Date: Mon, 29 Sep 2025 16:35:51 -0700 Subject: [PATCH 7/7] Detect Core.declare_const instead of Expr(:latestworld) This isn't ideal, but is required to have inference run in the same situations as before. --- src/toplevel.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/toplevel.c b/src/toplevel.c index 716f6ed0fc559..07d6bf66b3315 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -385,14 +385,6 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int // might still need to be optimized. return; } - else if (head == jl_latestworld_sym) { - // Detects: - // const - // using, import - // export (public is not lowered) - // global, globaldecl - *has_defs = 1; - } else if (head == jl_copyast_sym) { // Note: `copyast` is included here since it indicates the presence of // `quote` and probably `eval`. @@ -433,7 +425,8 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int if (jl_is_intrinsic(called) && jl_unbox_int32(called) == (int)llvmcall) { *has_ccall = 1; } - if (called == BUILTIN(_typebody)) { // TODO: rely on latestworld instead of function callee detection here (or add it to jl_is_toplevel_only_expr) + // TODO: rely on latestworld instead of function callee detection here (or add it to jl_is_toplevel_only_expr) + if (called == BUILTIN(_typebody) || called == BUILTIN(declare_const)) { *has_defs = 1; } }