From 1c34623ada74c7430c77075cd48a917af25134ab Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 20 Jan 2025 02:21:22 +0000 Subject: [PATCH 1/5] binding partition: Start enforcing minimum world age for constant bindings Currently, even though the binding partition system is implemented, it is largely enabled. New `const` definitions get magically "backdated" to the first world age in which the binding was undefined. Additionally, they do not get their own world age and there is currently no `latestworld` marker after `const` definitions. This PR changes this situation to give const markers their own world age with appropriate `latestworld` increments. Both of these are mandatory for `const` replacement to work. The other thing this PR does is to remove the automatic "backdating". To see the difference, consider: ``` function foo($i) Core.eval(:(const x = $i)) x end ``` Without an intervening world age increment, this will throw an UndefVarError on this PR. I believe this is the best option for two reasons: 1. It will allow us infer these to `Union{}` in the future (thus letting inference prune dead code faster). 2. I think it is less confusing in terms of the world age semantics for `const` definitions to become active only after they are defined. To illustrate the second point, suppose we did keep the automatic backdating. Then we would have: ``` foo(1) => 1 foo(2) => 1 foo(3) => 2 ``` as opposed to on this PR: ``` foo(1) => UndefVarError foo(2) => 1 foo(3) => 2 ``` The semantics are consistent, of course, but I am concerned that an automatic backdating will give users the wrong mental model about world age, since it "fixes itself" the first time, but if you Revise it, it will give an unexpected answer. I think this would encourage accidentally bad code patterns that only break under Revise (where they are hard to debug). The counterpoint of course is that that not backdating is a more breaking choice. As with the rest of the 1.12-era world age semantics changes, I think taking a look at PkgEval will be helpful. --- Compiler/src/abstractinterpretation.jl | 11 ++--- Compiler/src/optimize.jl | 2 +- Compiler/src/ssair/ir.jl | 4 +- Compiler/src/ssair/legacy.jl | 2 +- Compiler/src/ssair/verify.jl | 11 +++-- Compiler/src/tfuncs.jl | 2 +- Compiler/test/inline.jl | 4 +- Compiler/test/invalidation.jl | 2 +- base/Base.jl | 1 + base/client.jl | 12 ++--- base/essentials.jl | 6 +++ base/loading.jl | 22 +++++---- base/logging/logging.jl | 4 +- base/reflection.jl | 62 +++++++++++++++++++++++--- base/runtime_internals.jl | 16 +++++-- src/codegen.cpp | 34 +++++++++----- src/gf.c | 21 ++++++--- src/init.c | 13 ++++-- src/jlapi.c | 11 +++-- src/julia-syntax.scm | 14 ++++-- src/julia.h | 1 - src/julia_internal.h | 6 ++- src/method.c | 28 ++++++++---- src/module.c | 43 +++++++++--------- src/staticdata.c | 15 +++++-- src/toplevel.c | 35 ++++++++++++--- stdlib/REPL/src/REPLCompletions.jl | 16 ++++--- stdlib/REPL/src/precompile.jl | 1 + stdlib/REPL/test/precompilation.jl | 2 +- stdlib/REPL/test/repl.jl | 8 ++-- stdlib/Serialization/test/runtests.jl | 16 +++---- test/core.jl | 2 +- test/loading.jl | 1 + test/precompile.jl | 16 ++++--- test/runtests.jl | 6 +-- test/syntax.jl | 1 + test/worlds.jl | 8 ++-- 37 files changed, 312 insertions(+), 147 deletions(-) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 59510dbfbb65a..aad388b0a9dbb 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3454,10 +3454,10 @@ world_range(ir::IRCode) = ir.valid_worlds world_range(ci::CodeInfo) = WorldRange(ci.min_world, ci.max_world) world_range(compact::IncrementalCompact) = world_range(compact.ir) -function force_binding_resolution!(g::GlobalRef) +function force_binding_resolution!(g::GlobalRef, world::UInt) # Force resolution of the binding # TODO: This will go away once we switch over to fully partitioned semantics - ccall(:jl_globalref_boundp, Cint, (Any,), g) + ccall(:jl_force_binding_resolution, Cvoid, (Any, Csize_t), g, world) return nothing end @@ -3475,7 +3475,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, # This method is surprisingly hot. For performance, don't ask the runtime to resolve # the binding unless necessary - doing so triggers an additional lookup, which though # not super expensive is hot enough to show up in benchmarks. - force_binding_resolution!(g) + force_binding_resolution!(g, min_world(worlds)) return abstract_eval_globalref_type(g, src, false) end # return Union{} @@ -3488,7 +3488,7 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, end function lookup_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) - force_binding_resolution!(g) + force_binding_resolution!(g, get_inference_world(interp)) partition = lookup_binding_partition(get_inference_world(interp), g) update_valid_age!(sv, WorldRange(partition.min_world, partition.max_world)) partition @@ -3537,7 +3537,8 @@ function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, saw_ partition = abstract_eval_binding_partition!(interp, g, sv) ret = abstract_eval_partition_load(interp, partition) if ret.rt !== Union{} && ret.exct === UndefVarError && InferenceParams(interp).assume_bindings_static - if isdefined(g, :binding) && isdefined(g.binding, :value) + b = convert(Core.Binding, g) + if isdefined(b, :value) ret = RTEffects(ret.rt, Union{}, Effects(generic_getglobal_effects, nothrow=true)) end # We do not assume in general that assigned global bindings remain assigned. diff --git a/Compiler/src/optimize.jl b/Compiler/src/optimize.jl index 12b2f3c9a269f..9f74f028507cd 100644 --- a/Compiler/src/optimize.jl +++ b/Compiler/src/optimize.jl @@ -1286,7 +1286,7 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) # types of call arguments only once `slot2reg` converts this `IRCode` to the SSA form # and eliminates slots (see below) argtypes = sv.slottypes - return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, WorldRange(ci.min_world, ci.max_world)) + return IRCode(stmts, sv.cfg, di, argtypes, meta, sv.sptypes, world_range(ci)) end function process_meta!(meta::Vector{Expr}, @nospecialize stmt) diff --git a/Compiler/src/ssair/ir.jl b/Compiler/src/ssair/ir.jl index 9103dba04fa54..f86ada2309ddc 100644 --- a/Compiler/src/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -434,7 +434,7 @@ struct IRCode function IRCode(stmts::InstructionStream, cfg::CFG, debuginfo::DebugInfoStream, argtypes::Vector{Any}, meta::Vector{Expr}, sptypes::Vector{VarState}, valid_worlds=WorldRange(typemin(UInt), typemax(UInt))) - return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta) + return new(stmts, argtypes, sptypes, debuginfo, cfg, NewNodeStream(), meta, valid_worlds) end function IRCode(ir::IRCode, stmts::InstructionStream, cfg::CFG, new_nodes::NewNodeStream) di = ir.debuginfo @@ -1462,7 +1462,7 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr result[result_idx][:stmt] = GotoNode(label) result_idx += 1 elseif isa(stmt, GlobalRef) - total_flags = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE + total_flags = IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW flag = result[result_idx][:flag] if has_flag(flag, total_flags) ssa_rename[idx] = stmt diff --git a/Compiler/src/ssair/legacy.jl b/Compiler/src/ssair/legacy.jl index 675ca2dea9b32..64a39c72ad9eb 100644 --- a/Compiler/src/ssair/legacy.jl +++ b/Compiler/src/ssair/legacy.jl @@ -44,7 +44,7 @@ function inflate_ir!(ci::CodeInfo, sptypes::Vector{VarState}, argtypes::Vector{A di = DebugInfoStream(nothing, ci.debuginfo, nstmts) stmts = InstructionStream(code, ssavaluetypes, info, di.codelocs, ci.ssaflags) meta = Expr[] - return IRCode(stmts, cfg, di, argtypes, meta, sptypes, WorldRange(ci.min_world, ci.max_world)) + return IRCode(stmts, cfg, di, argtypes, meta, sptypes, world_range(ci)) end """ diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 12eb09be693f3..6fb26fc0e2ccf 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -12,7 +12,7 @@ end if !isdefined(@__MODULE__, Symbol("@verify_error")) macro verify_error(arg) - arg isa String && return esc(:(print && println(stderr, $arg))) + arg isa String && return esc(:(print && println($(GlobalRef(Core, :stderr)), $arg))) isexpr(arg, :string) || error("verify_error macro expected a string expression") pushfirst!(arg.args, GlobalRef(Core, :stderr)) pushfirst!(arg.args, :println) @@ -61,8 +61,13 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, raise_error() end elseif isa(op, GlobalRef) - if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name) - @verify_error "Unbound GlobalRef not allowed in value position" + bpart = lookup_binding_partition(min_world(ir.valid_worlds), op) + while is_some_imported(binding_kind(bpart)) && max_world(ir.valid_worlds) <= bpart.max_world + imported_binding = partition_restriction(bpart)::Core.Binding + bpart = lookup_binding_partition(min_world(ir.valid_worlds), imported_binding) + end + if !is_defined_const_binding(binding_kind(bpart)) || (bpart.max_world < max_world(ir.valid_worlds)) + @verify_error "Unbound or partitioned GlobalRef not allowed in value position" raise_error() end elseif isa(op, Expr) diff --git a/Compiler/src/tfuncs.jl b/Compiler/src/tfuncs.jl index cfb865b06e9e5..a0bdd5c1330bc 100644 --- a/Compiler/src/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -1109,7 +1109,7 @@ function _getfield_tfunc_const(@nospecialize(sv), name::Const) if isa(sv, DataType) && nv == DATATYPE_TYPES_FIELDINDEX && isdefined(sv, nv) return Const(getfield(sv, nv)) end - if isconst(typeof(sv), nv) + if !isa(sv, Module) && isconst(typeof(sv), nv) if isdefined(sv, nv) return Const(getfield(sv, nv)) end diff --git a/Compiler/test/inline.jl b/Compiler/test/inline.jl index 5f95fb761859e..b8ff14405391d 100644 --- a/Compiler/test/inline.jl +++ b/Compiler/test/inline.jl @@ -149,7 +149,7 @@ end (src, _) = only(code_typed(sum27403, Tuple{Vector{Int}})) @test !any(src.code) do x - x isa Expr && x.head === :invoke && x.args[2] !== Core.GlobalRef(Base, :throw_boundserror) + x isa Expr && x.head === :invoke && !(x.args[2] in (Core.GlobalRef(Base, :throw_boundserror), Base.throw_boundserror)) end end @@ -313,7 +313,7 @@ end const _a_global_array = [1] f_inline_global_getindex() = _a_global_array[1] let ci = code_typed(f_inline_global_getindex, Tuple{})[1].first - @test any(x->(isexpr(x, :call) && x.args[1] === GlobalRef(Base, :memoryrefget)), ci.code) + @test any(x->(isexpr(x, :call) && x.args[1] in (GlobalRef(Base, :memoryrefget), Base.memoryrefget)), ci.code) end # Issue #29114 & #36087 - Inlining of non-tuple splats diff --git a/Compiler/test/invalidation.jl b/Compiler/test/invalidation.jl index 2642c1647a682..b77c7677e6987 100644 --- a/Compiler/test/invalidation.jl +++ b/Compiler/test/invalidation.jl @@ -55,7 +55,7 @@ let mi = Base.method_instance(basic_caller, (Float64,)) end # this redefinition below should invalidate the cache -const BASIC_CALLER_WORLD = Base.get_world_counter() +const BASIC_CALLER_WORLD = Base.get_world_counter()+1 basic_callee(x) = x, x @test !isdefined(Base.method_instance(basic_callee, (Float64,)), :cache) let mi = Base.method_instance(basic_caller, (Float64,)) diff --git a/base/Base.jl b/base/Base.jl index 20b1636c29a8d..04f732a4309c9 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -401,6 +401,7 @@ end # we know whether the .ji can just give the Base copy or not. # TODO: We may want to do this earlier to avoid TOCTOU issues. const _compiler_require_dependencies = Any[] +@Core.latestworld for i = 1:length(_included_files) isassigned(_included_files, i) || continue (mod, file) = _included_files[i] diff --git a/base/client.jl b/base/client.jl index e95d518d3e501..2527d382c695d 100644 --- a/base/client.jl +++ b/base/client.jl @@ -299,7 +299,7 @@ function exec_options(opts) elseif cmd == 'm' entrypoint = push!(split(arg, "."), "main") Base.eval(Main, Expr(:import, Expr(:., Symbol.(entrypoint)...))) - if !should_use_main_entrypoint() + if !invokelatest(should_use_main_entrypoint) error("`main` in `$arg` not declared as entry point (use `@main` to do so)") end return false @@ -408,8 +408,7 @@ function load_InteractiveUtils(mod::Module=Main) return nothing end end - Core.eval(mod, :(using Base.MainInclude.InteractiveUtils)) - return MainInclude.InteractiveUtils + return Core.eval(mod, :(using Base.MainInclude.InteractiveUtils; Base.MainInclude.InteractiveUtils)) end function load_REPL() @@ -556,11 +555,12 @@ function _start() local ret = 0 try repl_was_requested = exec_options(JLOptions()) - if should_use_main_entrypoint() && !is_interactive + if invokelatest(should_use_main_entrypoint) && !is_interactive + main = invokelatest(getglobal, Main, :main) if Base.generating_output() - precompile(Main.main, (typeof(ARGS),)) + precompile(main, (typeof(ARGS),)) else - ret = invokelatest(Main.main, ARGS) + ret = invokelatest(main, ARGS) end elseif (repl_was_requested || is_interactive) # Run the Base `main`, which will either load the REPL stdlib diff --git a/base/essentials.jl b/base/essentials.jl index fa5cf79192f56..58e4ce1125093 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -1266,6 +1266,9 @@ arbitrary code in fixed worlds. `world` may be `UnitRange`, in which case the ma will error unless the binding is valid and has the same value across the entire world range. +As a special case, the world `∞` always refers to the latest world, even if that world +is newer than the world currently running. + The `@world` macro is primarily used in the printing of bindings that are no longer available in the current world. @@ -1290,6 +1293,9 @@ julia> fold This functionality requires at least Julia 1.12. """ macro world(sym, world) + if world == :∞ + world = Expr(:call, get_world_counter) + end if isa(sym, Symbol) return :($(_resolve_in_world)($(esc(world)), $(QuoteNode(GlobalRef(__module__, sym))))) elseif isa(sym, GlobalRef) diff --git a/base/loading.jl b/base/loading.jl index 4193aae13b96a..240406292246b 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2364,6 +2364,18 @@ function require(into::Module, mod::Symbol) return invoke_in_world(world, __require, into, mod) end +function check_for_hint(into, mod) + return begin + if isdefined(into, mod) && getfield(into, mod) isa Module + true, "." + elseif isdefined(parentmodule(into), mod) && getfield(parentmodule(into), mod) isa Module + true, ".." + else + false, "" + end + end +end + function __require(into::Module, mod::Symbol) if into === __toplevel__ && generating_output(#=incremental=#true) error("`using/import $mod` outside of a Module detected. Importing a package outside of a module \ @@ -2377,15 +2389,7 @@ function __require(into::Module, mod::Symbol) if uuidkey_env === nothing where = PkgId(into) if where.uuid === nothing - hint, dots = begin - if isdefined(into, mod) && getfield(into, mod) isa Module - true, "." - elseif isdefined(parentmodule(into), mod) && getfield(parentmodule(into), mod) isa Module - true, ".." - else - false, "" - end - end + hint, dots = invokelatest(check_for_hint, into, mod) hint_message = hint ? ", maybe you meant `import/using $(dots)$(mod)`" : "" install_message = if mod != :Pkg start_sentence = hint ? "Otherwise, run" : "Run" diff --git a/base/logging/logging.jl b/base/logging/logging.jl index 41721e84cc934..a1a8417bcb388 100644 --- a/base/logging/logging.jl +++ b/base/logging/logging.jl @@ -372,7 +372,7 @@ function logmsg_code(_module, file, line, level, message, exs...) kwargs = (;$(log_data.kwargs...)) true else - @invokelatest logging_error(logger, level, _module, group, id, file, line, err, false) + @invokelatest $(logging_error)(logger, level, _module, group, id, file, line, err, false) false end end @@ -384,7 +384,7 @@ function logmsg_code(_module, file, line, level, message, exs...) kwargs = (;$(log_data.kwargs...)) true catch err - @invokelatest logging_error(logger, level, _module, group, id, file, line, err, true) + @invokelatest $(logging_error)(logger, level, _module, group, id, file, line, err, true) false end end diff --git a/base/reflection.jl b/base/reflection.jl index f9c5dd9765533..c23228fa617e9 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1362,6 +1362,18 @@ macro invoke(ex) return esc(out) end +apply_gr(gr::GlobalRef, @nospecialize args...) = getglobal(gr.mod, gr.name)(args...) +apply_gr_kw(@nospecialize(kwargs::NamedTuple), gr::GlobalRef, @nospecialize args...) = Core.kwcall(kwargs, getglobal(gr.mod, gr.name), args...) + +function invokelatest_gr(gr::GlobalRef, @nospecialize args...; kwargs...) + @inline + kwargs = merge(NamedTuple(), kwargs) + if isempty(kwargs) + return Core._call_latest(apply_gr, gr, args...) + end + return Core._call_latest(apply_gr_kw, kwargs, gr, args...) +end + """ @invokelatest f(args...; kwargs...) @@ -1375,6 +1387,12 @@ It also supports the following syntax: - `@invokelatest xs[i]` expands to `Base.invokelatest(getindex, xs, i)` - `@invokelatest xs[i] = v` expands to `Base.invokelatest(setindex!, xs, v, i)` +!!! note + If `f` is a global, it will be resolved consistently + in the (latest) world as the call target. However, all other arguments + (as well as `f` itself if it is not a literal global) will be evaluated + in the current world age. + ```jldoctest julia> @macroexpand @invokelatest f(x; kw=kwv) :(Base.invokelatest(f, x; kw = kwv)) @@ -1404,11 +1422,45 @@ julia> @macroexpand @invokelatest xs[i] = v macro invokelatest(ex) topmod = _topmod(__module__) f, args, kwargs = destructure_callex(topmod, ex) - out = Expr(:call, GlobalRef(Base, :invokelatest)) - isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) - push!(out.args, f) - append!(out.args, args) - return esc(out) + + if !isa(f, GlobalRef) + out_f = Expr(:call, GlobalRef(Base, :invokelatest)) + isempty(kwargs) || push!(out_f.args, Expr(:parameters, kwargs...)) + + if isexpr(f, :(.)) + s = gensym() + check = quote + $s = $(f.args[1]) + isa($s, Module) + end + push!(out_f.args, Expr(:(.), s, f.args[2])) + else + push!(out_f.args, f) + end + append!(out_f.args, args) + + if @isdefined(s) + f = :(GlobalRef($s, $(f.args[2]))) + elseif !isa(f, Symbol) + return esc(out_f) + else + check = :($(Expr(:isglobal, f))) + end + end + + out_gr = Expr(:call, GlobalRef(Base, :invokelatest_gr)) + isempty(kwargs) || push!(out_gr.args, Expr(:parameters, kwargs...)) + push!(out_gr.args, isa(f, GlobalRef) ? QuoteNode(f) : + isa(f, Symbol) ? QuoteNode(GlobalRef(__module__, f)) : + f) + append!(out_gr.args, args) + + if isa(f, GlobalRef) + return esc(out_gr) + end + + # f::Symbol + return esc(:($check ? $out_gr : $out_f)) end function destructure_callex(topmod::Module, @nospecialize(ex)) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 67694e533ac47..964e8063dd5af 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -239,12 +239,16 @@ function lookup_binding_partition(world::UInt, b::Core.Binding) ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) end -function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) +function convert(::Type{Core.Binding}, gr::Core.GlobalRef) if isdefined(gr, :binding) - b = gr.binding + return gr.binding else - b = ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), gr.mod, gr.name, true) + return ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), gr.mod, gr.name, true) end +end + +function lookup_binding_partition(world::UInt, gr::Core.GlobalRef) + b = convert(Core.Binding, gr) return lookup_binding_partition(world, b) end @@ -420,7 +424,11 @@ end """ isconst(t::DataType, s::Union{Int,Symbol}) -> Bool -Determine whether a field `s` is declared `const` in a given type `t`. +Determine whether a field `s` is const in a given type `t` +in the sense that a read from said field is consistent +for egal objects. Note in particular that out-of-bounds +fields are considered const under this definition (because +they always throw). """ function isconst(@nospecialize(t::Type), s::Symbol) @_foldable_meta diff --git a/src/codegen.cpp b/src/codegen.cpp index eefc675bdd665..29b2c9b10b23c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3136,8 +3136,11 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) { if (jl_is_symbol(ex)) { jl_sym_t *sym = (jl_sym_t*)ex; - if (jl_is_const(ctx.module, sym)) - return jl_get_global(ctx.module, sym); + jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return decode_restriction_value(pku); return NULL; } if (jl_is_slotnumber(ex) || jl_is_argument(ex)) @@ -3158,11 +3161,15 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) jl_sym_t *s = NULL; if (jl_is_globalref(ex)) { s = jl_globalref_name(ex); - jl_binding_t *b = jl_get_binding(jl_globalref_mod(ex), s); - jl_value_t *v = jl_get_binding_value_if_const(b); + jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + jl_value_t *v = NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + v = decode_restriction_value(pku); if (v) { - if (b->deprecated) - cg_bdw(ctx, s, b); + if (bnd->deprecated) + cg_bdw(ctx, s, bnd); return v; } return NULL; @@ -3181,11 +3188,15 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) // Assumes that the module is rooted somewhere. s = (jl_sym_t*)static_eval(ctx, jl_exprarg(e, 2)); if (s && jl_is_symbol(s)) { - jl_binding_t *b = jl_get_binding(m, s); - jl_value_t *v = jl_get_binding_value_if_const(b); + jl_binding_t *bnd = jl_get_module_binding(m, s, 0); + jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + jl_value_t *v = NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + v = decode_restriction_value(pku); if (v) { - if (b->deprecated) - cg_bdw(ctx, s, b); + if (bnd->deprecated) + cg_bdw(ctx, s, bnd); return v; } } @@ -3465,14 +3476,13 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * return mark_julia_const(ctx, constval); } } - if (!bpart) { + if (!bpart || decode_restriction_kind(pku) != BINDING_KIND_GLOBAL) { return emit_globalref_runtime(ctx, bnd, mod, name); } Value *bp = julia_binding_gv(ctx, bnd); if (bnd->deprecated) { cg_bdw(ctx, name, bnd); } - assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL); jl_value_t *ty = decode_restriction_value(pku); bp = julia_binding_pvalue(ctx, bp); if (ty == nullptr) diff --git a/src/gf.c b/src/gf.c index ba28edfbeeff7..356ff135cefa3 100644 --- a/src/gf.c +++ b/src/gf.c @@ -292,7 +292,8 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a { jl_sym_t *sname = jl_symbol(name); if (dt == NULL) { - jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type); + // Builtins are specially considered available from world 0 + jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type, 0); jl_set_const(jl_core_module, sname, f); dt = (jl_datatype_t*)jl_typeof(f); } @@ -3790,10 +3791,8 @@ jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, jl_value return _jl_invoke(gf, args, nargs - 1, mfunc, world); } -// Return value is rooted globally -jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st) +jl_sym_t *jl_gf_supertype_name(jl_sym_t *name) { - // type name is function name prefixed with # size_t l = strlen(jl_symbol_name(name)); char *prefixed; prefixed = (char*)malloc_s(l+2); @@ -3801,6 +3800,14 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ strcpy(&prefixed[1], jl_symbol_name(name)); jl_sym_t *tname = jl_symbol(prefixed); free(prefixed); + return tname; +} + +// Return value is rooted globally +jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, size_t new_world) +{ + // type name is function name prefixed with # + jl_sym_t *tname = jl_gf_supertype_name(name); jl_datatype_t *ftype = (jl_datatype_t*)jl_new_datatype( tname, module, st, jl_emptysvec, jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); @@ -3808,7 +3815,7 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ JL_GC_PUSH1(&ftype); ftype->name->mt->name = name; jl_gc_wb(ftype->name->mt, name); - jl_set_const(module, tname, (jl_value_t*)ftype); + jl_declare_constant_val3(NULL, module, tname, (jl_value_t*)ftype, BINDING_KIND_CONST, new_world); jl_value_t *f = jl_new_struct(ftype); ftype->instance = f; jl_gc_wb(ftype, f); @@ -3816,9 +3823,9 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ return (jl_function_t*)f; } -jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module) +jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module, size_t new_world) { - return jl_new_generic_function_with_supertype(name, module, jl_function_type); + return jl_new_generic_function_with_supertype(name, module, jl_function_type, new_world); } struct ml_matches_env { diff --git a/src/init.c b/src/init.c index 7b41e63e98455..e69467c75bd73 100644 --- a/src/init.c +++ b/src/init.c @@ -249,6 +249,8 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER } if (jl_base_module) { + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit")); if (f != NULL) { jl_value_t **fargs; @@ -257,10 +259,7 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER fargs[1] = jl_box_int32(exitcode); JL_TRY { assert(ct); - size_t last_age = ct->world_age; - ct->world_age = jl_get_world_counter(); jl_apply(fargs, 2); - ct->world_age = last_age; } JL_CATCH { jl_printf((JL_STREAM*)STDERR_FILENO, "\natexit hook threw an error: "); @@ -270,10 +269,15 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER } JL_GC_POP(); } + ct->world_age = last_age; } - if (ct && exitcode == 0) + if (ct && exitcode == 0) { + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); jl_write_compiler_output(); + ct->world_age = last_age; + } jl_print_gc_stats(JL_STDERR); if (jl_options.code_coverage) @@ -893,6 +897,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_init_primitives(); jl_init_main_module(); jl_load(jl_core_module, "boot.jl"); + jl_current_task->world_age = jl_atomic_load_acquire(&jl_world_counter); post_boot_hooks(); } diff --git a/src/jlapi.c b/src/jlapi.c index defb2db6ac911..b8fbda801f43b 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -910,26 +910,29 @@ static NOINLINE int true_main(int argc, char *argv[]) { jl_set_ARGS(argc, argv); + + jl_task_t *ct = jl_current_task; + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); + jl_function_t *start_client = jl_base_module ? (jl_function_t*)jl_get_global(jl_base_module, jl_symbol("_start")) : NULL; - jl_task_t *ct = jl_current_task; if (start_client) { int ret = 1; JL_TRY { - size_t last_age = ct->world_age; - ct->world_age = jl_get_world_counter(); jl_value_t *r = jl_apply(&start_client, 1); if (jl_typeof(r) != (jl_value_t*)jl_int32_type) jl_type_error("typeassert", (jl_value_t*)jl_int32_type, r); ret = jl_unbox_int32(r); - ct->world_age = last_age; } JL_CATCH { jl_no_exc_handler(jl_current_exception(ct), ct); } + ct->world_age = last_age; return ret; } + ct->world_age = last_age; // run program if specified, otherwise enter REPL if (argc > 0) { diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 7fd2dc7409c0e..57f67755df692 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -433,7 +433,7 @@ (inert ,loc))) ,body)))) (if (or (symbol? name) (globalref? name)) - `(block ,@generator (method ,name) ,mdef (unnecessary ,name)) ;; return the function + `(block ,@generator (method ,name) (latestworld-if-toplevel) ,mdef (unnecessary ,name)) ;; return the function (if (not (null? generator)) `(block ,@generator ,mdef) mdef)))))) @@ -531,6 +531,7 @@ `(call (core ifelse) (false) (false) (block ;; forward-declare function so its type can occur in the signature of the inner method below ,@(if (or (symbol? name) (globalref? name)) `((method ,name)) '()) + (latestworld-if-toplevel) ;; call with keyword args pre-sorted - original method code goes here ,(method-def-expr- @@ -1515,6 +1516,7 @@ (scope-block (block (hardscope) (local (= ,(cadr arg) ,rr)) ,.(map (lambda (v) `(,(car e) (globalref (thismodule) ,v) ,v)) (filter-not-underscore (lhs-vars (cadr arg)))) + (latestworld) ,rr)))))))) (else (error "expected assignment after \"const\""))))))) @@ -2473,7 +2475,7 @@ (error "Opaque closure argument type may not be specified both in the method signature and separately")) (if (or (varargexpr? lastarg) (vararg? lastarg)) '(true) '(false)))) - (meth (caddr (caddr (expand-forms F)))) ;; `method` expr + (meth (cadddr (caddr (expand-forms F)))) ;; `method` expr (lam (cadddr meth)) (sig-block (caddr meth)) (sig-block (if (and (pair? sig-block) (eq? (car sig-block) 'block)) @@ -3154,6 +3156,11 @@ (else `(globalref (thismodule) ,e))))) ((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel symbolicgoto symboliclabel toplevel-only))) e) + ((eq? (car e) 'isglobal) + (let ((val (and scope (get (scope:table scope) (cadr e) #f)))) + (cond (val `(false)) + ((underscore-symbol? (cadr e)) `(false)) + (else `(true))))) ((eq? (car e) 'global) (check-valid-name (cadr e)) e) @@ -3793,7 +3800,7 @@ f(x) = yt(x) (Set '(quote top core lineinfo line inert local-def unnecessary copyast meta inbounds boundscheck loopinfo decl aliasscope popaliasscope thunk with-static-parameters toplevel-only - global globalref assign-const-if-global thismodule + global globalref assign-const-if-global isglobal thismodule const atomic null true false ssavalue isdefined toplevel module lambda error gc_preserve_begin gc_preserve_end import using export public inline noinline purity))) @@ -4296,6 +4303,7 @@ f(x) = yt(x) `(toplevel-butfirst ,(convert-assignment name mk-closure fname lam interp opaq parsed-method-stack globals locals) ,@typedef + (latestworld) ,@(map (lambda (v) `(moved-local ,v)) moved-vars) ,@sp-inits ,@mk-method diff --git a/src/julia.h b/src/julia.h index b5416568b7ae9..699b3cc196411 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2009,7 +2009,6 @@ JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr); -JL_DLLEXPORT int jl_globalref_boundp(jl_globalref_t *gr); JL_DLLEXPORT jl_value_t *jl_get_globalref_value(jl_globalref_t *gr); JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m JL_PROPAGATES_ROOT, 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); diff --git a/src/julia_internal.h b/src/julia_internal.h index 3e4967c9d4dca..ec65acfdddd9b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -828,8 +828,8 @@ jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *pa jl_value_t *modify_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, enum atomic_kind isatomic); int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *owner, jl_value_t *rhs, enum atomic_kind isatomic); jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); -jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module); -jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st); +jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module, size_t new_world); +jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, size_t new_world); int jl_foreach_reachable_mtable(int (*visit)(jl_methtable_t *mt, void *env), void *env); int foreach_mtable_in_module(jl_module_t *m, int (*visit)(jl_methtable_t *mt, void *env), void *env); void jl_init_main_module(void); @@ -842,6 +842,7 @@ JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t 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); +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind, size_t new_world); JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno); STATIC_INLINE struct _jl_module_using *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; @@ -1669,6 +1670,7 @@ JL_DLLEXPORT void jl_set_next_task(jl_task_t *task) JL_NOTSAFEPOINT; // -- synchronization utilities -- // extern jl_mutex_t typecache_lock; +extern jl_mutex_t world_counter_lock; #if defined(__APPLE__) void jl_mach_gc_end(void) JL_NOTSAFEPOINT; diff --git a/src/method.c b/src/method.c index 0a58f0d5c482c..24154d4d03226 100644 --- a/src/method.c +++ b/src/method.c @@ -1073,16 +1073,26 @@ JL_DLLEXPORT void jl_check_gf(jl_value_t *gf, jl_sym_t *name) JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, jl_sym_t *name) { - jl_value_t *gf = jl_get_binding_value_if_const(b); - if (gf) { - jl_check_gf(gf, b->globalref->name); - return gf; - } - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (!jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; + jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, new_world); + jl_value_t *gf = NULL; + if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { + gf = decode_restriction_value(pku); + jl_check_gf(gf, b->globalref->name); + JL_UNLOCK(&world_counter_lock); + return gf; + } jl_errorf("cannot define function %s; it already has a value", jl_symbol_name(name)); - gf = (jl_value_t*)jl_new_generic_function(name, mod); - jl_declare_constant_val(b, mod, name, gf); + } + gf = (jl_value_t*)jl_new_generic_function(name, mod, new_world); + // From this point on (if we didn't error), we're committed to raising the world age, + // because we've used it to declare the type name. + jl_atomic_store_release(&jl_world_counter, new_world); + jl_declare_constant_val3(b, mod, name, gf, BINDING_KIND_CONST, new_world); + JL_UNLOCK(&world_counter_lock); return gf; } diff --git a/src/module.c b/src/module.c index 66049031f8790..be6779727bfdc 100644 --- a/src/module.c +++ b/src/module.c @@ -411,13 +411,13 @@ typedef struct _modstack_t { jl_sym_t *var; struct _modstack_t *prev; } modstack_t; -static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, modstack_t *st); +static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, modstack_t *st, size_t world); JL_DLLEXPORT jl_value_t *jl_reresolve_binding_value_seqcst(jl_binding_t *b) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))) { - jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL); + jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL, jl_current_task->world_age); } return jl_get_binding_value_seqcst(b); } @@ -473,7 +473,7 @@ static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_ } // find a binding from a module's `usings` list -static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, jl_module_t **from, modstack_t *st, int warn) +static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, jl_module_t **from, modstack_t *st, int warn, size_t world) { jl_binding_t *b = NULL; jl_binding_partition_t *bpart = NULL; @@ -487,20 +487,20 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl JL_UNLOCK(&m->lock); jl_binding_t *tempb = jl_get_module_binding(imp, var, 0); if (tempb != NULL && tempb->exportp) { - tempb = jl_resolve_owner(NULL, imp, var, st); // find the owner for tempb + tempb = jl_resolve_owner(NULL, imp, var, st, world); // find the owner for tempb if (tempb == NULL) // couldn't resolve; try next using (see issue #6105) continue; - jl_binding_partition_t *tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); + jl_binding_partition_t *tempbpart = jl_get_binding_partition(tempb, world); jl_ptr_kind_union_t tempb_pku = jl_atomic_load_relaxed(&tempbpart->restriction); - assert(decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(tempb_pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); + assert(jl_bkind_is_some_guard(decode_restriction_kind(tempb_pku)) || decode_restriction_kind(tempb_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(tempb_pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(tempb_pku))); (void)tempb_pku; - if (bpart != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempbpart, b, jl_current_task->world_age)) { + if (bpart != NULL && !tempb->deprecated && !b->deprecated && !eq_bindings(tempbpart, b, world)) { if (warn) { // set usingfailed=1 to avoid repeating this warning // the owner will still be NULL, so it can be later imported or defined tempb = jl_get_module_binding(m, var, 1); - tempbpart = jl_get_binding_partition(tempb, jl_current_task->world_age); + tempbpart = jl_get_binding_partition(tempb, world); jl_atomic_store_release(&tempbpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED)); } return NULL; @@ -524,7 +524,7 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) != BINDING_KIND_GLOBAL) { // for implicitly imported globals, try to re-resolve it to find the module we got it from most directly jl_module_t *from = NULL; - jl_binding_t *b2 = using_resolve_binding(m, var, &from, NULL, 0); + jl_binding_t *b2 = using_resolve_binding(m, var, &from, NULL, 0, jl_current_task->world_age); if (b2) { jl_binding_partition_t *b2part = jl_get_binding_partition(b2, jl_current_task->world_age); if (eq_bindings(b2part, b, jl_current_task->world_age)) @@ -538,11 +538,11 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t *b); // get binding for reading. might return NULL for unbound. -static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m, jl_sym_t *var, modstack_t *st) +static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t *m, jl_sym_t *var, modstack_t *st, size_t world) { if (b == NULL) b = jl_get_module_binding(m, var, 1); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + jl_binding_partition_t *bpart = jl_get_binding_partition(b, world); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); retry: if (decode_restriction_kind(pku) == BINDING_KIND_FAILED) @@ -561,7 +561,7 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * } } jl_module_t *from = NULL; // for error message printing - b2 = using_resolve_binding(m, var, &from, &top, 1); + b2 = using_resolve_binding(m, var, &from, &top, 1, world); if (b2 == NULL) return NULL; assert(from); @@ -594,7 +594,7 @@ static jl_binding_t *jl_resolve_owner(jl_binding_t *b/*optional*/, jl_module_t * } return b2; } - jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + jl_walk_binding_inplace(&b, &bpart, world); return b; } @@ -606,7 +606,7 @@ JL_DLLEXPORT jl_binding_t *jl_binding_owner(jl_module_t *m, jl_sym_t *var) jl_module_t *from = m; jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); if (decode_restriction_kind(pku) == BINDING_KIND_GUARD) { - b = using_resolve_binding(m, var, &from, NULL, 0); + b = using_resolve_binding(m, var, &from, NULL, 0, jl_current_task->world_age); bpart = jl_get_binding_partition(b, jl_current_task->world_age); } pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); @@ -637,7 +637,7 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) { - return jl_resolve_owner(NULL, m, var, NULL); + return jl_resolve_owner(NULL, m, var, NULL, jl_current_task->world_age); } JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var) @@ -1001,7 +1001,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m, jl_sym_t *var, JL_DLLEXPORT jl_value_t *jl_get_globalref_value(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + b = jl_resolve_owner(b, gr->mod, gr->name, NULL, jl_current_task->world_age); // ignores b->deprecated return b == NULL ? NULL : jl_get_binding_value(b); } @@ -1028,6 +1028,7 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var // this function is mostly only used during initialization, so the data races here are not too important to us jl_binding_t *bp = jl_get_module_binding(m, var, 1); jl_binding_partition_t *bpart = jl_get_binding_partition(bp, jl_current_task->world_age); + assert(jl_bkind_is_some_guard(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)))); jl_atomic_store_release(&bpart->restriction, encode_restriction(val, BINDING_KIND_CONST)); jl_gc_wb(bpart, val); } @@ -1047,11 +1048,10 @@ void jl_invalidate_binding_refs(jl_globalref_t *ref, jl_binding_partition_t *inv JL_GC_POP(); } -extern jl_mutex_t world_counter_lock; JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + b = jl_resolve_owner(b, gr->mod, gr->name, NULL, jl_current_task->world_age); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GUARD) { @@ -1074,18 +1074,17 @@ JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) JL_DLLEXPORT int jl_globalref_is_const(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); + b = jl_resolve_owner(b, gr->mod, gr->name, NULL, jl_current_task->world_age); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); if (!bpart) return 0; return jl_bkind_is_some_constant(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction))); } -JL_DLLEXPORT int jl_globalref_boundp(jl_globalref_t *gr) +JL_DLLEXPORT void jl_force_binding_resolution(jl_globalref_t *gr, size_t world) { jl_binding_t *b = gr->binding; - b = jl_resolve_owner(b, gr->mod, gr->name, NULL); - return b && jl_get_binding_value(b) != NULL; + jl_resolve_owner(b, gr->mod, gr->name, NULL, world); } JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var) diff --git a/src/staticdata.c b/src/staticdata.c index 7fad87652b26a..ff352cd8c152f 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1674,8 +1674,18 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED #ifndef _P64 write_uint(f, decode_restriction_kind(pku)); #endif - write_uint(f, bpart->min_world); - write_uint(f, jl_atomic_load_relaxed(&bpart->max_world)); + size_t max_world = jl_atomic_load_relaxed(&bpart->max_world); + if (max_world == ~(size_t)0) { + // Still valid. Will be considered primordial after re-load. + // We could consider updating min_world to the loaded world, but + // there doesn't appear to be much point. + write_uint(f, 0); + write_uint(f, max_world); + } else { + // The world will not be reachable after loading + write_uint(f, 1); + write_uint(f, 0); + } write_pointerfield(s, (jl_value_t*)jl_atomic_load_relaxed(&bpart->next)); #ifdef _P64 write_uint(f, decode_restriction_kind(pku)); // This will be moved back into place during deserialization (if necessary) @@ -2714,7 +2724,6 @@ static void jl_strip_all_codeinfos(void) jl_genericmemory_t *jl_global_roots_list; jl_genericmemory_t *jl_global_roots_keyset; jl_mutex_t global_roots_lock; -extern jl_mutex_t world_counter_lock; jl_mutex_t precompile_field_replace_lock; jl_svec_t *precompile_field_replace JL_GLOBALLY_ROOTED; diff --git a/src/toplevel.c b/src/toplevel.c index fb217ec7cb52e..aff881481c0ae 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -66,14 +66,13 @@ void jl_module_run_initializer(jl_module_t *m) { JL_TIMING(INIT_MODULE, INIT_MODULE); jl_timing_show_module(m, JL_TIMING_DEFAULT_BLOCK); - jl_function_t *f = jl_module_get_initializer(m); - if (f == NULL) - return; jl_task_t *ct = jl_current_task; size_t last_age = ct->world_age; JL_TRY { ct->world_age = jl_atomic_load_acquire(&jl_world_counter); - jl_apply(&f, 1); + jl_function_t *f = jl_module_get_initializer(m); + if (f != NULL) + jl_apply(&f, 1); ct->world_age = last_age; } JL_CATCH { @@ -740,10 +739,15 @@ static void jl_eval_errorf(jl_module_t *m, const char *filename, int lineno, con JL_GC_POP(); } -JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, enum jl_partition_kind constant_kind) +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( + jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, + enum jl_partition_kind constant_kind, size_t new_world) { JL_GC_PUSH1(&val); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (!b) { + b = jl_get_module_binding(mod, var, 1); + } + jl_binding_partition_t *bpart = jl_get_binding_partition(b, new_world); jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); int did_warn = 0; while (1) { @@ -780,10 +784,24 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2(jl_binding_t *b, j break; } } + bpart->min_world = new_world; JL_GC_POP(); return bpart; } +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2( + jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val, + enum jl_partition_kind constant_kind) +{ + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_relaxed(&jl_world_counter) + 1; + jl_binding_partition_t *bpart = jl_declare_constant_val3(b, mod, var, val, constant_kind, new_world); + if (bpart->min_world == new_world) + jl_atomic_store_release(&jl_world_counter, new_world); + JL_UNLOCK(&world_counter_lock); + return bpart; +} + JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *val) { return jl_declare_constant_val2(b, mod, var, val, val ? BINDING_KIND_CONST : BINDING_KIND_UNDEF_CONST); @@ -869,6 +887,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val else if (head == jl_using_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_from(m, ex, "using"); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); size_t i = 0; if (from) { i = 1; @@ -908,6 +927,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0); name = NULL; jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)path)->args, &name, "using"); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); assert(name); check_macro_rename(name, asname, "using"); // `using A: B as C` syntax @@ -919,11 +939,13 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val "syntax: malformed \"using\" statement"); } JL_GC_POP(); + ct->world_age = last_age; return jl_nothing; } else if (head == jl_import_sym) { jl_sym_t *name = NULL; jl_module_t *from = eval_import_from(m, ex, "import"); + ct->world_age = jl_atomic_load_acquire(&jl_world_counter); size_t i = 0; if (from) { i = 1; @@ -967,6 +989,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val "syntax: malformed \"import\" statement"); } JL_GC_POP(); + ct->world_age = last_age; return jl_nothing; } else if (head == jl_export_sym || head == jl_public_sym) { diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index e8aa1188ec213..c63c5a557acd8 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -639,18 +639,24 @@ function is_call_graph_uncached(sv::CC.InferenceState) return is_call_graph_uncached(parent::CC.InferenceState) end -isdefined_globalref(g::GlobalRef) = !iszero(ccall(:jl_globalref_boundp, Cint, (Any,), g)) - # aggressive global binding resolution within `repl_frame` function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool, sv::CC.InferenceState) + # Ignore saw_latestworld + partition = CC.abstract_eval_binding_partition!(interp, g, sv) if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv)) - if isdefined_globalref(g) + if CC.is_defined_const_binding(CC.binding_kind(partition)) return Pair{CC.RTEffects, Union{Nothing, Core.BindingPartition}}( - CC.RTEffects(Const(ccall(:jl_get_globalref_value, Any, (Any,), g)), Union{}, CC.EFFECTS_TOTAL), nothing) + CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL), partition) + else + b = convert(Core.Binding, g) + if CC.binding_kind(partition) == CC.BINDING_KIND_GLOBAL && isdefined(b, :value) + return Pair{CC.RTEffects, Union{Nothing, Core.BindingPartition}}( + CC.RTEffects(Const(b.value), Union{}, CC.EFFECTS_TOTAL), partition) + end end return Pair{CC.RTEffects, Union{Nothing, Core.BindingPartition}}( - CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS), nothing) + CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS), partition) end return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, bailed::Bool, sv::CC.InferenceState) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index daa01f626aeab..4f3b3a6eae083 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -8,6 +8,7 @@ import ..REPL Base._track_dependencies[] = false try Base.include(@__MODULE__, joinpath(Sys.BINDIR, "..", "share", "julia", "test", "testhelpers", "FakePTYs.jl")) + @Core.latestworld import .FakePTYs: open_fake_pty finally Base._track_dependencies[] = true diff --git a/stdlib/REPL/test/precompilation.jl b/stdlib/REPL/test/precompilation.jl index 7efcf0b5e8282..01a062644596c 100644 --- a/stdlib/REPL/test/precompilation.jl +++ b/stdlib/REPL/test/precompilation.jl @@ -33,7 +33,7 @@ if !Sys.iswindows() # given this test checks that startup is snappy, it's best to add workloads to # contrib/generate_precompile.jl rather than increase this number. But if that's not # possible, it'd be helpful to add a comment with the statement and a reason below - expected_precompiles = 0 + expected_precompiles = 1 n_precompiles = count(r"precompile\(", tracecompile_out) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 809913502c3d7..8944fd76f31de 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -769,9 +769,9 @@ fake_repl() do stdin_write, stdout_read, repl julia> A = 3\e[201~ """) @test Main.A == 3 - @test Base.invokelatest(Main.foo, 4) - @test Base.invokelatest(Main.T17599, 3).a == 3 - @test !Base.invokelatest(Main.foo, 2) + @test @invokelatest(Main.foo(4)) + @test @invokelatest(Main.T17599(3)).a == 3 + @test !@invokelatest(Main.foo(2)) sendrepl2("""\e[200~ julia> goo(x) = x + 1 @@ -781,7 +781,7 @@ fake_repl() do stdin_write, stdout_read, repl 4\e[201~ """) @test Main.A == 4 - @test Base.invokelatest(Main.goo, 4) == 5 + @test @invokelatest(Main.goo(4)) == 5 # Test prefix removal only active in bracket paste mode sendrepl2("julia = 4\n julia> 3 && (A = 1)\n") diff --git a/stdlib/Serialization/test/runtests.jl b/stdlib/Serialization/test/runtests.jl index 4d9b439e639d7..f1b83ca947c7e 100644 --- a/stdlib/Serialization/test/runtests.jl +++ b/stdlib/Serialization/test/runtests.jl @@ -130,7 +130,7 @@ create_serialization_stream() do s # user-defined module modtype = eval(Meta.parse("$(modstring)")) serialize(s, modtype) seek(s, 0) - @test deserialize(s) === modtype + @test invokelatest(deserialize, s) === modtype end # DataType @@ -151,7 +151,7 @@ create_serialization_stream() do s # user-defined type utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) === utype + @test invokelatest(deserialize, s) === utype end create_serialization_stream() do s # user-defined type @@ -160,7 +160,7 @@ create_serialization_stream() do s # user-defined type utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) === utype + @test invokelatest(deserialize, s) === utype end create_serialization_stream() do s # user-defined type @@ -169,7 +169,7 @@ create_serialization_stream() do s # user-defined type utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) == utype + @test invokelatest(deserialize, s) == utype end create_serialization_stream() do s # immutable struct with 1 field @@ -178,7 +178,7 @@ create_serialization_stream() do s # immutable struct with 1 field utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) - @test deserialize(s) == utype + @test invokelatest(deserialize, s) == utype end create_serialization_stream() do s # immutable struct with 2 field @@ -187,7 +187,7 @@ create_serialization_stream() do s # immutable struct with 2 field utval = eval(Meta.parse("$(usertype)(1,2)")) serialize(s, utval) seek(s, 0) - @test deserialize(s) === utval + @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # immutable struct with 3 field @@ -196,7 +196,7 @@ create_serialization_stream() do s # immutable struct with 3 field utval = eval(Meta.parse("$(usertype)(1,2,3)")) serialize(s, utval) seek(s, 0) - @test deserialize(s) === utval + @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # immutable struct with 4 field @@ -205,7 +205,7 @@ create_serialization_stream() do s # immutable struct with 4 field utval = eval(Meta.parse("$(usertype)(1,2,3,4)")) serialize(s, utval) seek(s, 0) - @test deserialize(s) === utval + @test invokelatest(deserialize, s) === utval end # Expression diff --git a/test/core.jl b/test/core.jl index 4bbb2ca368019..3886e6728df10 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2622,7 +2622,7 @@ end # issue #8338 let ex = Expr(:(=), :(f8338(x;y=4)), :(x*y)) eval(ex) - @test invokelatest(f8338, 2) == 8 + @test (@invokelatest f8338(2)) == 8 end # call overloading (#2403) diff --git a/test/loading.jl b/test/loading.jl index 09f96e1f43578..be8f08b4bfe22 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -798,6 +798,7 @@ end @testset "`::AbstractString` constraint on the path argument to `include`" begin for m ∈ (NotPkgModule, evalfile("testhelpers/just_module.jl")) + @Core.latestworld let i = m.include @test !applicable(i, (nothing,)) @test !applicable(i, (identity, nothing,)) diff --git a/test/precompile.jl b/test/precompile.jl index 78a96250600a4..e72755ed77573 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -610,13 +610,17 @@ precompile_test_harness(false) do dir @eval using UseBaz @test haskey(Base.loaded_modules, Base.PkgId("UseBaz")) @test haskey(Base.loaded_modules, Base.PkgId("Baz")) - @test Base.invokelatest(UseBaz.biz) === 1 - @test Base.invokelatest(UseBaz.buz) === 2 - @test UseBaz.generating == 0 - @test UseBaz.incremental == 0 + invokelatest() do + @test UseBaz.biz() === 1 + @test UseBaz.buz() === 2 + @test UseBaz.generating == 0 + @test UseBaz.incremental == 0 + end @eval using Baz - @test Base.invokelatest(Baz.baz) === 1 - @test Baz === UseBaz.Baz + invokelatest() do + @test Baz.baz() === 1 + @test Baz === UseBaz.Baz + end # should not throw if the cachefile does not exist @test !isfile("DoesNotExist.ji") diff --git a/test/runtests.jl b/test/runtests.jl index fd0326d48ee6c..f0c5e1b94c376 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -112,7 +112,7 @@ cd(@__DIR__) do @everywhere include("testdefs.jl") if use_revise - Base.invokelatest(revise_trackall) + @invokelatest revise_trackall() Distributed.remotecall_eval(Main, workers(), revise_init_expr) end @@ -250,7 +250,7 @@ cd(@__DIR__) do wrkr = p before = time() resp, duration = try - r = remotecall_fetch(runtests, wrkr, test, test_path(test); seed=seed) + r = remotecall_fetch(@Base.world(runtests, ∞), wrkr, test, test_path(test); seed=seed) r, time() - before catch e isa(e, InterruptException) && return @@ -310,7 +310,7 @@ cd(@__DIR__) do t == "SharedArrays" && (isolate = false) before = time() resp, duration = try - r = Base.invokelatest(runtests, t, test_path(t), isolate, seed=seed) # runtests is defined by the include above + r = @invokelatest runtests(t, test_path(t), isolate, seed=seed) # runtests is defined by the include above r, time() - before catch e isa(e, InterruptException) && rethrow() diff --git a/test/syntax.jl b/test/syntax.jl index 9fd0204821eab..d4fa1dd798e10 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3303,6 +3303,7 @@ const typeof = error end let ex = :(const $(esc(:x)) = 1; (::typeof($(esc(:foo43993))))() = $(esc(:x))) Core.eval(M43993, Expr(:var"hygienic-scope", ex, Core)) + @Core.latestworld @test M43993.x === 1 @test invokelatest(M43993.foo43993) === 1 end diff --git a/test/worlds.jl b/test/worlds.jl index 268a6664571fb..025aaba6cea4f 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -115,15 +115,15 @@ wc265_41332a = Task(tls_world_age) global wc265_41332d = Task(tls_world_age) nothing end)() -@test wc265 + 2 == get_world_counter() == tls_world_age() +@test wc265 + 4 == 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 + 1 == fetch(wc265_41332b) -@test wc265 + 2 == fetch(wc265_41332c) -@test wc265 + 1 == fetch(wc265_41332d) +@test wc265 + 2 == fetch(wc265_41332b) +@test wc265 + 4 == fetch(wc265_41332c) +@test wc265 + 2 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] From 388910d9e70f04533d0b43d56365a74a1012e35f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 21 Jan 2025 19:19:30 +0000 Subject: [PATCH 2/5] Restore backdating --- Compiler/test/invalidation.jl | 2 +- src/toplevel.c | 5 ++++- test/worlds.jl | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Compiler/test/invalidation.jl b/Compiler/test/invalidation.jl index b77c7677e6987..2642c1647a682 100644 --- a/Compiler/test/invalidation.jl +++ b/Compiler/test/invalidation.jl @@ -55,7 +55,7 @@ let mi = Base.method_instance(basic_caller, (Float64,)) end # this redefinition below should invalidate the cache -const BASIC_CALLER_WORLD = Base.get_world_counter()+1 +const BASIC_CALLER_WORLD = Base.get_world_counter() basic_callee(x) = x, x @test !isdefined(Base.method_instance(basic_callee, (Float64,)), :cache) let mi = Base.method_instance(basic_caller, (Float64,)) diff --git a/src/toplevel.c b/src/toplevel.c index aff881481c0ae..dee9029e2feb7 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -784,7 +784,10 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( break; } } - bpart->min_world = new_world; + // N.B.: This backdates the first definition of the constant to world age 0 for backwards compatibility + // TODO: Mark this specially with a separate partition. + if (bpart->min_world != 0) + bpart->min_world = new_world; JL_GC_POP(); return bpart; } diff --git a/test/worlds.jl b/test/worlds.jl index 025aaba6cea4f..8bc96f8303aef 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -115,15 +115,15 @@ 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 + 3 == 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_41332b) +@test wc265 + 3 == fetch(wc265_41332c) +@test wc265 + 1 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] From 6d1cc473eac08360d10d86d11a28e7bb9fc0b40b Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 21 Jan 2025 21:32:58 +0000 Subject: [PATCH 3/5] Add GC annotations --- src/julia_internal.h | 10 +++++----- src/method.c | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/julia_internal.h b/src/julia_internal.h index ec65acfdddd9b..0da6d412c8a49 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -842,7 +842,7 @@ JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t 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); -JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind, size_t new_world); +JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind, size_t new_world) JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded, const char **toplevel_filename, int *toplevel_lineno); STATIC_INLINE struct _jl_module_using *module_usings_getidx(jl_module_t *m JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; @@ -986,15 +986,15 @@ STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFE return kind == BINDING_KIND_FAILED || kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED; } -JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world); -JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world); +JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world) JL_GLOBALLY_ROOTED; +JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_GLOBALLY_ROOTED; EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) JL_NOTSAFEPOINT { return decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); } -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT; -STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; +STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT; +STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; #ifndef __clang_analyzer__ STATIC_INLINE jl_ptr_kind_union_t jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT diff --git a/src/method.c b/src/method.c index 24154d4d03226..4b39de9aa67e1 100644 --- a/src/method.c +++ b/src/method.c @@ -1081,6 +1081,7 @@ JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { gf = decode_restriction_value(pku); + JL_GC_PROMISE_ROOTED(gf); jl_check_gf(gf, b->globalref->name); JL_UNLOCK(&world_counter_lock); return gf; @@ -1092,6 +1093,7 @@ JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, // because we've used it to declare the type name. jl_atomic_store_release(&jl_world_counter, new_world); jl_declare_constant_val3(b, mod, name, gf, BINDING_KIND_CONST, new_world); + JL_GC_PROMISE_ROOTED(gf); JL_UNLOCK(&world_counter_lock); return gf; } From 3a84d5f1ec938e9674c3e4d116b35fbbcec6a43a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 21 Jan 2025 22:55:48 +0000 Subject: [PATCH 4/5] Remove implementation doctest This doctest is bad, because it tests the internal representation of the macro, not its behavior. With the additional features added to the macro, the expansion is no longer as simple, so remove the test. --- base/reflection.jl | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index c23228fa617e9..78e701692a2a7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1393,23 +1393,6 @@ It also supports the following syntax: (as well as `f` itself if it is not a literal global) will be evaluated in the current world age. -```jldoctest -julia> @macroexpand @invokelatest f(x; kw=kwv) -:(Base.invokelatest(f, x; kw = kwv)) - -julia> @macroexpand @invokelatest x.f -:(Base.invokelatest(Base.getproperty, x, :f)) - -julia> @macroexpand @invokelatest x.f = v -:(Base.invokelatest(Base.setproperty!, x, :f, v)) - -julia> @macroexpand @invokelatest xs[i] -:(Base.invokelatest(Base.getindex, xs, i)) - -julia> @macroexpand @invokelatest xs[i] = v -:(Base.invokelatest(Base.setindex!, xs, v, i)) -``` - !!! compat "Julia 1.7" This macro requires Julia 1.7 or later. From 111140d75a02263fe11906d451e480f1a48ea7f5 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 22 Jan 2025 00:57:11 +0000 Subject: [PATCH 5/5] Fix debug build --- Compiler/src/ssair/verify.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 6fb26fc0e2ccf..fa16bdcc7ab19 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -61,6 +61,7 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, raise_error() end elseif isa(op, GlobalRef) + force_binding_resolution!(op, min_world(ir.valid_worlds)) bpart = lookup_binding_partition(min_world(ir.valid_worlds), op) while is_some_imported(binding_kind(bpart)) && max_world(ir.valid_worlds) <= bpart.max_world imported_binding = partition_restriction(bpart)::Core.Binding