Skip to content

Commit f00f2e6

Browse files
committed
lowering: Refactor lowering for const and typed globals
This is a prepratory commit for #54654 to change the lowering of `const` and typed globals to be compatible with the new semantics. Currently, we lower `const a::T = val` to: ``` const a global a::T a = val ``` (which further expands to typed-globals an implicit converts). This works, because, under the hood, our const declarations are actually assign-once globals. Note however, that this is not syntactically reachable, since we have a parse error for plain `const a`: ``` julia> const a ERROR: ParseError: # Error @ REPL[1]:1:1 const a └─────┘ ── expected assignment after `const` Stacktrace: [1] top-level scope @ none:1 ``` However, this lowering is not atomic with respect to world age. The semantics in #54654 require that the const-ness and the value are established atomically (with respect to world age, potentially on another thread) or undergo invalidation. To resolve this issue, this PR changes the lowering of `const a::T = val` to: ``` let local a::T = val const (global a) = a end ``` where the latter is a special syntax form `Expr(:const, GlobalRef(,:a), :a)`. A similar change is made to const global declarations, which previously lowered via intrinsic, i.e. `global a::T = val` lowered to: ``` global a Core.set_binding_type!(Main, :a, T) _T = Core.get_binding_type(Main, :a) if !isa(val, _T) val = convert(_T, val) end a = val ``` This changes the `set_binding_type!` to instead be a syntax form `Expr(:globaldecl, :a, T)`. This is not technically required, but we currently do not use intrinsics for world-age affecting side-effects anywhere else in the system. In particular, after #54654, it would be illegal to call `set_binding_type!` in anything but top-level context. Now, we have discussed in the past that there should potentially be intrinsic functions for global modifications (method table additions, etc), currently only reachable through `Core.eval`, but such an intrinsic would require semantics that differ from both the current `set_binding_type!` and the new `:globaldecl`. Using an Expr form here is the most consistent with our current practice for these sort of things elsewhere and accordingly, this PR removes the intrinsic. Note that this PR does not yet change any syntax semantics, although there could in principle be a reordering of side-effects within an expression (e.g. things like `global a::(@isdefined(a) ? Int : Float64)` might behave differently after this commit. However, we never defined the order of side effects (which is part of what this is cleaning up, although, I am not formally defining any specific ordering here either - #54654 will do some of that), and that is not a common case, so this PR should be largely considered non-semantic with respect to the syntax change.
1 parent 6f39acb commit f00f2e6

File tree

15 files changed

+360
-169
lines changed

15 files changed

+360
-169
lines changed

base/docs/basedocs.jl

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,18 +2542,6 @@ Retrieve the declared type of the binding `name` from the module `module`.
25422542
"""
25432543
Core.get_binding_type
25442544

2545-
"""
2546-
Core.set_binding_type!(module::Module, name::Symbol, [type::Type])
2547-
2548-
Set the declared type of the binding `name` in the module `module` to `type`. Error if the
2549-
binding already has a type that is not equivalent to `type`. If the `type` argument is
2550-
absent, set the binding type to `Any` if unset, but do not error.
2551-
2552-
!!! compat "Julia 1.9"
2553-
This function requires Julia 1.9 or later.
2554-
"""
2555-
Core.set_binding_type!
2556-
25572545
"""
25582546
swapglobal!(module::Module, name::Symbol, x, [order::Symbol=:monotonic])
25592547

doc/src/devdocs/builtins.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ Core.Intrinsics.atomic_pointerswap
2222
Core.Intrinsics.atomic_pointermodify
2323
Core.Intrinsics.atomic_pointerreplace
2424
Core.get_binding_type
25-
Core.set_binding_type!
2625
Core.IntrinsicFunction
2726
Core.Intrinsics
2827
Core.IR

src/ast.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ JL_DLLEXPORT jl_sym_t *jl_thunk_sym;
6060
JL_DLLEXPORT jl_sym_t *jl_foreigncall_sym;
6161
JL_DLLEXPORT jl_sym_t *jl_as_sym;
6262
JL_DLLEXPORT jl_sym_t *jl_global_sym;
63+
JL_DLLEXPORT jl_sym_t *jl_globaldecl_sym;
6364
JL_DLLEXPORT jl_sym_t *jl_local_sym;
6465
JL_DLLEXPORT jl_sym_t *jl_list_sym;
6566
JL_DLLEXPORT jl_sym_t *jl_dot_sym;
@@ -355,6 +356,7 @@ void jl_init_common_symbols(void)
355356
jl_opaque_closure_method_sym = jl_symbol("opaque_closure_method");
356357
jl_const_sym = jl_symbol("const");
357358
jl_global_sym = jl_symbol("global");
359+
jl_globaldecl_sym = jl_symbol("globaldecl");
358360
jl_local_sym = jl_symbol("local");
359361
jl_thunk_sym = jl_symbol("thunk");
360362
jl_toplevel_sym = jl_symbol("toplevel");

src/builtin_proto.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ JL_CALLABLE(jl_f__primitivetype);
7979
JL_CALLABLE(jl_f__setsuper);
8080
JL_CALLABLE(jl_f__equiv_typedef);
8181
JL_CALLABLE(jl_f_get_binding_type);
82-
JL_CALLABLE(jl_f_set_binding_type);
8382
JL_CALLABLE(jl_f__compute_sparams);
8483
JL_CALLABLE(jl_f__svec_ref);
8584
#ifdef __cplusplus

src/builtins.c

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,27 +1384,6 @@ JL_CALLABLE(jl_f_get_binding_type)
13841384
return ty;
13851385
}
13861386

1387-
JL_CALLABLE(jl_f_set_binding_type)
1388-
{
1389-
JL_NARGS(set_binding_type!, 2, 3);
1390-
jl_module_t *m = (jl_module_t*)args[0];
1391-
jl_sym_t *s = (jl_sym_t*)args[1];
1392-
JL_TYPECHK(set_binding_type!, module, (jl_value_t*)m);
1393-
JL_TYPECHK(set_binding_type!, symbol, (jl_value_t*)s);
1394-
jl_value_t *ty = nargs == 2 ? (jl_value_t*)jl_any_type : args[2];
1395-
JL_TYPECHK(set_binding_type!, type, ty);
1396-
jl_binding_t *b = jl_get_binding_wr(m, s, 0);
1397-
jl_value_t *old_ty = NULL;
1398-
if (jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, ty)) {
1399-
jl_gc_wb(b, ty);
1400-
}
1401-
else if (nargs != 2 && !jl_types_equal(ty, old_ty)) {
1402-
jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.",
1403-
jl_symbol_name(m->name), jl_symbol_name(s));
1404-
}
1405-
return jl_nothing;
1406-
}
1407-
14081387
JL_CALLABLE(jl_f_swapglobal)
14091388
{
14101389
enum jl_memory_order order = jl_memory_order_release;
@@ -2416,7 +2395,6 @@ void jl_init_primitives(void) JL_GC_DISABLED
24162395
jl_builtin_getglobal = add_builtin_func("getglobal", jl_f_getglobal);
24172396
jl_builtin_setglobal = add_builtin_func("setglobal!", jl_f_setglobal);
24182397
add_builtin_func("get_binding_type", jl_f_get_binding_type);
2419-
add_builtin_func("set_binding_type!", jl_f_set_binding_type);
24202398
jl_builtin_swapglobal = add_builtin_func("swapglobal!", jl_f_swapglobal);
24212399
jl_builtin_replaceglobal = add_builtin_func("replaceglobal!", jl_f_replaceglobal);
24222400
jl_builtin_modifyglobal = add_builtin_func("modifyglobal!", jl_f_modifyglobal);

src/codegen.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,24 @@ static const auto jldeclareconst_func = new JuliaFunction<>{
932932
{T_pjlvalue, T_pjlvalue, T_pjlvalue}, false); },
933933
nullptr,
934934
};
935+
static const auto jldeclareconstval_func = new JuliaFunction<>{
936+
XSTR(jl_declare_constant_val),
937+
[](LLVMContext &C) {
938+
auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C);
939+
auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C);
940+
return FunctionType::get(getVoidTy(C),
941+
{T_pjlvalue, T_pjlvalue, T_pjlvalue, T_prjlvalue}, false); },
942+
nullptr,
943+
};
944+
static const auto jldeclareglobal_func = new JuliaFunction<>{
945+
XSTR(jl_declare_global),
946+
[](LLVMContext &C) {
947+
auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C);
948+
auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C);
949+
return FunctionType::get(getVoidTy(C),
950+
{T_pjlvalue, T_pjlvalue, T_prjlvalue}, false); },
951+
nullptr,
952+
};
935953
static const auto jlgetbindingorerror_func = new JuliaFunction<>{
936954
XSTR(jl_get_binding_or_error),
937955
[](LLVMContext &C) {
@@ -6461,7 +6479,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
64616479
return meth;
64626480
}
64636481
else if (head == jl_const_sym) {
6464-
assert(nargs == 1);
6482+
assert(nargs <= 2);
64656483
jl_sym_t *sym = (jl_sym_t*)args[0];
64666484
jl_module_t *mod = ctx.module;
64676485
if (jl_is_globalref(sym)) {
@@ -6471,10 +6489,29 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
64716489
if (jl_is_symbol(sym)) {
64726490
jl_binding_t *bnd = NULL;
64736491
Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, true);
6474-
if (bp)
6475-
ctx.builder.CreateCall(prepare_call(jldeclareconst_func),
6476-
{ bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym) });
6492+
if (bp) {
6493+
if (nargs == 2) {
6494+
jl_cgval_t rhs = emit_expr(ctx, args[1]);
6495+
ctx.builder.CreateCall(prepare_call(jldeclareconstval_func),
6496+
{ bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, rhs) });
6497+
} else {
6498+
ctx.builder.CreateCall(prepare_call(jldeclareconst_func),
6499+
{ bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym) });
6500+
}
6501+
}
6502+
}
6503+
}
6504+
else if (head == jl_globaldecl_sym) {
6505+
assert(nargs == 2);
6506+
jl_sym_t *sym = (jl_sym_t*)args[0];
6507+
jl_module_t *mod = ctx.module;
6508+
if (jl_is_globalref(sym)) {
6509+
mod = jl_globalref_mod(sym);
6510+
sym = jl_globalref_name(sym);
64776511
}
6512+
jl_cgval_t typ = emit_expr(ctx, args[1]);
6513+
ctx.builder.CreateCall(prepare_call(jldeclareglobal_func),
6514+
{ literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, typ) });
64786515
}
64796516
else if (head == jl_new_sym) {
64806517
bool is_promotable = false;

src/interpreter.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,18 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
628628
jl_value_t *res = jl_toplevel_eval(s->module, stmt);
629629
s->locals[jl_source_nslots(s->src) + s->ip] = res;
630630
}
631+
else if (head == jl_globaldecl_sym) {
632+
jl_value_t *val = eval_value(jl_exprarg(stmt, 1), s);
633+
s->locals[jl_source_nslots(s->src) + s->ip] = val; // temporarily root
634+
jl_declare_global(s->module, jl_exprarg(stmt, 0), val);
635+
s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing;
636+
}
637+
else if (head == jl_const_sym) {
638+
jl_value_t *val = jl_expr_nargs(stmt) == 1 ? NULL : eval_value(jl_exprarg(stmt, 1), s);
639+
s->locals[jl_source_nslots(s->src) + s->ip] = val; // temporarily root
640+
jl_eval_const_decl(s->module, jl_exprarg(stmt, 0), val);
641+
s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing;
642+
}
631643
else if (jl_is_toplevel_only_expr(stmt)) {
632644
jl_toplevel_eval(s->module, stmt);
633645
}

0 commit comments

Comments
 (0)