diff --git a/Compiler/test/abioverride.jl b/Compiler/test/abioverride.jl index 49907ea8e4c63..feb992b27ee43 100644 --- a/Compiler/test/abioverride.jl +++ b/Compiler/test/abioverride.jl @@ -46,7 +46,7 @@ let world = Base.tls_world_age() global new_ci = Core.CodeInstance(Core.ABIOverride(Tuple{typeof(myplus), Int}, mi), #=owner=#SecondArgConstOverride(1), new_source.rettype, Any#=new_source.exctype is missing=#, #=inferred_const=#nothing, #=code=#nothing, #=const_flags=#Int32(0), - new_source.min_world, new_source.max_world, #=new_source.ipo_purity_bits is missing=#UInt32(0), + new_source.min_world, typemax(UInt), #=new_source.ipo_purity_bits is missing=#UInt32(0), #=analysis_results=#nothing, new_source.debuginfo, new_source.edges) # Poke the CI into the global cache diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 61c0cf71e70c2..061a94bffd9cf 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -75,18 +75,23 @@ const META = gensym(:meta) const METAType = IdDict{Any,Any} function meta(m::Module; autoinit::Bool=true) - if !isdefined(m, META) || getfield(m, META) === nothing - autoinit ? initmeta(m) : return nothing + if !isdefinedglobal(m, META) + return autoinit ? invokelatest(initmeta, m) : nothing end - return getfield(m, META)::METAType + # TODO: This `invokelatest` is not technically required, but because + # of the automatic constant backdating is currently required to avoid + # a warning. + return invokelatest(getglobal, m, META)::METAType end function initmeta(m::Module) - if !isdefined(m, META) || getfield(m, META) === nothing - Core.eval(m, :($META = $(METAType()))) + if !isdefinedglobal(m, META) + val = METAType() + Core.eval(m, :(const $META = $val)) push!(modules, m) + return val end - nothing + return getglobal(m, META) end function signature!(tv::Vector{Any}, expr::Expr) diff --git a/base/exports.jl b/base/exports.jl index 56cd58ce269e7..d81067478dd55 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -818,6 +818,7 @@ export @invoke, invokelatest, @invokelatest, + @world, # loading source files __precompile__, diff --git a/base/loading.jl b/base/loading.jl index 240406292246b..57d69f49483c9 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1389,7 +1389,7 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) restored = sv[1]::Vector{Any} for M in restored M = M::Module - if isdefined(M, Base.Docs.META) && getfield(M, Base.Docs.META) !== nothing + if isdefinedglobal(M, Base.Docs.META) push!(Base.Docs.modules, M) end if is_root_module(M) diff --git a/src/builtins.c b/src/builtins.c index 90d8a0d453e20..f67ef65d35356 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1395,7 +1395,7 @@ JL_CALLABLE(jl_f_setglobal) jl_atomic_error("setglobal!: module binding cannot be written non-atomically"); else if (order >= jl_memory_order_seq_cst) jl_fence(); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); jl_checked_assignment(b, mod, var, args[2]); // release store if (order >= jl_memory_order_seq_cst) jl_fence(); @@ -1430,7 +1430,7 @@ JL_CALLABLE(jl_f_swapglobal) if (order == jl_memory_order_notatomic) jl_atomic_error("swapglobal!: module binding cannot be written non-atomically"); // is seq_cst already, no fence needed - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); return jl_checked_swap(b, mod, var, args[2]); } @@ -1448,7 +1448,7 @@ JL_CALLABLE(jl_f_modifyglobal) JL_TYPECHK(modifyglobal!, symbol, (jl_value_t*)var); if (order == jl_memory_order_notatomic) jl_atomic_error("modifyglobal!: module binding cannot be written non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); // is seq_cst already, no fence needed return jl_checked_modify(b, mod, var, args[2], args[3]); } @@ -1477,7 +1477,7 @@ JL_CALLABLE(jl_f_replaceglobal) jl_atomic_error("replaceglobal!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("replaceglobal!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); // is seq_cst already, no fence needed return jl_checked_replace(b, mod, var, args[2], args[3]); } @@ -1506,7 +1506,7 @@ JL_CALLABLE(jl_f_setglobalonce) jl_atomic_error("setglobalonce!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("setglobalonce!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var, 0); + jl_binding_t *b = jl_get_binding_wr(mod, var); // is seq_cst already, no fence needed jl_value_t *old = jl_checked_assignonce(b, mod, var, args[2]); return old == NULL ? jl_true : jl_false; diff --git a/src/codegen.cpp b/src/codegen.cpp index 19ee1e9161152..e9e4275672c7e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -930,12 +930,12 @@ static const auto jlgetbindingorerror_func = new JuliaFunction<>{ }, nullptr, }; -static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{ - XSTR(jl_get_binding_wr), +static const auto jlcheckbpwritable_func = new JuliaFunction<>{ + XSTR(jl_check_binding_currently_writable), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); - return FunctionType::get(T_pjlvalue, - {T_pjlvalue, T_pjlvalue, getInt32Ty(C)}, false); + return FunctionType::get(getVoidTy(C), + {T_pjlvalue, T_pjlvalue, T_pjlvalue}, false); }, nullptr, }; @@ -2098,8 +2098,6 @@ static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg, ArrayRef ArgNames=None, unsigned nreq=0); static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1); -static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign, bool alloc); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa); static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i); static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const Twine &msg); @@ -3498,19 +3496,17 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s bool issetglobal, bool isreplaceglobal, bool isswapglobal, bool ismodifyglobal, bool issetglobalonce, const jl_cgval_t *modifyop, bool alloc) { - jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, alloc); + jl_binding_t *bnd = jl_get_module_binding(mod, sym, 1); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - if (bp == NULL) - return jl_cgval_t(); + Value *bp = julia_binding_gv(ctx, bnd); if (bpart) { jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (!jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + if (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL) { jl_value_t *ty = decode_restriction_value(pku); if (ty != nullptr) { const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; if (!ismodifyglobal) { - // TODO: use typeassert in jl_check_binding_wr too + // TODO: use typeassert in jl_check_binding_assign_value too emit_typecheck(ctx, rval, ty, "typeassert"); rval = update_julia_type(ctx, rval, ty); if (rval.typ == jl_bottom_type) @@ -3545,6 +3541,8 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s } Value *m = literal_pointer_val(ctx, (jl_value_t*)mod); Value *s = literal_pointer_val(ctx, (jl_value_t*)sym); + ctx.builder.CreateCall(prepare_call(jlcheckbpwritable_func), + { bp, m, s }); if (issetglobal) { ctx.builder.CreateCall(prepare_call(jlcheckassign_func), { bp, m, s, mark_callee_rooted(ctx, boxed(ctx, rval)) }); @@ -5991,85 +5989,6 @@ static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_datatyp ctx.builder.SetInsertPoint(ifok); } -// returns a jl_ppvalue_t location for the global variable m.s -// if the reference currently bound or assign == true, -// pbnd will also be assigned with the binding address -static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign, bool alloc) -{ - jl_binding_t *b = jl_get_module_binding(m, s, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition_all(b, ctx.min_world, ctx.max_world); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - if (assign) { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) - // not yet declared - b = NULL; - } - else { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - // try to look this up now - b = jl_get_binding(m, s); - bpart = jl_get_binding_partition_all(b, ctx.min_world, ctx.max_world); - } - pku = jl_walk_binding_inplace_all(&b, &bpart, ctx.min_world, ctx.max_world); - } - if (!b || !bpart) { - // var not found. switch to delayed lookup. - Constant *initnul = Constant::getNullValue(ctx.types().T_pjlvalue); - GlobalVariable *bindinggv = new GlobalVariable(*ctx.f->getParent(), ctx.types().T_pjlvalue, - false, GlobalVariable::PrivateLinkage, initnul, "jl_binding_ptr"); // LLVM has bugs with nameless globals - LoadInst *cachedval = ctx.builder.CreateAlignedLoad(ctx.types().T_pjlvalue, bindinggv, Align(sizeof(void*))); - setName(ctx.emission_context, cachedval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".cached"); - cachedval->setOrdering(AtomicOrdering::Unordered); - BasicBlock *have_val = BasicBlock::Create(ctx.builder.getContext(), "found"); - BasicBlock *not_found = BasicBlock::Create(ctx.builder.getContext(), "notfound"); - BasicBlock *currentbb = ctx.builder.GetInsertBlock(); - auto iscached = ctx.builder.CreateICmpNE(cachedval, initnul); - setName(ctx.emission_context, iscached, "iscached"); - ctx.builder.CreateCondBr(iscached, have_val, not_found); - not_found->insertInto(ctx.f); - ctx.builder.SetInsertPoint(not_found); - Value *bval = nullptr; - if (assign) { - bval = ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s), - ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc)}); - } else { - bval = ctx.builder.CreateCall(prepare_call(jlgetbindingorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s)}); - } - setName(ctx.emission_context, bval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".found"); - ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); - ctx.builder.CreateBr(have_val); - have_val->insertInto(ctx.f); - ctx.builder.SetInsertPoint(have_val); - PHINode *p = ctx.builder.CreatePHI(ctx.types().T_pjlvalue, 2); - p->addIncoming(cachedval, currentbb); - p->addIncoming(bval, not_found); - setName(ctx.emission_context, p, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s)); - return p; - } - if (assign) { - if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - // this will fail at runtime, so defer to the runtime to create the error - ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s), - ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc) }); - CreateTrap(ctx.builder); - return NULL; - } - } - else { - if (b->deprecated) - cg_bdw(ctx, s, b); - } - *pbnd = b; - return julia_binding_gv(ctx, b); -} - static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa) { LoadInst *v = ctx.builder.CreateAlignedLoad(ctx.types().T_prjlvalue, bp, Align(sizeof(void*))); @@ -10184,7 +10103,7 @@ static void init_jit_functions(void) add_named_global(jltypeerror_func, &jl_type_error); add_named_global(jlcheckassign_func, &jl_checked_assignment); add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error); - add_named_global(jlgetbindingwrorerror_func, &jl_get_binding_wr); + add_named_global(jlcheckbpwritable_func, &jl_check_binding_currently_writable); add_named_global(jlboundp_func, &jl_boundp); for (auto it : builtin_func_map()) add_named_global(it.second, it.first); diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c1b29a091511b..9e221420aa9f4 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -196,6 +196,7 @@ XX(jl_get_binding_for_method_def) \ XX(jl_get_binding_or_error) \ XX(jl_get_binding_wr) \ + XX(jl_check_binding_currently_writable) \ XX(jl_get_cpu_name) \ XX(jl_get_cpu_features) \ XX(jl_cpu_has_fma) \ diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 57f67755df692..97d76e7762a9e 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4626,6 +4626,8 @@ f(x) = yt(x) (if (globalref? lhs) (begin (emit `(global ,lhs)) + (if (null? (cadr lam)) + (emit `(latestworld))) (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) (emit `(= ,lhs ,rhs)))) (define (emit-assignment lhs rhs) @@ -4944,13 +4946,16 @@ f(x) = yt(x) #f)) ((global) ; keep global declarations as statements (if value (error "misplaced \"global\" declaration")) - (emit e)) + (emit e) + (if (null? (cadr lam)) + (emit `(latestworld)))) ((globaldecl) (if value (error "misplaced \"global\" declaration")) - (if (atom? (caddr e)) (emit e) + (if (atom? (caddr e)) (begin (emit e) (emit `(latestworld))) (let ((rr (make-ssavalue))) (emit `(= ,rr ,(caddr e))) - (emit `(globaldecl ,(cadr e) ,rr))))) + (emit `(globaldecl ,(cadr e) ,rr)) + (emit `(latestworld))))) ((local-def) #f) ((local) #f) ((moved-local) diff --git a/src/julia.h b/src/julia.h index f36133088119f..a5f56ad580335 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2001,7 +2001,8 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var); // get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); +JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s); +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import); JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); diff --git a/src/module.c b/src/module.c index f4c56e19efa61..2630db2dbfd94 100644 --- a/src/module.c +++ b/src/module.c @@ -279,38 +279,42 @@ extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) JL_GLOBALLY_ROOTED; -// get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc) +// Checks that the binding in general is currently writable, but does not perform any checks on the +// value to be written into the binding. +JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s) { - jl_binding_t *b = jl_get_module_binding(m, var, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); retry: if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_constant(decode_restriction_kind(pku))) { if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { if (decode_restriction_kind(pku) != BINDING_KIND_DECLARED) { - check_safe_newbinding(m, var); - if (!alloc) - jl_errorf("Global %s.%s does not exist and cannot be assigned.\n" - "Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).\n" - "Hint: Declare it using `global %s` inside `%s` before attempting assignment.", - jl_symbol_name(m->name), jl_symbol_name(var), - jl_symbol_name(var), jl_symbol_name(m->name)); + jl_errorf("Global %s.%s does not exist and cannot be assigned.\n" + "Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).\n" + "Hint: Declare it using `global %s` inside `%s` before attempting assignment.", + jl_symbol_name(m->name), jl_symbol_name(s), + jl_symbol_name(s), jl_symbol_name(m->name)); } jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)jl_any_type, BINDING_KIND_GLOBAL); if (!jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) goto retry; jl_gc_wb_knownold(bpart, jl_any_type); } else { - jl_module_t *from = jl_binding_dbgmodule(b, m, var); + jl_module_t *from = jl_binding_dbgmodule(b, m, s); if (from == m) jl_errorf("cannot assign a value to imported variable %s.%s", - jl_symbol_name(from->name), jl_symbol_name(var)); + jl_symbol_name(from->name), jl_symbol_name(s)); else jl_errorf("cannot assign a value to imported variable %s.%s from module %s", - jl_symbol_name(from->name), jl_symbol_name(var), jl_symbol_name(m->name)); + jl_symbol_name(from->name), jl_symbol_name(s), jl_symbol_name(m->name)); } } +} + +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) +{ + jl_binding_t *b = jl_get_module_binding(m, var, 1); + jl_check_binding_currently_writable(b, m, var); return b; } @@ -1066,7 +1070,7 @@ JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) { - jl_binding_t *bp = jl_get_binding_wr(m, var, 0); + jl_binding_t *bp = jl_get_binding_wr(m, var); jl_checked_assignment(bp, m, var, val); } @@ -1186,7 +1190,9 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b } } -jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, int reassign) +// For a generally writable binding (checked using jl_check_binding_currently_writable in this world age), check whether +// we can actually write the value `rhs` to it. +jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED) { JL_GC_PUSH1(&rhs); // callee-rooted jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); @@ -1219,7 +1225,7 @@ jl_value_t *jl_check_binding_wr(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs) { - if (jl_check_binding_wr(b, mod, var, rhs, 1) != NULL) { + if (jl_check_binding_assign_value(b, mod, var, rhs) != NULL) { jl_atomic_store_release(&b->value, rhs); jl_gc_wb(b, rhs); } @@ -1227,7 +1233,7 @@ JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sy JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs) { - jl_check_binding_wr(b, mod, var, rhs, 0); + jl_check_binding_assign_value(b, mod, var, rhs); jl_value_t *old = jl_atomic_exchange(&b->value, rhs); jl_gc_wb(b, rhs); if (__unlikely(old == NULL)) @@ -1237,7 +1243,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *expected, jl_value_t *rhs) { - jl_value_t *ty = jl_check_binding_wr(b, mod, var, rhs, 0); + jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs); return replace_value(ty, &b->value, (jl_value_t*)b, expected, rhs, 1, mod, var); } @@ -1256,7 +1262,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs ) { - jl_check_binding_wr(b, mod, var, rhs, 0); + jl_check_binding_assign_value(b, mod, var, rhs); jl_value_t *old = NULL; if (jl_atomic_cmpswap(&b->value, &old, rhs)) jl_gc_wb(b, rhs); diff --git a/src/toplevel.c b/src/toplevel.c index 30e78bf813fc8..0562be4a267b5 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -302,38 +302,6 @@ static jl_value_t *jl_eval_dot_expr(jl_module_t *m, jl_value_t *x, jl_value_t *f return args[0]; } -void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty) -{ - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - jl_ptr_kind_union_t new_pku = encode_restriction(ty, BINDING_KIND_GLOBAL); - while (1) { - if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) - break; - continue; - } else { - jl_errorf("cannot set type for imported global %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(sym)); - } - } - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { - jl_errorf("cannot set type for imported constant %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(sym)); - } - jl_value_t *old_ty = decode_restriction_value(pku); - JL_GC_PROMISE_ROOTED(old_ty); - if (!jl_types_equal(ty, old_ty)) { - jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", - jl_symbol_name(mod->name), jl_symbol_name(sym)); - } - if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) - break; - } - jl_gc_wb(bpart, ty); -} - extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var); void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { // create uninitialized mutable binding for "global x" decl sometimes or probably @@ -349,17 +317,49 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { gm = m; gs = (jl_sym_t*)arg; } + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; jl_binding_t *b = jl_get_module_binding(gm, gs, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); - while (decode_restriction_kind(pku) == BINDING_KIND_GUARD || decode_restriction_kind(pku) == BINDING_KIND_FAILED) { - check_safe_newbinding(gm, gs); - if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(NULL, BINDING_KIND_DECLARED))) - break; - } - if (set_type) { - jl_binding_set_type(b, gm, gs, set_type); + jl_binding_partition_t *bpart = NULL; + jl_ptr_kind_union_t new_pku = encode_restriction(set_type, set_type == NULL ? BINDING_KIND_DECLARED : BINDING_KIND_GLOBAL); + while (1) { + bpart = jl_get_binding_partition(b, new_world); + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { + if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (decode_restriction_kind(pku) == BINDING_KIND_DECLARED && !set_type) + goto done; + check_safe_newbinding(gm, gs); + if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) { + break; + } + continue; + } else if (set_type) { + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + jl_errorf("cannot set type for imported constant %s.%s.", + jl_symbol_name(gm->name), jl_symbol_name(gs)); + } else { + jl_errorf("cannot set type for imported global %s.%s.", + jl_symbol_name(gm->name), jl_symbol_name(gs)); + } + } + } + if (!set_type) + goto done; + jl_value_t *old_ty = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(old_ty); + if (!jl_types_equal(set_type, old_ty)) { + jl_errorf("cannot set type for global %s.%s. It already has a value or is already set to a different type.", + jl_symbol_name(gm->name), jl_symbol_name(gs)); + } + goto done; } + if (set_type) + jl_gc_wb(bpart, set_type); + bpart->min_world = new_world; + jl_atomic_store_release(&jl_world_counter, new_world); +done: + JL_UNLOCK(&world_counter_lock); } void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) @@ -751,7 +751,8 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); int did_warn = 0; while (1) { - if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (jl_bkind_is_some_constant(kind)) { if (!val) { break; } @@ -789,9 +790,20 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( continue; } jl_gc_wb(bpart, val); - int needs_backdate = bpart->min_world == 0 && new_world && val; + size_t prev_min_world = bpart->min_world; bpart->min_world = new_world; - if (needs_backdate) { + int need_backdate = 0; + if (new_world && val) { + if (prev_min_world == 0) { + need_backdate = 1; + } else if (kind == BINDING_KIND_DECLARED) { + jl_binding_partition_t *prev_bpart = jl_get_binding_partition(b, new_world-1); + jl_ptr_kind_union_t prev_pku = jl_atomic_load_relaxed(&prev_bpart->restriction); + if (prev_bpart->min_world == 0 && decode_restriction_kind(prev_pku) == BINDING_KIND_GUARD) + need_backdate = 1; + } + } + if (need_backdate) { jl_declare_constant_val3(b, mod, var, val, BINDING_KIND_BACKDATED_CONST, 0); } } @@ -1094,9 +1106,8 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val else { // use interpreter assert(thk); - if (has_opaque) { + if (has_opaque) jl_resolve_definition_effects_in_ir((jl_array_t*)thk->code, m, NULL, 0); - } size_t world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = world; result = jl_interpret_toplevel_thunk(m, thk); diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index f83fa867748af..699024c1723a4 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -44,8 +44,8 @@ function UndefVarError_hint(io::IO, ex::UndefVarError) "with the module it should come from.") elseif kind === Base.BINDING_KIND_GUARD print(io, "\nSuggestion: check for spelling errors or missing imports.") - else - print(io, "\nSuggestion: this global was defined as `$(bpart.restriction.globalref)` but not assigned a value.") + elseif Base.is_some_imported(kind) + print(io, "\nSuggestion: this global was defined as `$(Base.partition_restriction(bpart).globalref)` but not assigned a value.") end elseif scope === :static_parameter print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.") @@ -1884,16 +1884,26 @@ function get_usings!(usings, ex) return usings end +function create_global_out!(mod) + if !isdefinedglobal(mod, :Out) + out = Dict{Int, Any}() + @eval mod begin + const Out = $(out) + export Out + end + return out + end + return getglobal(mod, Out) +end + function capture_result(n::Ref{Int}, @nospecialize(x)) n = n[] mod = Base.MainInclude - if !isdefined(mod, :Out) - @eval mod global Out - @eval mod export Out - setglobal!(mod, :Out, Dict{Int, Any}()) - end - if x !== getglobal(mod, :Out) && x !== nothing # remove this? - getglobal(mod, :Out)[n] = x + # TODO: This invokelatest is only required due to backdated constants + # and should be removed after + out = isdefinedglobal(mod, :Out) ? invokelatest(getglobal, mod, :Out) : invokelatest(create_global_out!, mod) + if x !== out && x !== nothing # remove this? + out[n] = x end nothing end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 8944fd76f31de..018cf9c36430e 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -752,11 +752,11 @@ fake_repl() do stdin_write, stdout_read, repl # Test removal of prefix in single statement paste sendrepl2("\e[200~julia> A = 2\e[201~\n") - @test Main.A == 2 + @test @world(Main.A, ∞) == 2 # Test removal of prefix in single statement paste sendrepl2("\e[200~In [12]: A = 2.2\e[201~\n") - @test Main.A == 2.2 + @test @world(Main.A, ∞) == 2.2 # Test removal of prefix in multiple statement paste sendrepl2("""\e[200~ @@ -768,7 +768,7 @@ fake_repl() do stdin_write, stdout_read, repl julia> A = 3\e[201~ """) - @test Main.A == 3 + @test @world(Main.A, ∞) == 3 @test @invokelatest(Main.foo(4)) @test @invokelatest(Main.T17599(3)).a == 3 @test !@invokelatest(Main.foo(2)) @@ -780,12 +780,12 @@ fake_repl() do stdin_write, stdout_read, repl julia> A = 4 4\e[201~ """) - @test Main.A == 4 + @test @world(Main.A, ∞) == 4 @test @invokelatest(Main.goo(4)) == 5 # Test prefix removal only active in bracket paste mode sendrepl2("julia = 4\n julia> 3 && (A = 1)\n") - @test Main.A == 1 + @test @world(Main.A, ∞) == 1 # Test that indentation corresponding to the prompt is removed s = sendrepl2("""\e[200~julia> begin\n α=1\n β=2\n end\n\e[201~""") @@ -820,8 +820,8 @@ fake_repl() do stdin_write, stdout_read, repl julia> B = 2 2\e[201~ """) - @test Main.A == 1 - @test Main.B == 2 + @test @world(Main.A, ∞) == 1 + @test @world(Main.B, ∞) == 2 end # redirect_stdout # Close repl diff --git a/sysimage.mk b/sysimage.mk index 571e2da003346..ae6ce8699f417 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -39,6 +39,7 @@ COMPILER_SRCS := $(addprefix $(JULIAHOME)/, \ base/error.jl \ base/essentials.jl \ base/expr.jl \ + base/exports.jl \ base/generator.jl \ base/int.jl \ base/indices.jl \ diff --git a/test/core.jl b/test/core.jl index 3886e6728df10..5e2677f0f075f 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8233,6 +8233,7 @@ end let M = @__MODULE__ Core.eval(M, :(global a_typed_global)) @test Core.eval(M, :(global a_typed_global::$(Tuple{Union{Integer,Nothing}}))) === nothing + @Core.latestworld @test Core.get_binding_type(M, :a_typed_global) === Tuple{Union{Integer,Nothing}} @test Core.eval(M, :(global a_typed_global::$(Tuple{Union{Integer,Nothing}}))) === nothing @test Core.eval(M, :(global a_typed_global::$(Union{Tuple{Integer},Tuple{Nothing}}))) === nothing diff --git a/test/precompile.jl b/test/precompile.jl index f5a412b416ddc..a9516231ff8d7 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1969,7 +1969,7 @@ precompile_test_harness("Issue #50538") do load_path """ module I50538 const newglobal = try - Base.newglobal = false + eval(Expr(:global, GlobalRef(Base, :newglobal))) catch ex ex isa ErrorException || rethrow() ex diff --git a/test/worlds.jl b/test/worlds.jl index 025aaba6cea4f..48fb6593d3a37 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -107,7 +107,7 @@ end g265() = [f265(x) for x in 1:3.] wc265 = get_world_counter() wc265_41332a = Task(tls_world_age) -@test tls_world_age() == wc265 +@test tls_world_age() == wc265 + 2 (function () global wc265_41332b = Task(tls_world_age) @eval f265(::Any) = 1.0 @@ -115,24 +115,24 @@ wc265_41332a = Task(tls_world_age) global wc265_41332d = Task(tls_world_age) nothing end)() -@test wc265 + 4 == get_world_counter() == tls_world_age() +@test wc265 + 10 == get_world_counter() == tls_world_age() schedule(wc265_41332a) schedule(wc265_41332b) schedule(wc265_41332c) schedule(wc265_41332d) -@test wc265 == fetch(wc265_41332a) -@test wc265 + 2 == fetch(wc265_41332b) -@test wc265 + 4 == fetch(wc265_41332c) -@test wc265 + 2 == fetch(wc265_41332d) +@test wc265 + 1 == fetch(wc265_41332a) +@test wc265 + 8 == fetch(wc265_41332b) +@test wc265 + 10 == fetch(wc265_41332c) +@test wc265 + 8 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] wc265 = get_world_counter() @test put_n_take!(get_world_counter, ()) == wc265 -@test put_n_take!(tls_world_age, ()) == wc265 +@test put_n_take!(tls_world_age, ()) + 3 == wc265 f265(::Int) = 1 @test put_n_take!(get_world_counter, ()) == wc265 + 1 == get_world_counter() == tls_world_age() -@test put_n_take!(tls_world_age, ()) == wc265 +@test put_n_take!(tls_world_age, ()) + 3 == wc265 @test g265() == Int[1, 1, 1] @test Core.Compiler.return_type(f265, Tuple{Any,}) == Union{Float64, Int} @@ -162,12 +162,12 @@ let ex = t265.exception @test ex isa MethodError @test ex.f == h265 @test ex.args == () - @test ex.world == wc265 + @test ex.world == wc265-3 str = sprint(showerror, ex) wc = get_world_counter() cmps = """ MethodError: no method matching h265() - The applicable method may be too new: running in world age $wc265, while current world is $wc.""" + The applicable method may be too new: running in world age $(wc265-3), while current world is $wc.""" @test startswith(str, cmps) cmps = "\n h265() (method too new to be called from this world context.)\n $loc_h265" @test occursin(cmps, str) @@ -500,3 +500,12 @@ end end @test_throws ErrorException("Generated function result with `edges == nothing` and `max_world == typemax(UInt)` must have `min_world == 1`") generated_no_edges() + +# Test that backdating of constants is working for structs +before_backdate_age = Base.tls_world_age() +struct FooBackdated + x::Vector{FooBackdated} + + FooBackdated() = new(FooBackdated[]) +end +@test Base.invoke_in_world(before_backdate_age, isdefined, @__MODULE__, :FooBackdated)