diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index e6a8ffe6d539e..ac160201a84a5 100644 --- a/Compiler/src/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -584,7 +584,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 067d9460b52ec..af7dab14165c3 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 19dcf035b837e..38c493094dbae 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2758,6 +2758,41 @@ See also [`setpropertyonce!`](@ref Base.setpropertyonce!) and [`setglobal!`](@re """ setglobalonce! +""" + 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. + +When `x` is not given, `name` becomes an undefined constant; it cannot be read +or written to, but can be redefined. + +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 + println(x) + const x = 2 + println(x) + Core.declare_const(Main, :x, 3) + println(x) + Core.@latestworld + println(x) + end +1 +2 +2 +3 +``` + +!!! 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.declare_const + """ _import(to::Module, from::Module, asname::Symbol, [sym::Symbol, imported::Bool]) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 25c6b3af73ea6..aaf1678c1ee9f 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -501,6 +501,7 @@ Core.modifyglobal! Core.swapglobal! Core.setglobalonce! Core.replaceglobal! +Core.declare_const ``` ## Documentation diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 0da7a0833bb80..f3a007a032474 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -61,6 +61,7 @@ extern "C" { XX(opaque_closure_call,"opaque_closure_call") \ XX(replacefield,"replacefield!") \ XX(replaceglobal,"replaceglobal!") \ + 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 a30e8ee77e6ac..3dc13833b93c9 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1518,6 +1518,18 @@ JL_CALLABLE(jl_f_setglobalonce) return old == NULL ? jl_true : jl_false; } +JL_CALLABLE(jl_f_declare_const) +{ + JL_NARGS(declare_const, 2, 3); + JL_TYPECHK(declare_const, module, args[0]); + if (nargs == 3) + 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); + return nargs > 2 ? args[2] : jl_nothing; +} + // import, using -------------------------------------------------------------- // Import binding `from.sym` as `asname` into `to`: diff --git a/src/codegen.cpp b/src/codegen.cpp index 45ef1b965ea71..ace576c849c67 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) { @@ -6469,26 +6460,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 0ae4698d9788b..3c2a6493996e0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -643,12 +643,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 641b753365b34..a3eee2e17800b 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")) @@ -3100,7 +3106,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))))) @@ -3522,7 +3529,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 @@ -4126,8 +4134,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) @@ -4696,10 +4702,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 _ _) _) => 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 declare_const) ,(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) @@ -4780,21 +4791,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 declare_const) ,(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 7184be910a84f..52c8d77c2b698 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -895,7 +895,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 dc1cb1a6f77fc..07d6bf66b3315 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -385,7 +385,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; @@ -425,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; } } @@ -486,7 +487,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); @@ -503,7 +503,7 @@ int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT head == jl_incomplete_sym || 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); @@ -587,23 +587,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); -} - static jl_value_t *jl_eval_toplevel_stmts(jl_module_t *JL_NONNULL m, jl_array_t *stmts, int fast, int need_value, const char **toplevel_filename, int *toplevel_lineno) { jl_task_t *ct = jl_current_task; @@ -725,11 +708,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_eval_toplevel_stmts(m, ex->args, fast, 1, toplevel_filename, toplevel_lineno);