Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Compiler/src/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 1 addition & 2 deletions Compiler/src/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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])

Expand Down
1 change: 1 addition & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ Core.modifyglobal!
Core.swapglobal!
Core.setglobalonce!
Core.replaceglobal!
Core.declare_const
```

## Documentation
Expand Down
1 change: 1 addition & 0 deletions src/builtin_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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!") \
Expand Down
12 changes: 12 additions & 0 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
Expand Down
29 changes: 0 additions & 29 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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<PointerType>(ctx.types().T_prjlvalue)) });
}
}
}
else if (head == jl_globaldecl_sym) {
assert(nargs <= 2 && nargs >= 1);
jl_sym_t *sym = (jl_sym_t*)args[0];
Expand Down
6 changes: 0 additions & 6 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/jlfrontend.scm
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
82 changes: 49 additions & 33 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down Expand Up @@ -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)))))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand Down
1 change: 0 additions & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
30 changes: 4 additions & 26 deletions src/toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down