diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index 0fc7bd6e328e74..ba6d1607ded55f 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -49,7 +49,7 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc using Base using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer, - BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST, + BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST, BINDING_KIND_DECLARED, Base, BitVector, Bottom, Callable, DataTypeFieldDesc, EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES, OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME, diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index fe7cc1dbf5680e..8a7e8aee715a67 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3464,14 +3464,7 @@ world_range(ci::CodeInfo) = WorldRange(ci.min_world, ci.max_world) world_range(ci::CodeInstance) = WorldRange(ci.min_world, ci.max_world) world_range(compact::IncrementalCompact) = world_range(compact.ir) -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_force_binding_resolution, Cvoid, (Any, Csize_t), g, world) - return nothing -end - -function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, IncrementalCompact}, retry_after_resolve::Bool=true) +function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, IncrementalCompact}) worlds = world_range(src) partition = lookup_binding_partition(min_world(worlds), g) partition.max_world < max_world(worlds) && return Any @@ -3480,25 +3473,18 @@ function abstract_eval_globalref_type(g::GlobalRef, src::Union{CodeInfo, IRCode, partition = lookup_binding_partition(min_world(worlds), imported_binding) partition.max_world < max_world(worlds) && return Any end - if is_some_guard(binding_kind(partition)) - if retry_after_resolve - # 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, min_world(worlds)) - return abstract_eval_globalref_type(g, src, false) - end + kind = binding_kind(partition) + if is_some_guard(kind) # return Union{} return Any end - if is_some_const_binding(binding_kind(partition)) + if is_some_const_binding(kind) return Const(partition_restriction(partition)) end - return partition_restriction(partition) + return kind == BINDING_KIND_DECLARED ? Any : partition_restriction(partition) end function lookup_binding_partition!(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) - 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 @@ -3541,7 +3527,11 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE)) end - rt = partition_restriction(partition) + if kind == BINDING_KIND_DECLARED + rt = Any + else + rt = partition_restriction(partition) + end return RTEffects(rt, UndefVarError, generic_getglobal_effects) end @@ -3580,7 +3570,7 @@ function global_assignment_binding_rt_exct(interp::AbstractInterpreter, partitio elseif is_some_const_binding(kind) return Pair{Any,Any}(Bottom, ErrorException) end - ty = partition_restriction(partition) + ty = kind == BINDING_KIND_DECLARED ? Any : partition_restriction(partition) wnewty = widenconst(newty) if !hasintersect(wnewty, ty) return Pair{Any,Any}(Bottom, TypeError) diff --git a/Compiler/src/ssair/verify.jl b/Compiler/src/ssair/verify.jl index 2d6d59cc4e22b9..974690885a2e4b 100644 --- a/Compiler/src/ssair/verify.jl +++ b/Compiler/src/ssair/verify.jl @@ -61,7 +61,6 @@ 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 diff --git a/Compiler/src/validation.jl b/Compiler/src/validation.jl index 6700aa8d4508f2..9bde405a49956b 100644 --- a/Compiler/src/validation.jl +++ b/Compiler/src/validation.jl @@ -22,7 +22,7 @@ const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}( :copyast => 1:1, :meta => 0:typemax(Int), :global => 1:1, - :globaldecl => 2:2, + :globaldecl => 1:2, :foreigncall => 5:typemax(Int), # name, RT, AT, nreq, (cconv, effects), args..., roots... :cfunction => 5:5, :isdefined => 1:2, diff --git a/Compiler/test/codegen.jl b/Compiler/test/codegen.jl index 57a9c26aefac67..4d0020ea58d4d1 100644 --- a/Compiler/test/codegen.jl +++ b/Compiler/test/codegen.jl @@ -689,6 +689,10 @@ U41096 = Term41096{:U}(Modulate41096(:U, false)) # test that we can start julia with libjulia-codegen removed; PR #41936 mktempdir() do pfx + # First check that ordinary running won't try to compile things. If it does that indicates an insufficient precompile or invalidation. + @test success(`$(Base.julia_cmd()) --startup-file=no --trace-compile="$(pfx)/compiles.txt" -e 'print("no codegen!\n")'`) + @test !isfile(joinpath(pfx, "compiles.txt")) + cp(dirname(Sys.BINDIR), pfx; force=true) libpath = relpath(dirname(dlpath(libjulia_codegen_name())), dirname(Sys.BINDIR)) libs_deleted = 0 diff --git a/Compiler/test/effects.jl b/Compiler/test/effects.jl index b8a841b6b74b71..082d954b4a9bca 100644 --- a/Compiler/test/effects.jl +++ b/Compiler/test/effects.jl @@ -378,32 +378,27 @@ let effects = Base.infer_effects(; optimize=false) do end # we should taint `nothrow` if the binding doesn't exist and isn't fixed yet, -# as the cached effects can be easily wrong otherwise -# since the inference currently doesn't track "world-age" of global variables -@eval global_assignment_undefinedyet() = $(GlobalRef(@__MODULE__, :UNDEFINEDYET)) = 42 setglobal!_nothrow_undefinedyet() = setglobal!(@__MODULE__, :UNDEFINEDYET, 42) -let effects = Base.infer_effects() do - global_assignment_undefinedyet() - end +let effects = Base.infer_effects(setglobal!_nothrow_undefinedyet) @test !Compiler.is_nothrow(effects) end -let effects = Base.infer_effects() do - setglobal!_nothrow_undefinedyet() - end - @test !Compiler.is_nothrow(effects) +@test_throws ErrorException setglobal!_nothrow_undefinedyet() +# This declares the binding as ::Any +@eval global_assignment_undefinedyet() = $(GlobalRef(@__MODULE__, :UNDEFINEDYET)) = 42 +let effects = Base.infer_effects(global_assignment_undefinedyet) + @test Compiler.is_nothrow(effects) end -global UNDEFINEDYET::String = "0" -let effects = Base.infer_effects() do - global_assignment_undefinedyet() - end +# Again with type mismatch +global UNDEFINEDYET2::String = "0" +setglobal!_nothrow_undefinedyet2() = setglobal!(@__MODULE__, :UNDEFINEDYET2, 42) +@eval global_assignment_undefinedyet2() = $(GlobalRef(@__MODULE__, :UNDEFINEDYET2)) = 42 +let effects = Base.infer_effects(global_assignment_undefinedyet2) @test !Compiler.is_nothrow(effects) end -let effects = Base.infer_effects() do - setglobal!_nothrow_undefinedyet() - end +let effects = Base.infer_effects(setglobal!_nothrow_undefinedyet2) @test !Compiler.is_nothrow(effects) end -@test_throws Union{ErrorException,TypeError} setglobal!_nothrow_undefinedyet() # TODO: what kind of error should this be? +@test_throws TypeError setglobal!_nothrow_undefinedyet2() # Nothrow for setfield! mutable struct SetfieldNothrow diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index d4ea990e7d1485..563828ac77296b 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -6160,7 +6160,7 @@ end === Int swapglobal!(@__MODULE__, :swapglobal!_xxx, x) end === Union{} -global swapglobal!_must_throw +eval(Expr(:const, :swapglobal!_must_throw)) @newinterp SwapGlobalInterp Compiler.InferenceParams(::SwapGlobalInterp) = Compiler.InferenceParams(; assume_bindings_static=true) function func_swapglobal!_must_throw(x) diff --git a/base/boot.jl b/base/boot.jl index e50d74659d399f..26a405f92f884b 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -229,7 +229,7 @@ export Expr, QuoteNode, LineNumberNode, GlobalRef, # object model functions fieldtype, getfield, setfield!, swapfield!, modifyfield!, replacefield!, setfieldonce!, - nfields, throw, tuple, ===, isdefined, eval, + nfields, throw, tuple, ===, isdefined, # access to globals getglobal, setglobal!, swapglobal!, modifyglobal!, replaceglobal!, setglobalonce!, isdefinedglobal, # ifelse, sizeof # not exported, to avoid conflicting with Base diff --git a/base/client.jl b/base/client.jl index 2527d382c695de..3449e2fbf2e62c 100644 --- a/base/client.jl +++ b/base/client.jl @@ -267,9 +267,8 @@ function exec_options(opts) let Distributed = require(PkgId(UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed")) Core.eval(MainInclude, :(const Distributed = $Distributed)) Core.eval(Main, :(using Base.MainInclude.Distributed)) + invokelatest(Distributed.process_opts, opts) end - - invokelatest(Main.Distributed.process_opts, opts) end interactiveinput = (repl || is_interactive::Bool) && isa(stdin, TTY) diff --git a/base/deprecated.jl b/base/deprecated.jl index cffff05d954d1d..f72698ad470081 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -531,4 +531,29 @@ end # BEGIN 1.12 deprecations +@deprecate isbindingresolved(m::Module, var::Symbol) true false + +""" + isbindingresolved(m::Module, s::Symbol) -> Bool + +Returns whether the binding of a symbol in a module is resolved. + +See also: [`isexported`](@ref), [`ispublic`](@ref), [`isdeprecated`](@ref) + +```jldoctest +julia> module Mod + foo() = 17 + end +Mod + +julia> Base.isbindingresolved(Mod, :foo) +true +``` + +!!! warning + This function is deprecated. The concept of binding "resolvedness" was removed in Julia 1.12. + The function now always returns `true`. +""" +isbindingresolved + # END 1.12 deprecations diff --git a/base/invalidation.jl b/base/invalidation.jl index 36b867ede28682..c0aed35aa90a00 100644 --- a/base/invalidation.jl +++ b/base/invalidation.jl @@ -113,32 +113,56 @@ function invalidate_method_for_globalref!(gr::GlobalRef, method::Method, invalid end end -function invalidate_code_for_globalref!(gr::GlobalRef, invalidated_bpart::Core.BindingPartition, new_max_world::UInt) - b = convert(Core.Binding, gr) - try - valid_in_valuepos = false - foreach_module_mtable(gr.mod, new_max_world) do mt::Core.MethodTable - for method in MethodList(mt) - invalidate_method_for_globalref!(gr, method, invalidated_bpart, new_max_world) +const BINDING_FLAG_EXPORTP = 0x2 + +function invalidate_code_for_globalref!(b::Core.Binding, invalidated_bpart::Core.BindingPartition, new_bpart::Union{Core.BindingPartition, Nothing}, new_max_world::UInt) + gr = b.globalref + if is_some_guard(binding_kind(invalidated_bpart)) + # TODO: We may want to invalidate for these anyway, since they have performance implications + return + end + foreach_module_mtable(gr.mod, new_max_world) do mt::Core.MethodTable + for method in MethodList(mt) + invalidate_method_for_globalref!(gr, method, invalidated_bpart, new_max_world) + end + return true + end + if isdefined(b, :backedges) + for edge in b.backedges + if isa(edge, CodeInstance) + ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), edge, new_max_world) + elseif isa(edge, Core.Binding) + isdefined(edge, :partitions) || continue + latest_bpart = edge.partitions + latest_bpart.max_world == typemax(UInt) || continue + is_some_imported(binding_kind(latest_bpart)) || continue + partition_restriction(latest_bpart) === b || continue + invalidate_code_for_globalref!(edge, latest_bpart, nothing, new_max_world) + else + invalidate_method_for_globalref!(gr, edge::Method, invalidated_bpart, new_max_world) end - return true end - b = convert(Core.Binding, gr) - if isdefined(b, :backedges) - for edge in b.backedges - if isa(edge, CodeInstance) - ccall(:jl_invalidate_code_instance, Cvoid, (Any, UInt), edge, new_max_world) - else - invalidate_method_for_globalref!(gr, edge::Method, invalidated_bpart, new_max_world) - end + end + if (b.flags & BINDING_FLAG_EXPORTP) != 0 + # This binding was exported - we need to check all modules that `using` us to see if they + # have an implicit binding to us. + usings_backedges = ccall(:jl_get_module_usings_backedges, Any, (Any,), gr.mod) + if usings_backedges !== nothing + for user in usings_backedges::Vector{Any} + user_binding = ccall(:jl_get_module_binding_or_nothing, Any, (Any, Any), user, gr.name) + user_binding === nothing && continue + isdefined(user_binding, :partitions) || continue + latest_bpart = user_binding.partitions + latest_bpart.max_world == typemax(UInt) || continue + is_some_imported(binding_kind(latest_bpart)) || continue + partition_restriction(latest_bpart) === b || continue + invalidate_code_for_globalref!(convert(Core.Binding, user_binding), latest_bpart, nothing, new_max_world) end end - catch err - bt = catch_backtrace() - invokelatest(Base.println, "Internal Error during invalidation:") - invokelatest(Base.display_error, err, bt) end end +invalidate_code_for_globalref!(gr::GlobalRef, invalidated_bpart::Core.BindingPartition, new_bpart::Core.BindingPartition, new_max_world::UInt) = + invalidate_code_for_globalref!(convert(Core.Binding, gr), invalidated_bpart, new_bpart, new_max_world) gr_needs_backedge_in_module(gr::GlobalRef, mod::Module) = gr.mod !== mod diff --git a/base/reflection.jl b/base/reflection.jl index 78e701692a2a76..c98f6244cc89f7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -46,92 +46,6 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= return ret end -# high-level, more convenient method lookup functions - -function visit(f, mt::Core.MethodTable) - mt.defs !== nothing && visit(f, mt.defs) - nothing -end -function visit(f, mc::Core.TypeMapLevel) - function avisit(f, e::Memory{Any}) - for i in 2:2:length(e) - isassigned(e, i) || continue - ei = e[i] - if ei isa Memory{Any} - for j in 2:2:length(ei) - isassigned(ei, j) || continue - visit(f, ei[j]) - end - else - visit(f, ei) - end - end - end - if mc.targ !== nothing - avisit(f, mc.targ::Memory{Any}) - end - if mc.arg1 !== nothing - avisit(f, mc.arg1::Memory{Any}) - end - if mc.tname !== nothing - avisit(f, mc.tname::Memory{Any}) - end - if mc.name1 !== nothing - avisit(f, mc.name1::Memory{Any}) - end - mc.list !== nothing && visit(f, mc.list) - mc.any !== nothing && visit(f, mc.any) - nothing -end -function visit(f, d::Core.TypeMapEntry) - while d !== nothing - f(d.func) - d = d.next - end - nothing -end -struct MethodSpecializations - specializations::Union{Nothing, Core.MethodInstance, Core.SimpleVector} -end -""" - specializations(m::Method) → itr - -Return an iterator `itr` of all compiler-generated specializations of `m`. -""" -specializations(m::Method) = MethodSpecializations(isdefined(m, :specializations) ? m.specializations : nothing) -function iterate(specs::MethodSpecializations) - s = specs.specializations - s === nothing && return nothing - isa(s, Core.MethodInstance) && return (s, nothing) - return iterate(specs, 0) -end -iterate(specs::MethodSpecializations, ::Nothing) = nothing -function iterate(specs::MethodSpecializations, i::Int) - s = specs.specializations::Core.SimpleVector - n = length(s) - i >= n && return nothing - item = nothing - while i < n && item === nothing - item = s[i+=1] - end - item === nothing && return nothing - return (item, i) -end -length(specs::MethodSpecializations) = count(Returns(true), specs) - -function length(mt::Core.MethodTable) - n = 0 - visit(mt) do m - n += 1 - end - return n::Int -end -isempty(mt::Core.MethodTable) = (mt.defs === nothing) - -uncompressed_ir(m::Method) = isdefined(m, :source) ? _uncompressed_ir(m) : - isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") : - error("Code for this Method is not available.") - # for backwards compat const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index b61e24c11f3f93..36961f58c5c3fa 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -177,28 +177,6 @@ ispublic(m::Module, s::Symbol) = ccall(:jl_module_public_p, Cint, (Any, Any), m, # `Base.deprecate`, not the @deprecated macro: isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0 -""" - isbindingresolved(m::Module, s::Symbol) -> Bool - -Returns whether the binding of a symbol in a module is resolved. - -See also: [`isexported`](@ref), [`ispublic`](@ref), [`isdeprecated`](@ref) - -```jldoctest -julia> module Mod - foo() = 17 - end -Mod - -julia> Base.isbindingresolved(Mod, :foo) -true - -julia> Base.isbindingresolved(Mod, :bar) -false -``` -""" -isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0 - function binding_module(m::Module, s::Symbol) p = ccall(:jl_get_module_of_binding, Ptr{Cvoid}, (Any, Any), m, s) p == C_NULL && return m @@ -234,7 +212,7 @@ const BINDING_KIND_BACKDATED_CONST = 0xa is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST) is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST) is_some_imported(kind::UInt8) = (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT || kind == BINDING_KIND_IMPORTED) -is_some_guard(kind::UInt8) = (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_FAILED || kind == BINDING_KIND_UNDEF_CONST) +is_some_guard(kind::UInt8) = (kind == BINDING_KIND_GUARD || kind == BINDING_KIND_FAILED || kind == BINDING_KIND_UNDEF_CONST) function lookup_binding_partition(world::UInt, b::Core.Binding) ccall(:jl_get_binding_partition, Ref{Core.BindingPartition}, (Any, UInt), b, world) @@ -412,8 +390,13 @@ parentmodule(t::UnionAll) = parentmodule(unwrap_unionall(t)) """ isconst(m::Module, s::Symbol) -> Bool + isconst(g::GlobalRef) -Determine whether a global is declared `const` in a given module `m`. +Determine whether a global is `const` in a given module `m`, either +because it was declared constant or because it was imported from a +constant binding. Note that constant-ness is specific to a particular +world age, so the result of this function may not be assumed to hold +after a world age update. """ isconst(m::Module, s::Symbol) = ccall(:jl_is_const, Cint, (Any, Any), m, s) != 0 @@ -1584,3 +1567,90 @@ hasintersect(@nospecialize(a), @nospecialize(b)) = typeintersect(a, b) !== Botto ########### _topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module + + +# high-level, more convenient method lookup functions + +function visit(f, mt::Core.MethodTable) + mt.defs !== nothing && visit(f, mt.defs) + nothing +end +function visit(f, mc::Core.TypeMapLevel) + function avisit(f, e::Memory{Any}) + for i in 2:2:length(e) + isassigned(e, i) || continue + ei = e[i] + if ei isa Memory{Any} + for j in 2:2:length(ei) + isassigned(ei, j) || continue + visit(f, ei[j]) + end + else + visit(f, ei) + end + end + end + if mc.targ !== nothing + avisit(f, mc.targ::Memory{Any}) + end + if mc.arg1 !== nothing + avisit(f, mc.arg1::Memory{Any}) + end + if mc.tname !== nothing + avisit(f, mc.tname::Memory{Any}) + end + if mc.name1 !== nothing + avisit(f, mc.name1::Memory{Any}) + end + mc.list !== nothing && visit(f, mc.list) + mc.any !== nothing && visit(f, mc.any) + nothing +end +function visit(f, d::Core.TypeMapEntry) + while d !== nothing + f(d.func) + d = d.next + end + nothing +end +struct MethodSpecializations + specializations::Union{Nothing, Core.MethodInstance, Core.SimpleVector} +end +""" + specializations(m::Method) → itr + +Return an iterator `itr` of all compiler-generated specializations of `m`. +""" +specializations(m::Method) = MethodSpecializations(isdefined(m, :specializations) ? m.specializations : nothing) +function iterate(specs::MethodSpecializations) + s = specs.specializations + s === nothing && return nothing + isa(s, Core.MethodInstance) && return (s, nothing) + return iterate(specs, 0) +end +iterate(specs::MethodSpecializations, ::Nothing) = nothing +function iterate(specs::MethodSpecializations, i::Int) + s = specs.specializations::Core.SimpleVector + n = length(s) + i >= n && return nothing + item = nothing + while i < n && item === nothing + item = s[i+=1] + end + item === nothing && return nothing + return (item, i) +end +length(specs::MethodSpecializations) = count(Returns(true), specs) + +function length(mt::Core.MethodTable) + n = 0 + visit(mt) do m + n += 1 + end + return n::Int +end +isempty(mt::Core.MethodTable) = (mt.defs === nothing) + +uncompressed_ir(m::Method) = isdefined(m, :source) ? _uncompressed_ir(m) : + isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") : + error("Code for this Method is not available.") diff --git a/base/show.jl b/base/show.jl index 42788f05eceb5d..6b27fe838c89bd 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1021,18 +1021,24 @@ end # If an object with this name exists in 'from', we need to check that it's the same binding # and that it's not deprecated. function isvisible(sym::Symbol, parent::Module, from::Module) - owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), parent, sym) - from_owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), from, sym) - return owner !== C_NULL && from_owner === owner && - !isdeprecated(parent, sym) && - isdefinedglobal(from, sym) # if we're going to return true, force binding resolution + isdeprecated(parent, sym) && return false + isdefinedglobal(from, sym) || return false + parent_binding = convert(Core.Binding, GlobalRef(parent, sym)) + from_binding = convert(Core.Binding, GlobalRef(from, sym)) + while true + from_binding === parent_binding && return true + partition = lookup_binding_partition(tls_world_age(), from_binding) + is_some_imported(binding_kind(partition)) || break + from_binding = partition_restriction(partition)::Core.Binding + end + return false end function is_global_function(tn::Core.TypeName, globname::Union{Symbol,Nothing}) if globname !== nothing globname_str = string(globname::Symbol) if ('#' ∉ globname_str && '@' ∉ globname_str && isdefined(tn, :module) && - isbindingresolved(tn.module, globname) && isdefinedglobal(tn.module, globname) && + isdefinedglobal(tn.module, globname) && isconcretetype(tn.wrapper) && isa(getglobal(tn.module, globname), tn.wrapper)) return true end @@ -3377,16 +3383,16 @@ function print_partition(io::IO, partition::Core.BindingPartition) elseif kind == BINDING_KIND_FAILED print(io, "ambiguous binding - guard entry") elseif kind == BINDING_KIND_DECLARED - print(io, "undefined, but declared using `global` - guard entry") + print(io, "weak global binding declared using `global` (implicit type Any)") elseif kind == BINDING_KIND_IMPLICIT print(io, "implicit `using` from ") - print(io, partition_restriction(partition)) + print(io, partition_restriction(partition).globalref) elseif kind == BINDING_KIND_EXPLICIT print(io, "explicit `using` from ") - print(io, partition_restriction(partition)) + print(io, partition_restriction(partition).globalref) elseif kind == BINDING_KIND_IMPORTED print(io, "explicit `import` from ") - print(io, partition_restriction(partition)) + print(io, partition_restriction(partition).globalref) else @assert kind == BINDING_KIND_GLOBAL print(io, "global variable with type ") diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index b075223d9c7e4a..f12e94e22fca5d 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -107,6 +107,9 @@ precompile(Base.CoreLogging.env_override_minlevel, (Symbol, Module)) precompile(Base.StackTraces.lookup, (Ptr{Nothing},)) precompile(Tuple{typeof(Base.run_module_init), Module, Int}) +# Presence tested in the tests +precompile(Tuple{typeof(Base.print), Base.IOStream, String}) + # precompilepkgs precompile(Tuple{typeof(Base.get), Type{Array{String, 1}}, Base.Dict{String, Any}, String}) precompile(Tuple{typeof(Base.get), Type{Base.Dict{String, Any}}, Base.Dict{String, Any}, String}) @@ -352,10 +355,10 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe PrecompileStagingArea = Module() for (_pkgid, _mod) in Base.loaded_modules if !(_pkgid.name in ("Main", "Core", "Base")) - eval(PrecompileStagingArea, :(const $(Symbol(_mod)) = $_mod)) + Core.eval(PrecompileStagingArea, :(const $(Symbol(_mod)) = $_mod)) end end - eval(PrecompileStagingArea, :(const Compiler = Base.Compiler)) + Core.eval(PrecompileStagingArea, :(const Compiler = Base.Compiler)) n_succeeded = 0 # Make statements unique diff --git a/src/builtins.c b/src/builtins.c index f67ef65d353569..ecbf0a229270f5 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -2197,11 +2197,13 @@ static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layou JL_CALLABLE(jl_f__typebody) { - JL_NARGS(_typebody!, 1, 2); - jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(args[0]); + JL_NARGS(_typebody!, 2, 3); + jl_value_t *prev = args[0]; + jl_value_t *tret = args[1]; + jl_datatype_t *dt = (jl_datatype_t*)jl_unwrap_unionall(args[1]); JL_TYPECHK(_typebody!, datatype, (jl_value_t*)dt); - if (nargs == 2) { - jl_value_t *ft = args[1]; + if (nargs == 3) { + jl_value_t *ft = args[2]; JL_TYPECHK(_typebody!, simplevector, ft); size_t nf = jl_svec_len(ft); for (size_t i = 0; i < nf; i++) { @@ -2212,30 +2214,52 @@ JL_CALLABLE(jl_f__typebody) (jl_value_t*)jl_type_type, elt); } } - if (dt->types != NULL) { - if (!equiv_field_types((jl_value_t*)dt->types, ft)) - jl_errorf("invalid redefinition of type %s", jl_symbol_name(dt->name->name)); - } - else { - dt->types = (jl_svec_t*)ft; - jl_gc_wb(dt, ft); - // If a supertype can reference the same type, then we may not be - // able to compute the layout of the object before needing to - // publish it, so we must assume it cannot be inlined, if that - // check passes, then we also still need to check the fields too. - if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 0, 1))) { - int mayinlinealloc = 1; - size_t i; - for (i = 0; i < nf; i++) { - jl_value_t *fld = jl_svecref(ft, i); - if (references_name(fld, dt->name, 1, 1)) { - mayinlinealloc = 0; - break; + // Optimization: To avoid lots of unnecessary churning, lowering contains an optimization + // that re-uses the typevars of an existing definition (if any exists) for compute the field + // types. If such a previous type exists, there are two possibilities: + // 1. The field types are identical, we don't need to do anything and can proceed with the + // old type as if it was the new one. + // 2. The field types are not identical, in which case we need to rename the typevars + // back to their equivalents in the new type before proceeding. + if (prev == jl_false) { + if (dt->types != NULL) + jl_errorf("Internal Error: Expected type fields to be unset"); + } else { + jl_datatype_t *prev_dt = (jl_datatype_t*)jl_unwrap_unionall(prev); + JL_TYPECHK(_typebody!, datatype, (jl_value_t*)prev_dt); + if (equiv_field_types((jl_value_t*)prev_dt->types, ft)) { + tret = prev; + goto have_type; + } else { + if (jl_svec_len(prev_dt->parameters) != jl_svec_len(dt->parameters)) + jl_errorf("Internal Error: Types should not have been considered equivalent"); + for (size_t i = 0; i < nf; i++) { + jl_value_t *elt = jl_svecref(ft, i); + for (int j = 0; j < jl_svec_len(prev_dt->parameters); ++j) { + // Only the last svecset matters for semantics, but we re-use the GC root + jl_svecset(ft, i, jl_substitute_var(elt, (jl_tvar_t *)jl_svecref(prev_dt->parameters, j), jl_svecref(dt->parameters, j))); } } - dt->name->mayinlinealloc = mayinlinealloc; } } + dt->types = (jl_svec_t*)ft; + jl_gc_wb(dt, ft); + // If a supertype can reference the same type, then we may not be + // able to compute the layout of the object before needing to + // publish it, so we must assume it cannot be inlined, if that + // check passes, then we also still need to check the fields too. + if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 0, 1))) { + int mayinlinealloc = 1; + size_t i; + for (i = 0; i < nf; i++) { + jl_value_t *fld = jl_svecref(ft, i); + if (references_name(fld, dt->name, 1, 1)) { + mayinlinealloc = 0; + break; + } + } + dt->name->mayinlinealloc = mayinlinealloc; + } } JL_TRY { @@ -2248,7 +2272,8 @@ JL_CALLABLE(jl_f__typebody) if (jl_is_structtype(dt)) jl_compute_field_offsets(dt); - return jl_nothing; +have_type: + return tret; } // this is a heuristic for allowing "redefining" a type to something identical diff --git a/src/codegen.cpp b/src/codegen.cpp index e9e4275672c7e6..ddbac31badd7b9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -918,7 +918,7 @@ static const auto jldeclareglobal_func = new JuliaFunction<>{ auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); return FunctionType::get(getVoidTy(C), - {T_pjlvalue, T_pjlvalue, T_prjlvalue}, false); }, + {T_pjlvalue, T_pjlvalue, T_prjlvalue, getInt32Ty(C)}, false); }, nullptr, }; static const auto jlgetbindingorerror_func = new JuliaFunction<>{ @@ -6958,16 +6958,21 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ } } else if (head == jl_globaldecl_sym) { - assert(nargs == 2); + assert(nargs <= 2 && nargs >= 1); jl_sym_t *sym = (jl_sym_t*)args[0]; jl_module_t *mod = ctx.module; if (jl_is_globalref(sym)) { mod = jl_globalref_mod(sym); sym = jl_globalref_name(sym); } - jl_cgval_t typ = emit_expr(ctx, args[1]); - ctx.builder.CreateCall(prepare_call(jldeclareglobal_func), - { literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, typ) }); + if (nargs == 2) { + jl_cgval_t typ = emit_expr(ctx, args[1]); + ctx.builder.CreateCall(prepare_call(jldeclareglobal_func), + { literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), boxed(ctx, typ), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 1) }); + } else { + ctx.builder.CreateCall(prepare_call(jldeclareglobal_func), + { literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym), ConstantPointerNull::get(cast(ctx.types().T_prjlvalue)), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 1) }); + } } else if (head == jl_new_sym) { bool is_promotable = false; diff --git a/src/gc-stock.c b/src/gc-stock.c index 8118b3c5629ae9..e7ce48c1730b4f 100644 --- a/src/gc-stock.c +++ b/src/gc-stock.c @@ -2144,6 +2144,8 @@ STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent gc_heap_snapshot_record_module_to_binding(mb_parent, bindings, bindingkeyset); gc_assert_parent_validity((jl_value_t *)mb_parent, (jl_value_t *)mb_parent->parent); gc_try_claim_and_push(mq, (jl_value_t *)mb_parent->parent, &nptr); + gc_assert_parent_validity((jl_value_t *)mb_parent, (jl_value_t *)mb_parent->usings_backedges); + gc_try_claim_and_push(mq, (jl_value_t *)mb_parent->usings_backedges, &nptr); size_t nusings = module_usings_length(mb_parent); if (nusings > 0) { // this is only necessary because bindings for "using" modules diff --git a/src/interpreter.c b/src/interpreter.c index 35c70a9ead2f1a..513fe58f7b5ccd 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -634,9 +634,12 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, s->locals[jl_source_nslots(s->src) + s->ip] = res; } else if (head == jl_globaldecl_sym) { - jl_value_t *val = eval_value(jl_exprarg(stmt, 1), s); - s->locals[jl_source_nslots(s->src) + s->ip] = val; // temporarily root - jl_declare_global(s->module, jl_exprarg(stmt, 0), val); + jl_value_t *val = NULL; + if (jl_expr_nargs(stmt) >= 2) { + val = eval_value(jl_exprarg(stmt, 1), s); + s->locals[jl_source_nslots(s->src) + s->ip] = val; // temporarily root + } + jl_declare_global(s->module, jl_exprarg(stmt, 0), val, 1); s->locals[jl_source_nslots(s->src) + s->ip] = jl_nothing; } else if (head == jl_const_sym) { diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 4d1ab94644e39d..4454ffb4cca3f8 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -42,7 +42,6 @@ XX(jl_atomic_swap_bits) \ XX(jl_backtrace_from_here) \ XX(jl_base_relative_to) \ - XX(jl_binding_resolved_p) \ XX(jl_bitcast) \ XX(jl_boundp) \ XX(jl_bounds_error) \ @@ -72,6 +71,7 @@ XX(jl_call1) \ XX(jl_call2) \ XX(jl_call3) \ + XX(jl_call4) \ XX(jl_calloc) \ XX(jl_call_in_typeinf_world) \ XX(jl_capture_interp_frame) \ @@ -213,6 +213,8 @@ XX(jl_get_module_infer) \ XX(jl_get_module_of_binding) \ XX(jl_get_module_optlevel) \ + XX(jl_get_module_usings_backedges) \ + XX(jl_get_module_binding_or_nothing) \ XX(jl_get_next_task) \ XX(jl_get_nth_field) \ XX(jl_get_nth_field_checked) \ diff --git a/src/jlapi.c b/src/jlapi.c index b8fbda801f43b6..e205a4a4dc723c 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -437,6 +437,46 @@ JL_DLLEXPORT jl_value_t *jl_call3(jl_function_t *f, jl_value_t *a, return v; } +/** + * @brief Call a Julia function with three arguments. + * + * A specialized case of `jl_call` for simpler scenarios. + * + * @param f A pointer to `jl_function_t` representing the Julia function to call. + * @param a A pointer to `jl_value_t` representing the first argument. + * @param b A pointer to `jl_value_t` representing the second argument. + * @param c A pointer to `jl_value_t` representing the third argument. + * @param d A pointer to `jl_value_t` representing the fourth argument. + * @return A pointer to `jl_value_t` representing the result of the function call. + */ +JL_DLLEXPORT jl_value_t *jl_call4(jl_function_t *f, jl_value_t *a, + jl_value_t *b, jl_value_t *c, + jl_value_t *d) +{ + jl_value_t *v; + jl_task_t *ct = jl_current_task; + JL_TRY { + jl_value_t **argv; + JL_GC_PUSHARGS(argv, 5); + argv[0] = f; + argv[1] = a; + argv[2] = b; + argv[3] = c; + argv[4] = d; + size_t last_age = ct->world_age; + ct->world_age = jl_get_world_counter(); + v = jl_apply(argv, 5); + ct->world_age = last_age; + JL_GC_POP(); + _jl_exception_clear(ct); + } + JL_CATCH { + ct->ptls->previous_exception = jl_current_exception(ct); + v = NULL; + } + return v; +} + /** * @brief Get a field from a Julia object. * diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 97d76e7762a9e0..726312a13d10f7 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1002,7 +1002,9 @@ (default-inner-ctors name field-names field-types params bounds locs) defs)) (min-initialized (min (ctors-min-initialized defs) (length fields))) - (prev (make-ssavalue))) + (hasprev (make-ssavalue)) + (prev (make-ssavalue)) + (newdef (make-ssavalue))) (let ((dups (has-dups field-names))) (if dups (error (string "duplicate field name: \"" (car dups) "\" is not unique")))) (for-each (lambda (v) @@ -1023,22 +1025,21 @@ (call (core svec) ,@attrs) ,mut ,min-initialized)) (call (core _setsuper!) ,name ,super) - (if (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) - (block - (= ,prev (globalref (thismodule) ,name)) - (if (call (core _equiv_typedef) ,prev ,name) - ;; if this is compatible with an old definition, use the existing type object - ;; and its parameters - (block (= ,name ,prev) - ,@(if (pair? params) - `((= (tuple ,@params) (|.| - ,(foldl (lambda (_ x) `(|.| ,x (quote body))) - prev - params) - (quote parameters)))) - '()))))) - (call (core _typebody!) ,name (call (core svec) ,@(insert-struct-shim field-types name))) - (const (globalref (thismodule) ,name) ,name) + (= ,hasprev (&& (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name))) + (= ,prev (if ,hasprev (globalref (thismodule) ,name) (false))) + (if ,hasprev + ;; if this is compatible with an old definition, use the old parameters, but the + ;; new object. This will fail to capture recursive cases, but the call to typebody! + ;; below is permitted to choose either type definition to put into the binding table + (block ,@(if (pair? params) + `((= (tuple ,@params) (|.| + ,(foldl (lambda (_ x) `(|.| ,x (quote body))) + prev + params) + (quote parameters)))) + '()))) + (= ,newdef (call (core _typebody!) ,prev ,name (call (core svec) ,@(insert-struct-shim field-types name)))) + (const (globalref (thismodule) ,name) ,newdef) (latestworld) (null))) ;; "inner" constructors @@ -1084,7 +1085,7 @@ (toplevel-only abstract_type) (= ,name (call (core _abstracttype) (thismodule) (inert ,name) (call (core svec) ,@params))) (call (core _setsuper!) ,name ,super) - (call (core _typebody!) ,name) + (call (core _typebody!) (false) ,name) (if (&& (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name)) (null) @@ -1105,7 +1106,7 @@ (toplevel-only primitive_type) (= ,name (call (core _primitivetype) (thismodule) (inert ,name) (call (core svec) ,@params) ,n)) (call (core _setsuper!) ,name ,super) - (call (core _typebody!) ,name) + (call (core _typebody!) (false) ,name) (if (&& (call (core isdefinedglobal) (thismodule) (inert ,name) (false)) (call (core _equiv_typedef) (globalref (thismodule) ,name) ,name)) (null) @@ -3525,7 +3526,7 @@ f(x) = yt(x) (false) ,(length fields))) (call (core _setsuper!) ,s ,super) (const (globalref (thismodule) ,name) ,s) - (call (core _typebody!) ,s (call (core svec) ,@types)) + (call (core _typebody!) (false) ,s (call (core svec) ,@types)) (return (null))))))))) (define (type-for-closure name fields super) @@ -3539,7 +3540,7 @@ f(x) = yt(x) (false) ,(length fields))) (call (core _setsuper!) ,s ,super) (const (globalref (thismodule) ,name) ,s) - (call (core _typebody!) ,s + (call (core _typebody!) (false) ,s (call (core svec) ,@(map (lambda (v) '(core Box)) fields))) (return (null))))))))) @@ -3641,8 +3642,8 @@ f(x) = yt(x) rhs1)) (ex `(= ,var ,rhs))) (if (eq? rhs1 rhs0) - `(block ,ex ,rhs0) - `(block (= ,rhs1 ,rhs0) + `(block (globaldecl ,var) ,ex ,rhs0) + `(block (globaldecl ,var) (= ,rhs1 ,rhs0) ,ex ,rhs1)))) @@ -4624,11 +4625,7 @@ f(x) = yt(x) tests)) (define (emit-assignment-or-setglobal lhs rhs) (if (globalref? lhs) - (begin - (emit `(global ,lhs)) - (if (null? (cadr lam)) - (emit `(latestworld))) - (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))) + (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs)) (emit `(= ,lhs ,rhs)))) (define (emit-assignment lhs rhs) (if rhs @@ -4951,11 +4948,12 @@ f(x) = yt(x) (emit `(latestworld)))) ((globaldecl) (if value (error "misplaced \"global\" declaration")) - (if (atom? (caddr e)) (begin (emit e) (emit `(latestworld))) + (if (or (length= e 2) (atom? (caddr e))) (emit e) (let ((rr (make-ssavalue))) (emit `(= ,rr ,(caddr e))) - (emit `(globaldecl ,(cadr e) ,rr)) - (emit `(latestworld))))) + (emit `(globaldecl ,(cadr e) ,rr)))) + (if (null? (cadr lam)) + (emit `(latestworld)))) ((local-def) #f) ((local) #f) ((moved-local) diff --git a/src/julia.h b/src/julia.h index a80a69049ccb2e..fe0c42c2e740b3 100644 --- a/src/julia.h +++ b/src/julia.h @@ -616,6 +616,52 @@ typedef struct _jl_weakref_t { } jl_weakref_t; // N.B: Needs to be synced with runtime_internals.jl +// We track essentially three levels of binding strength: +// +// 1. Implicit Bindings (Weakest) +// These binding kinds depend solely on the set of using'd packages and are not explicitly +// declared: +// +// BINDING_KIND_IMPLICIT +// BINDING_KIND_GUARD +// BINDING_KIND_FAILED +// +// 2. Weakly Declared Bindings (Weak) +// The binding was declared using `global`. It is treated as a mutable, `Any` type global +// for almost all purposes, except that it receives slightly worse optimizations, since it +// may be replaced. +// +// BINDING_KIND_DECLARED +// +// 3. Strong Declared Bindings (Weak) +// All other bindings are explicitly declared using a keyword or global assignment. +// These are considered strongest: +// +// BINDING_KIND_CONST +// BINDING_KIND_CONST_IMPORT +// BINDING_KIND_EXPLICIT +// BINDING_KIND_IMPORTED +// BINDING_KIND_GLOBAL +// BINDING_KIND_UNDEF_CONST +// +// The runtime supports syntactic invalidation (by raising the world age and changing the partition type +// in the new world age) from any partition kind to any other. +// +// However, not all transitions are allowed syntactically. We have the following rules for SYNTACTIC invalidation: +// 1. It is always syntactically permissable to replace a weaker binding by a stronger binding +// 2. Implicit bindings can be syntactically changed other other implicit bindings by chaing the `using` set. +// 3. Finally, we syntactically permit replacing one BINDING_KIND_CONST(_IMPORT) by another of a different value. +// +// We may make this list more permissive in the future. +// +// Finally, BINDING_KIND_BACKDATED_CONST is a special case, and the only case where we may replace an +// existing partition by a different partition kind in the same world age. As such, it needs special +// support in inference. Any partition kind that may be replaced by a BINDING_KIND_BACKDATED_CONST +// must be inferred accordingly. BINDING_KIND_BACKDATED_CONST is intended as a temporary compatibility +// measure. The following kinds may be replaced by BINDING_KIND_BACKDATED_CONST: +// - BINDING_KIND_GUARD +// - BINDING_KIND_FAILED +// - BINDING_KIND_DECLARED enum jl_partition_kind { // Constant: This binding partition is a constant declared using `const _ = ...` // ->restriction holds the constant value @@ -623,7 +669,8 @@ enum jl_partition_kind { // Import Constant: This binding partition is a constant declared using `import A` // ->restriction holds the constant value BINDING_KIND_CONST_IMPORT = 0x1, - // Global: This binding partition is a global variable. + // Global: This binding partition is a global variable. It was declared either using + // `global x::T` to implicitly through a syntactic global assignment. // -> restriction holds the type restriction BINDING_KIND_GLOBAL = 0x2, // Implicit: The binding was implicitly imported from a `using`'d module. @@ -638,7 +685,9 @@ enum jl_partition_kind { // Failed: We attempted to import the binding, but the import was ambiguous // ->restriction is NULL. BINDING_KIND_FAILED = 0x6, - // Declared: The binding was declared using `global` or similar + // Declared: The binding was declared using `global` or similar. This acts in most ways like + // BINDING_KIND_GLOBAL with an `Any` restriction, except that it may be redefined to a stronger + // binding like `const` or an explicit import. // ->restriction is NULL. BINDING_KIND_DECLARED = 0x7, // Guard: The binding was looked at, but no global or import was resolved at the time @@ -651,6 +700,10 @@ enum jl_partition_kind { // Backated constant. A constant that was backdated for compatibility. In all other // ways equivalent to BINDING_KIND_CONST, but prints a warning on access BINDING_KIND_BACKDATED_CONST = 0xa, + + // This is not a real binding kind, but can be used to ask for a re-resolution + // of the implicit binding kind + BINDING_KIND_IMPLICIT_RECOMPUTE = 0xb }; #ifdef _P64 @@ -672,17 +725,7 @@ typedef struct __attribute__((aligned(8))) _jl_binding_partition_t { * * Currently: Low 3 bits hold ->kind on _P64 to avoid needing >8 byte atomics * - * This field is updated atomically with both kind and restriction. The following - * transitions are allowed and modeled by the system: - * - * GUARD -> any - * (DECLARED, FAILED) -> any non-GUARD - * IMPLICIT -> {EXPLICIT, IMPORTED} (->restriction unchanged only) - * - * In addition, we permit (with warning about undefined behavior) changing the restriction - * pointer for CONST(_IMPORT). - * - * All other kind or restriction transitions are disallowed. + * This field is updated atomically with both kind and restriction */ _Atomic(jl_ptr_kind_union_t) restriction; size_t min_world; @@ -717,6 +760,7 @@ typedef struct _jl_module_t { _Atomic(jl_genericmemory_t*) bindingkeyset; // index lookup by name into bindings jl_sym_t *file; int32_t line; + jl_value_t *usings_backedges; // hidden fields: arraylist_t usings; /* arraylist of struct jl_module_using */ // modules with all bindings potentially imported jl_uuid_t build_id; @@ -1996,6 +2040,9 @@ JL_DLLEXPORT void jl_set_module_infer(jl_module_t *self, int value); JL_DLLEXPORT int jl_get_module_infer(jl_module_t *m); JL_DLLEXPORT void jl_set_module_max_methods(jl_module_t *self, int value); JL_DLLEXPORT int jl_get_module_max_methods(jl_module_t *m); +JL_DLLEXPORT jl_value_t *jl_get_module_usings_backedges(jl_module_t *m); +JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym_t *s); + // get binding for reading JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var); @@ -2007,7 +2054,6 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, 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); -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 jl_value_t *jl_get_globalref_value(jl_globalref_t *gr); @@ -2253,6 +2299,9 @@ JL_DLLEXPORT jl_value_t *jl_call1(jl_function_t *f JL_MAYBE_UNROOTED, jl_value_t JL_DLLEXPORT jl_value_t *jl_call2(jl_function_t *f JL_MAYBE_UNROOTED, jl_value_t *a JL_MAYBE_UNROOTED, jl_value_t *b JL_MAYBE_UNROOTED); JL_DLLEXPORT jl_value_t *jl_call3(jl_function_t *f JL_MAYBE_UNROOTED, jl_value_t *a JL_MAYBE_UNROOTED, jl_value_t *b JL_MAYBE_UNROOTED, jl_value_t *c JL_MAYBE_UNROOTED); +JL_DLLEXPORT jl_value_t *jl_call4(jl_function_t *f JL_MAYBE_UNROOTED, jl_value_t *a JL_MAYBE_UNROOTED, + jl_value_t *b JL_MAYBE_UNROOTED, jl_value_t *c JL_MAYBE_UNROOTED, + jl_value_t *d JL_MAYBE_UNROOTED); // async signal handling ------------------------------------------------------ diff --git a/src/julia_internal.h b/src/julia_internal.h index 9817c8cc8263b1..9c0b7e8029bf3c 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -843,7 +843,7 @@ int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val); void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty); void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); -JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type); +JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong); 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); @@ -905,6 +905,8 @@ void jl_compute_field_offsets(jl_datatype_t *st); void jl_module_run_initializer(jl_module_t *m); JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); JL_DLLEXPORT void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *sym, jl_binding_t *b); +JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b JL_PROPAGATES_ROOT, + jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) JL_GLOBALLY_ROOTED; extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED; extern htable_t jl_current_modules JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_module_t *jl_precompile_toplevel_module JL_GLOBALLY_ROOTED; @@ -991,7 +993,7 @@ STATIC_INLINE int jl_bkind_is_defined_constant(enum jl_partition_kind kind) JL_N } STATIC_INLINE int jl_bkind_is_some_guard(enum jl_partition_kind kind) JL_NOTSAFEPOINT { - return kind == BINDING_KIND_FAILED || kind == BINDING_KIND_GUARD || kind == BINDING_KIND_DECLARED; + return kind == BINDING_KIND_FAILED || kind == BINDING_KIND_GUARD; } JL_DLLEXPORT jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world) JL_GLOBALLY_ROOTED; diff --git a/src/method.c b/src/method.c index 8a14eb00182b1c..1b38a16649d8a1 100644 --- a/src/method.c +++ b/src/method.c @@ -62,6 +62,11 @@ static jl_value_t *resolve_definition_effects(jl_value_t *expr, jl_module_t *mod jl_eval_global_expr(module, e, 1); return jl_nothing; } + if (e->head == jl_globaldecl_sym && binding_effects) { + assert(jl_expr_nargs(e) == 1); + jl_declare_global(module, jl_exprarg(e, 0), NULL, 1); + return jl_nothing; + } // These exprs are not fully linearized if (e->head == jl_assign_sym) { jl_exprargset(e, 1, resolve_definition_effects(jl_exprarg(e, 1), module, sparam_vals, binding_edge, binding_effects, eager_resolve)); @@ -180,26 +185,24 @@ static jl_value_t *resolve_definition_effects(jl_value_t *expr, jl_module_t *mod jl_value_t *fe = jl_exprarg(e, 0); jl_module_t *fe_mod = jl_globalref_mod(fe); jl_sym_t *fe_sym = jl_globalref_name(fe); - if (jl_binding_resolved_p(fe_mod, fe_sym)) { - // look at some known called functions - jl_binding_t *b = jl_get_binding(fe_mod, fe_sym); - if (jl_get_binding_value_if_const(b) == jl_builtin_tuple) { - size_t j; - for (j = 1; j < nargs; j++) { - if (!jl_is_quotenode(jl_exprarg(e, j))) - break; + // look at some known called functions + jl_binding_t *b = jl_get_binding(fe_mod, fe_sym); + if (jl_get_binding_value_if_const(b) == jl_builtin_tuple) { + size_t j; + for (j = 1; j < nargs; j++) { + if (!jl_is_quotenode(jl_exprarg(e, j))) + break; + } + if (j == nargs) { + jl_value_t *val = NULL; + JL_TRY { + val = jl_interpret_toplevel_expr_in(module, (jl_value_t*)e, NULL, sparam_vals); } - if (j == nargs) { - jl_value_t *val = NULL; - JL_TRY { - val = jl_interpret_toplevel_expr_in(module, (jl_value_t*)e, NULL, sparam_vals); - } - JL_CATCH { - val = NULL; // To make the analyzer happy see #define JL_TRY - } - if (val) - return val; + JL_CATCH { + val = NULL; // To make the analyzer happy see #define JL_TRY } + if (val) + return val; } } } @@ -1052,9 +1055,11 @@ JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_binding_t *b, jl_module_t *mod, 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_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); jl_value_t *gf = NULL; - if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (!jl_bkind_is_some_guard(kind) && kind != BINDING_KIND_DECLARED && kind != BINDING_KIND_IMPLICIT) { + pku = jl_walk_binding_inplace(&b, &bpart, new_world); if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) { gf = decode_restriction_value(pku); JL_GC_PROMISE_ROOTED(gf); diff --git a/src/module.c b/src/module.c index b2a4018519fca6..0783a7ba44ebdb 100644 --- a/src/module.c +++ b/src/module.c @@ -29,7 +29,134 @@ static jl_binding_partition_t *new_binding_partition(void) return bpart; } -jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) { + +typedef struct _modstack_t { + jl_binding_t *b; + struct _modstack_t *prev; +} modstack_t; + +static jl_binding_partition_t *jl_get_binding_partition2(jl_binding_t *b, size_t world, modstack_t *st); + +static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_t world) +{ + jl_binding_t *ownerb = NULL; + jl_binding_partition_t *alias_bpart = jl_get_binding_partition(alias, world); + if (owner == alias_bpart) + return 1; + jl_ptr_kind_union_t owner_pku = jl_walk_binding_inplace(&ownerb, &owner, world); + jl_ptr_kind_union_t alias_pku = jl_walk_binding_inplace(&alias, &alias_bpart, world); + if (jl_bkind_is_some_constant(decode_restriction_kind(owner_pku)) && + jl_bkind_is_some_constant(decode_restriction_kind(alias_pku)) && + decode_restriction_value(owner_pku) && + decode_restriction_value(alias_pku) == decode_restriction_value(owner_pku)) + return 1; + return owner == alias_bpart; +} + +// find a binding from a module's `usings` list +static void check_new_binding_implicit( + jl_binding_partition_t *new_bpart JL_MAYBE_UNROOTED, jl_binding_t *b, modstack_t *st, int warn, size_t world) +{ + modstack_t top = { b, st }; + modstack_t *tmp = st; + for (; tmp != NULL; tmp = tmp->prev) { + if (tmp->b == b) { + jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED /* BINDING_KIND_CYCLE */)); + return; + } + } + + JL_GC_PUSH1(&new_bpart); + jl_module_t *m = b->globalref->mod; + jl_sym_t *var = b->globalref->name; + + jl_binding_t *deprecated_impb = NULL; + jl_binding_t *impb = NULL; + + size_t min_world = new_bpart->min_world; + size_t max_world = jl_atomic_load_relaxed(&new_bpart->max_world); + + JL_LOCK(&m->lock); + int i = (int)module_usings_length(m) - 1; + JL_UNLOCK(&m->lock); + enum jl_partition_kind guard_kind = BINDING_KIND_GUARD; + for (; i >= 0; --i) { + JL_LOCK(&m->lock); + struct _jl_module_using data = *module_usings_getidx(m, i); + JL_UNLOCK(&m->lock); + if (data.min_world > world) { + if (max_world > data.min_world) + max_world = data.min_world - 1; + continue; + } + if (data.max_world < world) { + if (min_world < data.max_world) + min_world = data.max_world + 1; + continue; + } + jl_module_t *imp = data.mod; + JL_GC_PROMISE_ROOTED(imp); + jl_binding_t *tempb = jl_get_module_binding(imp, var, 0); + if (tempb != NULL && tempb->exportp) { + if (data.min_world > min_world) + min_world = data.min_world; + if (data.max_world < min_world) + max_world = data.max_world; + + jl_binding_partition_t *tempbpart = jl_get_binding_partition2(tempb, world, &top); + JL_GC_PROMISE_ROOTED(tempbpart); + + size_t tempbmax_world = jl_atomic_load_relaxed(&tempbpart->max_world); + if (tempbpart->min_world > min_world) + min_world = tempbpart->min_world; + if (tempbmax_world < max_world) + max_world = tempbmax_world; + + if (impb) { + if (eq_bindings(tempbpart, impb, world)) + continue; + // Binding is ambiguous + // TODO: Even for eq bindings, this may need to further constrain the world age. + impb = NULL; + guard_kind = BINDING_KIND_FAILED; + break; + } + + if (tempb->deprecated) { + if (deprecated_impb) { + if (!eq_bindings(tempbpart, deprecated_impb, world)) { + guard_kind = BINDING_KIND_FAILED; + deprecated_impb = NULL; + } + } + else if (guard_kind == BINDING_KIND_GUARD) { + deprecated_impb = tempb; + } + } + else { + impb = tempb; + } + } + } + + if (deprecated_impb && !impb) + impb = deprecated_impb; + + assert(min_world <= max_world); + new_bpart->min_world = min_world; + jl_atomic_store_relaxed(&new_bpart->max_world, max_world); + if (impb) { + jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction((jl_value_t*)impb, BINDING_KIND_IMPLICIT)); + // TODO: World age constraints? + } else { + jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction(NULL, guard_kind)); + } + JL_GC_POP(); + return; +} + +STATIC_INLINE jl_binding_partition_t *jl_get_binding_partition_(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world, modstack_t *st) JL_GLOBALLY_ROOTED +{ if (!b) return NULL; assert(jl_is_binding(b)); @@ -53,6 +180,8 @@ jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) jl_gc_wb_fresh(new_bpart, bpart); new_bpart->min_world = bpart ? jl_atomic_load_relaxed(&bpart->max_world) + 1 : 0; jl_atomic_store_relaxed(&new_bpart->max_world, max_world); + JL_GC_PROMISE_ROOTED(new_bpart); // TODO: Analyzer doesn't understand MAYBE_UNROOTED properly + check_new_binding_implicit(new_bpart, b, st, 0, world); if (jl_atomic_cmpswap(insert, &bpart, new_bpart)) { jl_gc_wb(parent, new_bpart); return new_bpart; @@ -60,6 +189,15 @@ jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) } } +jl_binding_partition_t *jl_get_binding_partition(jl_binding_t *b, size_t world) { + // Duplicate the code for the entry frame for branch prediction + return jl_get_binding_partition_(b, world, NULL); +} + +jl_binding_partition_t *jl_get_binding_partition2(jl_binding_t *b JL_PROPAGATES_ROOT, size_t world, modstack_t *st) JL_GLOBALLY_ROOTED { + return jl_get_binding_partition_(b, world, st); +} + jl_binding_partition_t *jl_get_binding_partition_all(jl_binding_t *b, size_t min_world, size_t max_world) { if (!b) return NULL; @@ -89,6 +227,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui m->build_id.lo++; // build id 0 is invalid m->build_id.hi = ~(uint64_t)0; jl_atomic_store_relaxed(&m->counter, 1); + m->usings_backedges = jl_nothing; m->nospecialize = 0; m->optlevel = -1; m->compile = -1; @@ -255,7 +394,7 @@ extern jl_mutex_t jl_modules_mutex; extern void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) { if (jl_current_task->ptls->in_pure_callback) - jl_errorf("new globals cannot be created in a generated function"); + jl_errorf("new strong globals cannot be created in a generated function. Delare them outside using `global x::Any`."); if (jl_options.incremental && jl_generating_output()) { JL_LOCK(&jl_modules_mutex); int open = ptrhash_has(&jl_current_modules, (void*)m); @@ -285,20 +424,14 @@ JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module { 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) { - 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); + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (kind != BINDING_KIND_GLOBAL && kind != BINDING_KIND_DECLARED && !jl_bkind_is_some_constant(kind)) { + if (jl_bkind_is_some_guard(kind)) { + 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)); } else { jl_module_t *from = jl_binding_dbgmodule(b, m, s); if (from == m) @@ -321,10 +454,10 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, // return module of binding JL_DLLEXPORT jl_module_t *jl_get_module_of_binding(jl_module_t *m, jl_sym_t *var) { - jl_binding_t *b = jl_get_binding(m, var); - if (b == NULL) - return NULL; - return b->globalref->mod; // TODO: deprecate this? + 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_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + return b ? b->globalref->mod : m; } static NOINLINE void print_backdate_admonition(jl_binding_t *b) JL_NOTSAFEPOINT @@ -358,6 +491,7 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b) check_backdated_binding(b, kind); return decode_restriction_value(pku); } + assert(!jl_bkind_is_some_import(kind)); return jl_atomic_load_relaxed(&b->value); } @@ -372,6 +506,7 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b) check_backdated_binding(b, kind); return decode_restriction_value(pku); } + assert(!jl_bkind_is_some_import(kind)); return jl_atomic_load(&b->value); } @@ -444,19 +579,14 @@ JL_DLLEXPORT jl_value_t *jl_bpart_get_restriction_value(jl_binding_partition_t * return v; } -typedef struct _modstack_t { - jl_module_t *m; - 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, 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_current_task->world_age); } + */ return jl_get_binding_value_seqcst(b); } @@ -467,91 +597,48 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_ 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); - 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); - } + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (kind == BINDING_KIND_GLOBAL || kind == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(pku))) + return b; + if (jl_bkind_is_some_guard(kind)) { + check_safe_newbinding(m, var); + return b; + } + jl_binding_t *ownerb = b; + pku = jl_walk_binding_inplace(&ownerb, &bpart, jl_current_task->world_age); + jl_value_t *f = NULL; + if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) + f = decode_restriction_value(pku); + if (f == NULL) { + if (kind == BINDING_KIND_IMPLICIT) { + check_safe_newbinding(m, var); return b; } - jl_value_t *f = jl_get_binding_value_if_const(b); - if (f == NULL) { - jl_module_t *from = jl_binding_dbgmodule(b, m, var); - // we must have implicitly imported this with using, so call jl_binding_dbgmodule to try to get the name of the module we got this from - jl_errorf("invalid method definition in %s: exported function %s.%s does not exist", - jl_symbol_name(m->name), jl_symbol_name(from->name), jl_symbol_name(var)); + jl_module_t *from = jl_binding_dbgmodule(b, m, var); + // we must have implicitly imported this with using, so call jl_binding_dbgmodule to try to get the name of the module we got this from + jl_errorf("invalid method definition in %s: exported function %s.%s does not exist", + jl_symbol_name(m->name), from ? jl_symbol_name(from->name) : "", jl_symbol_name(var)); + } + int istype = f && jl_is_type(f); + if (!istype) { + if (kind == BINDING_KIND_IMPLICIT) { + check_safe_newbinding(m, var); + return b; } - // TODO: we might want to require explicitly importing types to add constructors - // or we might want to drop this error entirely - if (decode_restriction_kind(pku) != BINDING_KIND_IMPORTED && !(f && jl_is_type(f) && strcmp(jl_symbol_name(var), "=>") != 0)) { + else if (kind != BINDING_KIND_IMPORTED) { + // TODO: we might want to require explicitly importing types to add constructors + // or we might want to drop this error entirely jl_module_t *from = jl_binding_dbgmodule(b, m, var); jl_errorf("invalid method definition in %s: function %s.%s must be explicitly imported to be extended", - jl_symbol_name(m->name), jl_symbol_name(from->name), jl_symbol_name(var)); + jl_symbol_name(m->name), from ? jl_symbol_name(from->name) : "", jl_symbol_name(var)); } - return b; } - return b; -} - -static int eq_bindings(jl_binding_partition_t *owner, jl_binding_t *alias, size_t world) -{ - jl_ptr_kind_union_t owner_pku = jl_atomic_load_relaxed(&owner->restriction); - assert(decode_restriction_kind(owner_pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(owner_pku) == BINDING_KIND_DECLARED || - jl_bkind_is_some_constant(decode_restriction_kind(owner_pku))); - jl_binding_partition_t *alias_bpart = jl_get_binding_partition(alias, world); - if (owner == alias_bpart) - return 1; - jl_ptr_kind_union_t alias_pku = jl_walk_binding_inplace(&alias, &alias_bpart, world); - if (jl_bkind_is_some_constant(decode_restriction_kind(owner_pku)) && - jl_bkind_is_some_constant(decode_restriction_kind(alias_pku)) && - decode_restriction_value(owner_pku) && - decode_restriction_value(alias_pku) == decode_restriction_value(owner_pku)) - return 1; - return owner == alias_bpart; -} - -// 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, size_t world) -{ - jl_binding_t *b = NULL; - jl_binding_partition_t *bpart = NULL; - jl_module_t *owner = NULL; - JL_LOCK(&m->lock); - int i = (int)module_usings_length(m) - 1; - JL_UNLOCK(&m->lock); - for (; i >= 0; --i) { - JL_LOCK(&m->lock); - jl_module_t *imp = module_usings_getmod(m, i); - 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, 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, world); - jl_ptr_kind_union_t tempb_pku = jl_atomic_load_relaxed(&tempbpart->restriction); - 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, 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, world); - jl_atomic_store_release(&tempbpart->restriction, encode_restriction(NULL, BINDING_KIND_FAILED)); - } - return NULL; - } - if (owner == NULL || !tempb->deprecated) { - owner = imp; - b = tempb; - bpart = tempbpart; - } - } + else if (strcmp(jl_symbol_name(var), "=>") == 0 && (kind == BINDING_KIND_IMPLICIT || kind == BINDING_KIND_EXPLICIT)) { + jl_module_t *from = jl_binding_dbgmodule(b, m, var); + jl_errorf("invalid method definition in %s: function %s.%s must be explicitly imported to be extended", + jl_symbol_name(m->name), from ? jl_symbol_name(from->name) : "", jl_symbol_name(var)); } - *from = owner; - return b; + return ownerb; } // for error message printing: look up the module that exported a binding to m as var @@ -559,100 +646,15 @@ static jl_binding_t *using_resolve_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) { 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_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_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)) - return from; - // if we did not find it (or accidentally found a different one), ignore this - } + jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); + if (jl_bkind_is_some_import(decode_restriction_kind(pku))) { + return ((jl_binding_t*)decode_restriction_value(pku))->globalref->mod; } return m; } 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, size_t world) -{ - if (b == NULL) - b = jl_get_module_binding(m, var, 1); - 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) - return NULL; - if (decode_restriction_kind(pku) == BINDING_KIND_DECLARED) { - return b; - } - if (decode_restriction_kind(pku) == BINDING_KIND_GUARD) { - jl_binding_t *b2 = NULL; - modstack_t top = { m, var, st }; - modstack_t *tmp = st; - for (; tmp != NULL; tmp = tmp->prev) { - if (tmp->m == m && tmp->var == var) { - // import cycle without finding actual location - return NULL; - } - } - jl_module_t *from = NULL; // for error message printing - b2 = using_resolve_binding(m, var, &from, &top, 1, world); - if (b2 == NULL) - return NULL; - assert(from); - JL_GC_PROMISE_ROOTED(from); // gc-analysis does not understand output parameters - JL_GC_PROMISE_ROOTED(b2); - if (b2->deprecated) { - if (jl_get_binding_value(b2) == jl_nothing) { - // silently skip importing deprecated values assigned to nothing (to allow later mutation) - return NULL; - } - } - // do a full import to prevent the result of this lookup from - // changing, for example if this var is assigned to later. - if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction((jl_value_t*)b2, BINDING_KIND_IMPLICIT))) - goto retry; - jl_gc_wb(bpart, b2); - if (b2->deprecated) { - b->deprecated = 1; // we will warn about this below, but we might want to warn at the use sites too - if (m != jl_main_module && m != jl_base_module && - jl_options.depwarn != JL_OPTIONS_DEPWARN_OFF) { - /* with #22763, external packages wanting to replace - deprecated Base bindings should simply export the new - binding */ - jl_printf(JL_STDERR, - "WARNING: using deprecated binding %s.%s in %s.\n", - jl_symbol_name(from->name), jl_symbol_name(var), - jl_symbol_name(m->name)); - jl_binding_dep_message(from, var, b2); - } - } - return b2; - } - jl_walk_binding_inplace(&b, &bpart, world); - return b; -} - -// get the current likely owner of binding when accessing m.var, without resolving the binding (it may change later) -JL_DLLEXPORT jl_binding_t *jl_binding_owner(jl_module_t *m, jl_sym_t *var) -{ - 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_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, 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); - if (decode_restriction_kind(pku) != BINDING_KIND_GLOBAL && !jl_bkind_is_some_constant(decode_restriction_kind(pku))) - return NULL; - return b; -} - // get type of binding m.var, without resolving the binding JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var) { @@ -675,7 +677,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, jl_current_task->world_age); + return jl_get_module_binding(m, var, 1); } JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var) @@ -762,81 +764,82 @@ static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_sym_t *s, int explici) { jl_binding_t *b = jl_get_binding(from, s); - if (b == NULL) { + 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); + (void)pku; + if (b->deprecated) { + if (jl_get_binding_value(b) == jl_nothing) { + // silently skip importing deprecated values assigned to nothing (to allow later mutation) + return; + } + else if (to != jl_main_module && to != jl_base_module && + jl_options.depwarn != JL_OPTIONS_DEPWARN_OFF) { + /* with #22763, external packages wanting to replace + deprecated Base bindings should simply export the new + binding */ + jl_printf(JL_STDERR, + "WARNING: importing deprecated binding %s.%s into %s%s%s.\n", + jl_symbol_name(from->name), jl_symbol_name(s), + jl_symbol_name(to->name), + asname == s ? "" : " as ", + asname == s ? "" : jl_symbol_name(asname)); + jl_binding_dep_message(from, s, b); + } + } + + jl_binding_t *ownerb = b; + jl_binding_partition_t *ownerbpart = bpart; + jl_ptr_kind_union_t owner_pku = jl_walk_binding_inplace(&ownerb, &ownerbpart, jl_current_task->world_age); + + if (jl_bkind_is_some_guard(decode_restriction_kind(owner_pku))) { jl_printf(JL_STDERR, - "WARNING: could not import %s.%s into %s\n", + "WARNING: Imported binding %s.%s was undeclared at import time during import to %s.\n", jl_symbol_name(from->name), jl_symbol_name(s), jl_symbol_name(to->name)); } + + jl_binding_t *bto = jl_get_module_binding(to, asname, 1); + if (bto == b) { + // importing a binding on top of itself. harmless. + return; + } + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_acquire(&jl_world_counter)+1; + jl_binding_partition_t *btopart = jl_get_binding_partition(bto, new_world); + jl_ptr_kind_union_t bto_pku = jl_atomic_load_relaxed(&btopart->restriction); + if (decode_restriction_kind(bto_pku) == BINDING_KIND_GUARD || + decode_restriction_kind(bto_pku) == BINDING_KIND_IMPLICIT || + decode_restriction_kind(bto_pku) == BINDING_KIND_FAILED) { + + jl_binding_partition_t *new_bpart = jl_replace_binding_locked(bto, btopart, (jl_value_t*)b, (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT, new_world); + if (new_bpart->max_world == ~(size_t)0) + jl_add_binding_backedge(b, (jl_value_t*)bto); + jl_atomic_store_release(&jl_world_counter, new_world); + } else { - 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); - assert(decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(pku) == BINDING_KIND_DECLARED || jl_bkind_is_some_constant(decode_restriction_kind(pku))); - (void)pku; - if (b->deprecated) { - if (jl_get_binding_value(b) == jl_nothing) { - // silently skip importing deprecated values assigned to nothing (to allow later mutation) - return; - } - else if (to != jl_main_module && to != jl_base_module && - jl_options.depwarn != JL_OPTIONS_DEPWARN_OFF) { - /* with #22763, external packages wanting to replace - deprecated Base bindings should simply export the new - binding */ - jl_printf(JL_STDERR, - "WARNING: importing deprecated binding %s.%s into %s%s%s.\n", - jl_symbol_name(from->name), jl_symbol_name(s), - jl_symbol_name(to->name), - asname == s ? "" : " as ", - asname == s ? "" : jl_symbol_name(asname)); - jl_binding_dep_message(from, s, b); + if (eq_bindings(bpart, bto, new_world)) { + // already imported - potentially upgrade _EXPLICIT to _IMPORTED + if (decode_restriction_kind(bto_pku) == BINDING_KIND_EXPLICIT && explici != 0) { + jl_replace_binding_locked(bto, btopart, (jl_value_t*)b, BINDING_KIND_IMPORTED, new_world); + jl_atomic_store_release(&jl_world_counter, new_world); } } - - jl_binding_t *bto = jl_get_module_binding(to, asname, 1); - if (bto == b) { - // importing a binding on top of itself. harmless. - return; - } - jl_binding_partition_t *btopart = jl_get_binding_partition(bto, jl_current_task->world_age); - jl_ptr_kind_union_t bto_pku = jl_atomic_load_relaxed(&btopart->restriction); -retry: - if (decode_restriction_kind(bto_pku) == BINDING_KIND_GUARD || - decode_restriction_kind(bto_pku) == BINDING_KIND_IMPLICIT || - decode_restriction_kind(bto_pku) == BINDING_KIND_FAILED) { - - jl_ptr_kind_union_t new_pku = encode_restriction((jl_value_t*)b, (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT); - if (!jl_atomic_cmpswap(&btopart->restriction, &bto_pku, new_pku)) - goto retry; - jl_gc_wb(btopart, b); - bto->deprecated |= b->deprecated; // we already warned about this above, but we might want to warn at the use sites too + else if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { + // already imported from somewhere else + jl_printf(JL_STDERR, + "WARNING: ignoring conflicting import of %s.%s into %s\n", + jl_symbol_name(from->name), jl_symbol_name(s), + jl_symbol_name(to->name)); } else { - if (eq_bindings(bpart, bto, jl_current_task->world_age)) { - // already imported - potentially upgrade to _IMPORTED or _EXPLICIT - if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { - jl_ptr_kind_union_t new_pku = encode_restriction(decode_restriction_value(bto_pku), (explici != 0) ? BINDING_KIND_IMPORTED : BINDING_KIND_EXPLICIT); - if (!jl_atomic_cmpswap(&btopart->restriction, &bto_pku, new_pku)) - goto retry; - // No wb, because the value is unchanged - } - } - else if (jl_bkind_is_some_import(decode_restriction_kind(bto_pku))) { - // already imported from somewhere else - jl_printf(JL_STDERR, - "WARNING: ignoring conflicting import of %s.%s into %s\n", - jl_symbol_name(from->name), jl_symbol_name(s), - jl_symbol_name(to->name)); - } - else { - // conflict with name owned by destination module - jl_printf(JL_STDERR, - "WARNING: import of %s.%s into %s conflicts with an existing identifier; ignored.\n", - jl_symbol_name(from->name), jl_symbol_name(s), - jl_symbol_name(to->name)); - } + // conflict with name owned by destination module + jl_printf(JL_STDERR, + "WARNING: import of %s.%s into %s conflicts with an existing identifier; ignored.\n", + jl_symbol_name(from->name), jl_symbol_name(s), + jl_symbol_name(to->name)); } } + JL_UNLOCK(&world_counter_lock); } JL_DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s) @@ -863,53 +866,76 @@ JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from) { if (to == from) return; + JL_LOCK(&world_counter_lock); JL_LOCK(&to->lock); for (size_t i = 0; i < module_usings_length(to); i++) { if (from == module_usings_getmod(to, i)) { JL_UNLOCK(&to->lock); + JL_UNLOCK(&world_counter_lock); return; } } + + size_t new_world = jl_atomic_load_acquire(&jl_world_counter)+1; struct _jl_module_using new_item = { .mod = from, - .min_world = 0, - .max_world = (size_t)-1 + .min_world = new_world, + .max_world = ~(size_t)0 }; arraylist_grow(&to->usings, sizeof(struct _jl_module_using)/sizeof(void*)); memcpy(&to->usings.items[to->usings.len-3], &new_item, sizeof(struct _jl_module_using)); jl_gc_wb(to, from); + JL_UNLOCK(&to->lock); - // print a warning if something visible via this "using" conflicts with - // an existing identifier. note that an identifier added later may still - // silently override a "using" name. see issue #2054. + // Go through all exported bindings. If we have a binding for this in the + // importing module and it is some import or guard, we need to recompute + // it. jl_svec_t *table = jl_atomic_load_relaxed(&from->bindings); for (size_t i = 0; i < jl_svec_len(table); i++) { jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); if ((void*)b == jl_nothing) break; - 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); - if (b->exportp && (decode_restriction_kind(pku) == BINDING_KIND_GLOBAL || decode_restriction_kind(pku) == BINDING_KIND_IMPORTED)) { + if (b->exportp) { jl_sym_t *var = b->globalref->name; jl_binding_t *tob = jl_get_module_binding(to, var, 0); if (tob) { - jl_binding_partition_t *tobpart = jl_get_binding_partition(tob, jl_current_task->world_age); - jl_ptr_kind_union_t tobpku = jl_walk_binding_inplace(&tob, &tobpart, jl_current_task->world_age); - if (tob && decode_restriction_kind(tobpku) != BINDING_KIND_GUARD && - // don't warn for conflicts with the module name itself. - // see issue #4715 - var != to->name && - !eq_bindings(tobpart, b, jl_current_task->world_age)) { - jl_printf(JL_STDERR, - "WARNING: using %s.%s in module %s conflicts with an existing identifier.\n", - jl_symbol_name(from->name), jl_symbol_name(var), - jl_symbol_name(to->name)); + jl_binding_partition_t *tobpart = jl_get_binding_partition(tob, new_world); + jl_ptr_kind_union_t tobpku = jl_atomic_load_relaxed(&tobpart->restriction); + enum jl_partition_kind kind = decode_restriction_kind(tobpku); + if (kind == BINDING_KIND_IMPLICIT || jl_bkind_is_some_guard(kind)) { + jl_replace_binding_locked(tob, tobpart, NULL, BINDING_KIND_IMPLICIT_RECOMPUTE, new_world); } } } table = jl_atomic_load_relaxed(&from->bindings); } + + JL_LOCK(&from->lock); + if (from->usings_backedges == jl_nothing) { + from->usings_backedges = (jl_value_t*)jl_alloc_vec_any(0); + jl_gc_wb(from, from->usings_backedges); + } + jl_array_ptr_1d_push((jl_array_t*)from->usings_backedges, (jl_value_t*)to); + JL_UNLOCK(&from->lock); + + jl_atomic_store_release(&jl_world_counter, new_world); + JL_UNLOCK(&world_counter_lock); +} + +JL_DLLEXPORT jl_value_t *jl_get_module_usings_backedges(jl_module_t *m) +{ + // We assume the caller holds the world_counter_lock, which is the only place we set this + // TODO: We may want to make this more precise with the module lock + return m->usings_backedges; +} + +JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym_t *s) +{ + jl_binding_t *b = jl_get_module_binding(m, s, 0); + if (!b) + return jl_nothing; + return (jl_value_t*)b; } JL_DLLEXPORT void jl_module_public(jl_module_t *from, jl_sym_t *s, int exported) @@ -941,9 +967,6 @@ JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var, int allow_import) // u if (!bpart || jl_bkind_is_some_import(decode_restriction_kind(pku))) return 0; } else { - if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - jl_resolve_owner(b, b->globalref->mod, b->globalref->name, NULL, jl_current_task->world_age); - } pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); } if (jl_bkind_is_some_guard(decode_restriction_kind(pku))) @@ -974,16 +997,6 @@ JL_DLLEXPORT int jl_module_public_p(jl_module_t *m, jl_sym_t *var) return b && b->publicp; } -JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var) -{ - jl_binding_t *b = jl_get_module_binding(m, var, 0); - jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (!bpart) - return 0; - enum jl_partition_kind kind = decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)); - return kind == BINDING_KIND_DECLARED || !jl_bkind_is_some_guard(kind); -} - uint_t bindingkey_hash(size_t idx, jl_value_t *data) { jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); // This must always happen inside the lock @@ -1052,7 +1065,8 @@ 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, jl_current_task->world_age); + if (!b) + b = jl_get_module_binding(gr->mod, gr->name, 1); // ignores b->deprecated return b == NULL ? NULL : jl_get_binding_value(b); } @@ -1079,23 +1093,27 @@ 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)))); + bpart->min_world = 0; + bpart->max_world = ~(size_t)0; jl_atomic_store_release(&bpart->restriction, encode_restriction(val, BINDING_KIND_CONST)); jl_gc_wb(bpart, val); } -void jl_invalidate_binding_refs(jl_globalref_t *ref, jl_binding_partition_t *invalidated_bpart, size_t new_world) +void jl_invalidate_binding_refs(jl_globalref_t *ref, jl_binding_partition_t *invalidated_bpart, jl_binding_partition_t *new_bpart, size_t new_world) { static jl_value_t *invalidate_code_for_globalref = NULL; if (invalidate_code_for_globalref == NULL && jl_base_module != NULL) invalidate_code_for_globalref = jl_get_global(jl_base_module, jl_symbol("invalidate_code_for_globalref!")); if (!invalidate_code_for_globalref) jl_error("Binding invalidation is not permitted during bootstrap."); - if (jl_generating_output()) - jl_error("Binding invalidation is not permitted during image generation."); - jl_value_t *boxed_world = jl_box_ulong(new_world); - JL_GC_PUSH1(&boxed_world); - jl_call3((jl_function_t*)invalidate_code_for_globalref, (jl_value_t*)ref, (jl_value_t*)invalidated_bpart, boxed_world); + jl_value_t **fargs; + JL_GC_PUSHARGS(fargs, 5); + fargs[0] = (jl_function_t*)invalidate_code_for_globalref; + fargs[1] = (jl_value_t*)ref; + fargs[2] = (jl_value_t*)invalidated_bpart; + fargs[3] = (jl_value_t*)new_bpart; + fargs[4] = jl_box_ulong(new_world); + jl_apply(fargs, 5); JL_GC_POP(); } @@ -1131,50 +1149,90 @@ JL_DLLEXPORT void jl_maybe_add_binding_backedge(jl_globalref_t *gr, jl_module_t jl_add_binding_backedge(b, edge); } -JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) +JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b, + jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) { - jl_binding_t *b = gr->binding; - 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); + assert(jl_atomic_load_relaxed(&b->partitions) == old_bpart); + jl_atomic_store_release(&old_bpart->max_world, new_world-1); + jl_binding_partition_t *new_bpart = new_binding_partition(); + new_bpart->min_world = new_world; + if (kind == BINDING_KIND_IMPLICIT_RECOMPUTE) { + assert(!restriction_val); + check_new_binding_implicit(new_bpart, b, NULL, 0, new_world); + } + else + jl_atomic_store_relaxed(&new_bpart->restriction, encode_restriction(restriction_val, kind)); + jl_atomic_store_relaxed(&new_bpart->next, old_bpart); + jl_gc_wb(new_bpart, old_bpart); - if (decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction)) == BINDING_KIND_GUARD) { - // Already guard - return; + jl_atomic_store_release(&b->partitions, new_bpart); + jl_gc_wb(b, new_bpart); + + + if (jl_typeinf_world != 1) { + jl_task_t *ct = jl_current_task; + size_t last_world = ct->world_age; + ct->world_age = jl_typeinf_world; + jl_invalidate_binding_refs(b->globalref, old_bpart, new_bpart, new_world-1); + ct->world_age = last_world; } + return new_bpart; +} + +JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding(jl_binding_t *b, + jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind) { + JL_LOCK(&world_counter_lock); - jl_task_t *ct = jl_current_task; - size_t last_world = ct->world_age; - size_t new_max_world = jl_atomic_load_acquire(&jl_world_counter); - jl_atomic_store_release(&bpart->max_world, new_max_world); - ct->world_age = jl_typeinf_world; - jl_invalidate_binding_refs(gr, bpart, new_max_world); - ct->world_age = last_world; - jl_atomic_store_release(&jl_world_counter, new_max_world + 1); + + if (jl_atomic_load_relaxed(&b->partitions) != old_bpart) { + JL_UNLOCK(&world_counter_lock); + return NULL; + } + + size_t new_world = jl_atomic_load_acquire(&jl_world_counter)+1; + jl_binding_partition_t *bpart = jl_replace_binding_locked(b, old_bpart, restriction_val, kind, new_world); + if (bpart && bpart->min_world == new_world) + jl_atomic_store_release(&jl_world_counter, new_world); + JL_UNLOCK(&world_counter_lock); + return bpart; } 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, jl_current_task->world_age); + if (!b) + b = jl_get_module_binding(gr->mod, gr->name, 1); 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 void jl_force_binding_resolution(jl_globalref_t *gr, size_t world) +JL_DLLEXPORT void jl_disable_binding(jl_globalref_t *gr) { jl_binding_t *b = gr->binding; - jl_resolve_owner(b, gr->mod, gr->name, NULL, world); + if (!b) + b = jl_get_module_binding(gr->mod, gr->name, 1); + 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) { + // Already guard + return; + } + + for (;;) + if (jl_replace_binding(b, bpart, NULL, BINDING_KIND_GUARD)) + break; } JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_binding(m, var); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - return b && jl_bkind_is_some_constant(decode_restriction_kind(jl_atomic_load_relaxed(&bpart->restriction))); + jl_ptr_kind_union_t pku = jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + return b && jl_bkind_is_some_constant(decode_restriction_kind(pku)); } // set the deprecated flag for a binding: @@ -1188,12 +1246,9 @@ JL_DLLEXPORT void jl_deprecate_binding(jl_module_t *m, jl_sym_t *var, int flag) JL_DLLEXPORT int jl_is_binding_deprecated(jl_module_t *m, jl_sym_t *var) { - if (jl_binding_resolved_p(m, var)) { - // XXX: this only considers if the original is deprecated, not this precise binding - jl_binding_t *b = jl_get_binding(m, var); - return b && b->deprecated; - } - return 0; + // XXX: this only considers if the original is deprecated, not this precise binding + jl_binding_t *b = jl_get_binding(m, var); + return b && b->deprecated; } void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b) @@ -1229,22 +1284,19 @@ jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl JL_GC_PUSH1(&rhs); // callee-rooted 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); - assert(!jl_bkind_is_some_guard(decode_restriction_kind(pku)) && !jl_bkind_is_some_import(decode_restriction_kind(pku))); - 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)) { jl_value_t *old = decode_restriction_value(pku); JL_GC_PROMISE_ROOTED(old); if (jl_egal(rhs, old)) { JL_GC_POP(); return NULL; } - if (jl_typeof(rhs) == jl_typeof(old)) - jl_errorf("invalid redefinition of constant %s.%s. This redefinition may be permitted using the `const` keyword.", - jl_symbol_name(mod->name), jl_symbol_name(var)); - else - jl_errorf("invalid redefinition of constant %s.%s.", - jl_symbol_name(mod->name), jl_symbol_name(var)); + jl_errorf("invalid assignment to constant %s.%s. This redefinition may be permitted using the `const` keyword.", + jl_symbol_name(mod->name), jl_symbol_name(var)); } - jl_value_t *old_ty = decode_restriction_value(pku); + assert(kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_GLOBAL); + jl_value_t *old_ty = kind == BINDING_KIND_DECLARED ? (jl_value_t*)jl_any_type : decode_restriction_value(pku); JL_GC_PROMISE_ROOTED(old_ty); if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) { if (!jl_isa(rhs, old_ty)) @@ -1285,7 +1337,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl jl_ptr_kind_union_t pku = jl_atomic_load_relaxed(&bpart->restriction); assert(!jl_bkind_is_some_guard(decode_restriction_kind(pku)) && !jl_bkind_is_some_import(decode_restriction_kind(pku))); if (jl_bkind_is_some_constant(decode_restriction_kind(pku))) - jl_errorf("invalid redefinition of constant %s.%s", + jl_errorf("invalid assignment to constant %s.%s", jl_symbol_name(mod->name), jl_symbol_name(var)); jl_value_t *ty = decode_restriction_value(pku); JL_GC_PROMISE_ROOTED(ty); @@ -1422,7 +1474,7 @@ JL_DLLEXPORT void jl_clear_implicit_imports(jl_module_t *m) break; 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_IMPLICIT) { - jl_atomic_store_relaxed(&bpart->restriction, encode_restriction(NULL, BINDING_KIND_GUARD)); + jl_atomic_store_relaxed(&b->partitions, NULL); } } JL_UNLOCK(&m->lock); diff --git a/src/staticdata.c b/src/staticdata.c index cb1dc54d26d50b..d4bfcd8ecb69d5 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -827,6 +827,8 @@ static void jl_queue_module_for_serialization(jl_serializer_state *s, jl_module_ for (size_t i = 0; i < module_usings_length(m); i++) { jl_queue_for_serialization(s, module_usings_getmod(m, i)); } + + jl_queue_for_serialization(s, m->usings_backedges); } // Anything that requires uniquing or fixing during deserialization needs to be "toplevel" @@ -1299,10 +1301,14 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t newm->file = NULL; arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, file))); arraylist_push(&s->relocs_list, (void*)backref_id(s, m->file, s->link_ids_relocs)); + newm->usings_backedges = NULL; + arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings_backedges))); + arraylist_push(&s->relocs_list, (void*)backref_id(s, m->usings_backedges, s->link_ids_relocs)); // write out the usings list memset(&newm->usings._space, 0, sizeof(newm->usings._space)); if (m->usings.items == &m->usings._space[0]) { + newm->usings.items = &newm->usings._space[0]; // Push these relocations here, to keep them in order. This pairs with the `newm->usings.items = ` below. arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings.items))); arraylist_push(&s->relocs_list, (void*)(((uintptr_t)DataRef << RELOC_TAG_OFFSET) + item)); @@ -1316,7 +1322,7 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t if (s->incremental) { if (data->max_world != (size_t)-1) newm_data->max_world = 0; - newm_data->min_world = 0; + newm_data->min_world = jl_require_world; } arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, usings._space[3*i]))); arraylist_push(&s->relocs_list, (void*)backref_id(s, data->mod, s->link_ids_relocs)); diff --git a/src/toplevel.c b/src/toplevel.c index cdd390b9b49edd..d9ab4011904ebb 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -303,7 +303,7 @@ static jl_value_t *jl_eval_dot_expr(jl_module_t *m, jl_value_t *x, jl_value_t *f } 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) { +void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong) { // create uninitialized mutable binding for "global x" decl sometimes or probably jl_module_t *gm; jl_sym_t *gs; @@ -321,42 +321,58 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type) { 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 = NULL; - jl_ptr_kind_union_t new_pku = encode_restriction(set_type, set_type == NULL ? BINDING_KIND_DECLARED : BINDING_KIND_GLOBAL); + if (!strong && set_type) + jl_error("Weak global definitions cannot have types"); + enum jl_partition_kind new_kind = strong ? BINDING_KIND_GLOBAL : BINDING_KIND_DECLARED; + jl_value_t *global_type = set_type; + if (strong && !global_type) + global_type = (jl_value_t*)jl_any_type; 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; + enum jl_partition_kind kind = decode_restriction_kind(pku); + if (kind != BINDING_KIND_GLOBAL) { + if (jl_bkind_is_some_guard(kind) || kind == BINDING_KIND_DECLARED || kind == BINDING_KIND_IMPLICIT) { + if (decode_restriction_kind(pku) == new_kind) { + if (!set_type) + goto done; + goto check_type; + } check_safe_newbinding(gm, gs); - if (jl_atomic_cmpswap(&bpart->restriction, &pku, new_pku)) { - break; + if (bpart->min_world == new_world) { + if (jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(global_type, new_kind))) { + break; + } + if (set_type) + jl_gc_wb(bpart, set_type); + continue; + } else { + jl_replace_binding_locked(b, bpart, global_type, new_kind, new_world); } - continue; + break; } 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_errorf("cannot set type for constant %s.%s.", jl_symbol_name(gm->name), jl_symbol_name(gs)); } else { - jl_errorf("cannot set type for imported global %s.%s.", + jl_errorf("cannot set type for imported binding %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)); + if (set_type) + { +check_type: + 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); @@ -367,7 +383,7 @@ void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) size_t i, l = jl_array_nrows(ex->args); for (i = 0; i < l; i++) { jl_value_t *arg = jl_exprarg(ex, i); - jl_declare_global(m, arg, NULL); + jl_declare_global(m, arg, NULL, 0); } } @@ -431,10 +447,8 @@ static void expr_attributes(jl_value_t *v, jl_array_t *body, int *has_ccall, int if (jl_is_globalref(f)) { jl_module_t *mod = jl_globalref_mod(f); jl_sym_t *name = jl_globalref_name(f); - if (jl_binding_resolved_p(mod, name)) { - jl_binding_t *b = jl_get_binding(mod, name); - called = jl_get_binding_value_if_const(b); - } + jl_binding_t *b = jl_get_binding(mod, name); + called = jl_get_binding_value_if_const(b); } else if (jl_is_quotenode(f)) { called = jl_quotenode_value(f); @@ -747,72 +761,72 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( if (!b) { b = jl_get_module_binding(mod, var, 1); } + jl_binding_partition_t *new_bpart = NULL; 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) { + while (!new_bpart) { enum jl_partition_kind kind = decode_restriction_kind(pku); if (jl_bkind_is_some_constant(kind)) { if (!val) { + new_bpart = bpart; break; } jl_value_t *old = decode_restriction_value(pku); JL_GC_PROMISE_ROOTED(old); - if (jl_egal(val, old)) + if (jl_egal(val, old)) { + new_bpart = bpart; break; - if (!did_warn) { - if (jl_typeof(val) != jl_typeof(old) || jl_is_type(val) || jl_is_module(val)) - jl_errorf("invalid redefinition of constant %s.%s", - jl_symbol_name(mod->name), - jl_symbol_name(var)); - else - jl_safe_printf("WARNING: redefinition of constant %s.%s. This may fail, cause incorrect answers, or produce other errors.\n", - jl_symbol_name(mod->name), - jl_symbol_name(var)); - did_warn = 1; - } - if (new_world > bpart->min_world) { - // TODO: Invoke invalidation logic here - jl_atomic_store_relaxed(&bpart->max_world, new_world - 1); - bpart = jl_get_binding_partition(b, new_world); - pku = jl_atomic_load_relaxed(&bpart->restriction); - } - } else if (!jl_bkind_is_some_guard(decode_restriction_kind(pku))) { - if (jl_bkind_is_some_import(decode_restriction_kind(pku))) { - jl_errorf("cannot declare %s.%s constant; it was already declared as an import", - jl_symbol_name(mod->name), jl_symbol_name(var)); - } else { - jl_errorf("cannot declare %s.%s constant; it was already declared global", - jl_symbol_name(mod->name), jl_symbol_name(var)); } + } else if (jl_bkind_is_some_import(kind) && kind != BINDING_KIND_IMPLICIT) { + jl_errorf("cannot declare %s.%s constant; it was already declared as an import", + jl_symbol_name(mod->name), jl_symbol_name(var)); + } else if (kind == BINDING_KIND_GLOBAL) { + jl_errorf("cannot declare %s.%s constant; it was already declared global", + jl_symbol_name(mod->name), jl_symbol_name(var)); } - if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(val, constant_kind))) { - continue; + if (bpart->min_world == new_world) { + if (!jl_atomic_cmpswap(&bpart->restriction, &pku, encode_restriction(val, constant_kind))) { + continue; + } + jl_gc_wb(bpart, val); + new_bpart = bpart; + } else { + new_bpart = jl_replace_binding_locked(b, bpart, val, constant_kind, new_world); } - jl_gc_wb(bpart, val); - size_t prev_min_world = bpart->min_world; - bpart->min_world = new_world; - 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, prev_min_world-1); + int need_backdate = new_world && val; + if (need_backdate) { + // We will backdate as long as this partition was never explicitly + // declared const, global, or imported. + jl_binding_partition_t *prev_bpart = bpart; + for (;;) { 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) { - // Just keep it simple and use one backdated const entry for both previous guard partition - // ranges. - jl_atomic_store_relaxed(&prev_bpart->max_world, new_world-1); - need_backdate = 1; + enum jl_partition_kind prev_kind = decode_restriction_kind(prev_pku); + if (jl_bkind_is_some_constant(prev_kind) || prev_kind == BINDING_KIND_GLOBAL || + (jl_bkind_is_some_import(prev_kind))) { + need_backdate = 0; + break; } + if (prev_bpart->min_world == 0) + break; + prev_bpart = jl_get_binding_partition(b, prev_bpart->min_world - 1); } } + // If backdate is required, rewrite all previous binding partitions to + // backdated const if (need_backdate) { - jl_declare_constant_val3(b, mod, var, val, BINDING_KIND_BACKDATED_CONST, 0); + // We will backdate as long as this partition was never explicitly + // declared const, global, or *explicitly* imported. + jl_binding_partition_t *prev_bpart = bpart; + for (;;) { + jl_atomic_store_relaxed(&prev_bpart->restriction, encode_restriction(val, BINDING_KIND_BACKDATED_CONST)); + if (prev_bpart->min_world == 0) + break; + prev_bpart = jl_get_binding_partition(b, prev_bpart->min_world - 1); + } } } JL_GC_POP(); - return bpart; + return new_bpart; } JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val2( @@ -1035,7 +1049,7 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_val size_t i, l = jl_array_nrows(ex->args); for (i = 0; i < l; i++) { jl_value_t *arg = jl_exprarg(ex, i); - jl_declare_global(m, arg, NULL); + jl_declare_global(m, arg, NULL, 0); } JL_GC_POP(); return jl_nothing; diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 4a320282610cda..6b75a228b2761d 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -17,7 +17,7 @@ export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, import Base.Docs.apropos using Base: unwrap_unionall, rewrap_unionall, isdeprecated, Bottom, summarysize, - signature_type, format_bytes, isbindingresolved + signature_type, format_bytes using Base.Libc using Markdown @@ -264,7 +264,7 @@ function _subtypes_in!(mods::Array, x::Type) m = pop!(mods) xt = xt::DataType for s in names(m, all = true) - if isbindingresolved(m, s) && !isdeprecated(m, s) && isdefined(m, s) + if !isdeprecated(m, s) && isdefined(m, s) t = getfield(m, s) dt = isa(t, UnionAll) ? unwrap_unionall(t) : t if isa(dt, DataType) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index cc4f4f00cf8f61..561e4bcd4feb2d 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -72,7 +72,6 @@ function UndefVarError_hint(io::IO, ex::UndefVarError) end function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol) - Base.isbindingresolved(m, var) || return false (Base.isexported(m, var) || Base.ispublic(m, var)) || return false active_mod = Base.active_module() print(io, "\nHint: ") diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index eadc2672dd29bb..e70eb8dd97927d 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -240,8 +240,7 @@ function complete_symbol!(suggestions::Vector{Completion}, return suggestions end -completes_module(mod::Module, x::Symbol) = - Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getglobal(mod, x), Module) +completes_module(mod::Module, x::Symbol) = isdefined(mod, x) && isa(getglobal(mod, x), Module) function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t)) if isa(t, Union) diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 313994505b3eef..c25b7a748ccfbb 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -312,11 +312,7 @@ function summarize(binding::Binding, sig) else println(io, "No documentation found.\n") quot = any(isspace, sprint(print, binding)) ? "'" : "" - if Base.isbindingresolved(binding.mod, binding.var) - println(io, "Binding ", quot, "`", binding, "`", quot, " exists, but has not been assigned a value.") - else - println(io, "Binding ", quot, "`", binding, "`", quot, " does not exist.") - end + println(io, "Binding ", quot, "`", binding, "`", quot, " exists, but has not been assigned a value.") end md = Markdown.parse(seekstart(io)) # Save metadata in the generated markdown. @@ -567,8 +563,7 @@ function repl(io::IO, s::Symbol; brief::Bool=true, mod::Module=Main, internal_ac quote repl_latex($io, $str) repl_search($io, $str, $mod) - $(if !isdefined(mod, s) && !Base.isbindingresolved(mod, s) && !haskey(keywords, s) && !Base.isoperator(s) - # n.b. we call isdefined for the side-effect of resolving the binding, if possible + $(if !isdefined(mod, s) && !haskey(keywords, s) && !Base.isoperator(s) :(repl_corrections($io, $str, $mod)) end) $(_repl(s, brief, mod, internal_accesses)) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 77d056b63655da..59e994f88945b2 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -214,8 +214,6 @@ end let s = "using REP" c, r = test_complete_32377(s) @test count(isequal("REPL"), c) == 1 - # issue #30234 - @test !Base.isbindingresolved(M32377, :tanh) # check what happens if REPL is already imported M32377.eval(:(using REPL)) c, r = test_complete_32377(s) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 88fd055f7bd58b..e8d670b3d7d000 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -2158,7 +2158,7 @@ function detect_ambiguities(mods::Module...; while !isempty(work) mod = pop!(work) for n in names(mod, all = true) - (!Base.isbindingresolved(mod, n) || Base.isdeprecated(mod, n)) && continue + Base.isdeprecated(mod, n) && continue if !isdefined(mod, n) if is_in_mods(mod, recursive, mods) if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds @@ -2229,7 +2229,7 @@ function detect_unbound_args(mods...; while !isempty(work) mod = pop!(work) for n in names(mod, all = true) - (!Base.isbindingresolved(mod, n) || Base.isdeprecated(mod, n)) && continue + Base.isdeprecated(mod, n) && continue if !isdefined(mod, n) if is_in_mods(mod, recursive, mods) if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 5f859e773f5d28..0f29817e74dd55 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -171,12 +171,8 @@ module UnboundAmbig55868 using .B export C, D end -@test !Base.isbindingresolved(UnboundAmbig55868, :C) -@test !Base.isbindingresolved(UnboundAmbig55868, :D) @test isempty(detect_unbound_args(UnboundAmbig55868)) @test isempty(detect_ambiguities(UnboundAmbig55868)) -@test !Base.isbindingresolved(UnboundAmbig55868, :C) -@test !Base.isbindingresolved(UnboundAmbig55868, :D) # Test that Core and Base are free of ambiguities # not using isempty so this prints more information when it fails diff --git a/test/atomics.jl b/test/atomics.jl index adfe4c87138cdb..7e9f29c23ca108 100644 --- a/test/atomics.jl +++ b/test/atomics.jl @@ -23,22 +23,32 @@ mutable struct Refxy{T} end modname = String(nameof(@__MODULE__)) -@test_throws ErrorException("invalid redefinition of constant $modname.ARefxy") @eval mutable struct ARefxy{T} +const orig_Refxy = Refxy +const orig_ARefxy = ARefxy +mutable struct ARefxy{T} @atomic x::T @atomic y::T end -@test_throws ErrorException("invalid redefinition of constant $modname.ARefxy") @eval mutable struct ARefxy{T} +@test orig_ARefxy !== ARefxy +const ARefxy = orig_ARefxy +mutable struct ARefxy{T} x::T y::T end -@test_throws ErrorException("invalid redefinition of constant $modname.ARefxy") @eval mutable struct ARefxy{T} +@test orig_ARefxy !== ARefxy +const ARefxy = orig_ARefxy +mutable struct ARefxy{T} x::T @atomic y::T end -@test_throws ErrorException("invalid redefinition of constant $modname.Refxy") @eval mutable struct Refxy{T} +@test orig_ARefxy !== ARefxy +const ARefxy = orig_ARefxy +mutable struct Refxy{T} x::T @atomic y::T end +@test orig_Refxy !== Refxy +const Refxy = orig_Refxy copy(r::Union{Refxy,ARefxy}) = typeof(r)(r.x, r.y) function add(x::T, y)::T where {T}; x + y; end diff --git a/test/core.jl b/test/core.jl index 5e2677f0f075ff..853ab697a72956 100644 --- a/test/core.jl +++ b/test/core.jl @@ -63,20 +63,7 @@ mutable struct ABCDconst c const d::Union{Int,Nothing} end -@test_throws(ErrorException("invalid redefinition of constant $(nameof(curmod)).ABCDconst"), - mutable struct ABCDconst - const a - const b::Int - c - d::Union{Int,Nothing} - end) -@test_throws(ErrorException("invalid redefinition of constant $(nameof(curmod)).ABCDconst"), - mutable struct ABCDconst - a - b::Int - c - d::Union{Int,Nothing} - end) + let abcd = ABCDconst(1, 2, 3, 4) @test (1, 2, 3, 4) === (abcd.a, abcd.b, abcd.c, abcd.d) @test_throws(ErrorException("setfield!: const field .a of type ABCDconst cannot be changed"), @@ -113,6 +100,21 @@ let abcd = ABCDconst(1, 2, 3, 4) abcd.d = nothing) @test (1, 2, "not constant", 4) === (abcd.a, abcd.b, abcd.c, abcd.d) end +const orig_ABCDconst = ABCDconst +mutable struct ABCDconst + const a + const b::Int + c + d::Union{Int,Nothing} +end +@test ABCDconst !== orig_ABCDconst +mutable struct ABCDconst + a + b::Int + c + d::Union{Int,Nothing} +end +@test ABCDconst !== orig_ABCDconst # Issue #52686 struct A52686{T} end struct B52686{T, S} @@ -1210,10 +1212,6 @@ let A = [1] @test x == 1 end -# Make sure that `Module` is not resolved to `Core.Module` during sysimg generation -# so that users can define their own binding named `Module` in Main. -@test success(`$(Base.julia_cmd()) -e '@assert !Base.isbindingresolved(Main, :Module)'`) - # Module() constructor @test names(Module(:anonymous), all = true, imported = true) == [:anonymous] @test names(Module(:anonymous, false), all = true, imported = true) == [:anonymous] @@ -3885,11 +3883,13 @@ end struct NInitializedTestType a end +const orig_NInitializedTestType = NInitializedTestType -@test_throws ErrorException @eval struct NInitializedTestType +struct NInitializedTestType a NInitializedTestType() = new() end +@test orig_NInitializedTestType !== NInitializedTestType # issue #12394 mutable struct Empty12394 end @@ -5578,76 +5578,94 @@ struct A16424 x y end +const orig_A16424 = A16424 struct A16424 # allowed x y end +@test A16424 === orig_A16424 -@test_throws ErrorException @eval struct A16424 +struct A16424 x z end +@test A16424 !== orig_A16424 +const A16424 = orig_A16424 -@test_throws ErrorException @eval struct A16424 +struct A16424 x y::Real end +@test A16424 !== orig_A16424 +const A16424 = orig_A16424 struct B16424{T} a end +const orig_B16424 = B16424 struct B16424{T} a end +@test B16424 === orig_B16424 -@test_throws ErrorException @eval struct B16424{S} +struct B16424{S} a end +@test B16424 !== orig_B16424 struct C16424{T,S} x::T y::S end +const orig_C16424 = C16424 struct C16424{T,S} x::T y::S end +@test C16424 === orig_C16424 -@test_throws ErrorException @eval struct C16424{T,S} +struct C16424{T,S} x::S y::T end +@test C16424 !== orig_C16424 struct D16424{T<:Real,S<:T} x::Vector{S} y::Vector{T} end +const orig_D16424 = D16424 struct D16424{T<:Real,S<:T} x::Vector{S} y::Vector{T} end +@test D16424 === orig_D16424 -@test_throws ErrorException struct D16424{T<:Real,S<:Real} +struct D16424{T<:Real,S<:Real} x::Vector{S} y::Vector{T} end +@test D16424 !== orig_D16424 # issue #20999, allow more type redefinitions struct T20999 x::Array{T} where T<:Real end +const orig_T20999 = T20999 struct T20999 x::Array{T} where T<:Real end +@test T20999 === orig_T20999 -@test_throws ErrorException struct T20999 +struct T20999 x::Array{T} where T<:Integer end +@test T20999 !== orig_T20999 # issue #54757, type redefinitions with recursive reference in supertype struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A,N}},Vararg{Y,N}} where {X,Y<:T54757}, N} @@ -5655,20 +5673,27 @@ struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Unio y::Union{A,T54757{A,N}} z::T54757{A} end +const orig_T54757 = T54757 struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A,N}},Vararg{Y,N}} where {X,Y<:T54757}, N} x::A y::Union{A,T54757{A,N}} z::T54757{A} end +# The type is identical - either answer is semantically allowed here +# However, knowing that the type is identical would require reasoning about the purity of the +# field definitions exprs, which we do not do. Thus, simply check that this doesn't error and +# then reset to the original for the next test. +const T54757 = orig_T54757 -@test_throws ErrorException struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A}},Vararg{Y,N}} where {X,Y<:T54757}, N} +struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Tuple{Vararg},Union{T54757{Union{X,Integer}},T54757{A}},Vararg{Y,N}} where {X,Y<:T54757}, N} x::A y::Union{A,T54757{A,N}} z::T54757{A} end +@test orig_T54757 !== T54757 - +# initialization of Vector{Core.TypeofBottom} let a = Vector{Core.TypeofBottom}(undef, 2) @test a[1] == Union{} @test a == [Union{}, Union{}] @@ -7676,29 +7701,35 @@ struct S36104{K,V} # check that redefining it works S36104{K,V}() where {K,V} = new() S36104{K,V}(x::S36104) where {K,V} = new(x) end -# with a gensymmed unionall -struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} + +# with a gensymmed unionall (#39778) +struct Symmetric39778{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} data::S uplo::Char end -struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} +const orig_Symmetric39778 = Symmetric39778 +struct Symmetric39778{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} data::S uplo::Char end -@test_throws ErrorException begin - struct Symmetric{T,S<:AbstractMatrix{T}} <: AbstractMatrix{T} - data::S - uplo::Char - end -end +@test Symmetric39778 === orig_Symmetric39778 +struct Symmetric39778{T,S<:AbstractMatrix{T}} <: AbstractMatrix{T} + data::S + uplo::Char end +@test Symmetric39778 !== orig_Symmetric39778 + +end # module M36104 + @test fieldtypes(M36104.T36104) == (Vector{M36104.T36104},) @test_throws ErrorException("expected") @eval(struct X36104; x::error("expected"); end) @test !@isdefined(X36104) struct X36104; x::Int; end @test fieldtypes(X36104) == (Int,) primitive type P36104 8 end -@test_throws ErrorException("invalid redefinition of constant $(nameof(curmod)).P36104") @eval(primitive type P36104 16 end) +const orig_P36104 = P36104 +primitive type P36104 16 end +@test P36104 !== orig_P36104 # Malformed invoke f_bad_invoke(x::Int) = invoke(x, (Any,), x) diff --git a/test/errorshow.jl b/test/errorshow.jl index f83bbe31b7cc45..8f7482ce3235ec 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -216,13 +216,14 @@ Base.show_method_candidates(buf, try bad_vararg_decl("hello", 3) catch e e end) macro except_str(expr, err_type) source_info = __source__ + errmsg = "expected failure, but no exception thrown for $expr" return quote let err = nothing try $(esc(expr)) catch err end - err === nothing && error("expected failure, but no exception thrown") + err === nothing && error($errmsg) @testset let expr=$(repr(expr)) $(Expr(:macrocall, Symbol("@test"), source_info, :(typeof(err) === $(esc(err_type))))) end @@ -255,6 +256,7 @@ end macro except_stackframe(expr, err_type) source_info = __source__ + errmsg = "expected failure, but no exception thrown for $expr" return quote let err = nothing local st @@ -263,7 +265,7 @@ macro except_stackframe(expr, err_type) catch err st = stacktrace(catch_backtrace()) end - err === nothing && error("expected failure, but no exception thrown") + err === nothing && error($errmsg) @testset let expr=$(repr(expr)) $(Expr(:macrocall, Symbol("@test"), source_info, :(typeof(err) === $(esc(err_type))))) end @@ -297,6 +299,7 @@ err_str = @except_str 1 + 2 MethodError err_str = @except_str Float64[](1) MethodError @test !occursin("import Base.Array", err_str) +global Array Array() = 1 err_str = @except_str Array([1]) MethodError @test occursin("import Base.Array", err_str) diff --git a/test/misc.jl b/test/misc.jl index 070952db890322..fef573e9fc747b 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1218,8 +1218,8 @@ end @test readlines(`$(Base.julia_cmd()) --startup-file=no -e 'foreach(println, names(Main))'`) == ["Base","Core","Main"] # issue #26310 -@test_warn "could not import" Core.eval(@__MODULE__, :(import .notdefined_26310__)) -@test_warn "could not import" Core.eval(Main, :(import ........notdefined_26310__)) +@test_warn "undeclared at import time" Core.eval(@__MODULE__, :(import .notdefined_26310__)) +@test_warn "undeclared at import time" Core.eval(Main, :(import ........notdefined_26310__)) @test_nowarn Core.eval(Main, :(import .Main)) @test_nowarn Core.eval(Main, :(import ....Main)) diff --git a/test/precompile.jl b/test/precompile.jl index 194f88719642e9..a8154090c0360a 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1359,7 +1359,6 @@ precompile_test_harness("package_callbacks") do dir """) Base.compilecache(Base.PkgId("$(Test2_module)")) - @test !Base.isbindingresolved(Main, Test2_module) Base.require(Main, Test2_module) @test take!(loaded_modules) == Test1_module @test take!(loaded_modules) == Test2_module diff --git a/test/rebinding.jl b/test/rebinding.jl index aee866facaf023..5e50d7f022d061 100644 --- a/test/rebinding.jl +++ b/test/rebinding.jl @@ -62,6 +62,26 @@ module Rebinding @test f_generated_return_delete_me() == 4 Base.delete_binding(@__MODULE__, :delete_me) @test_throws UndefVarError f_generated_return_delete_me() + + module DeleteMeModule + export delete_me_implicit + const delete_me_explicit = 5 + const delete_me_implicit = 6 + end + + # + via import + using .DeleteMeModule: delete_me_explicit + f_return_delete_me_explicit() = delete_me_explicit + @test f_return_delete_me_explicit() == 5 + Base.delete_binding(DeleteMeModule, :delete_me_explicit) + @test_throws UndefVarError f_return_delete_me_explicit() + + # + via using + using .DeleteMeModule + f_return_delete_me_implicit() = delete_me_implicit + @test f_return_delete_me_implicit() == 6 + Base.delete_binding(DeleteMeModule, :delete_me_implicit) + @test_throws UndefVarError f_return_delete_me_implicit() end module RebindingPrecompile @@ -95,9 +115,11 @@ module RebindingPrecompile """) Base.compilecache(Base.PkgId("UseTheBindings")) @eval using LotsOfBindingsToDelete - # Delete some bindings before loading the dependent package - Base.delete_binding(LotsOfBindingsToDelete, :delete_me_1) - Base.delete_binding(LotsOfBindingsToDelete, :delete_me_3) + invokelatest() do + # Delete some bindings before loading the dependent package + Base.delete_binding(LotsOfBindingsToDelete, :delete_me_1) + Base.delete_binding(LotsOfBindingsToDelete, :delete_me_3) + end # Load the dependent package @eval using UseTheBindings invokelatest() do diff --git a/test/reflection.jl b/test/reflection.jl index 9aa8fe512cd7c0..57c32d19de6294 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -969,10 +969,6 @@ f20872(::Val, ::Val) = false @test_throws ErrorException which(f20872, Tuple{Any,Val{N}} where N) @test which(Tuple{typeof(f20872), Val{1}, Val{2}}).sig == Tuple{typeof(f20872), Val, Val} -module M29962 end -# make sure checking if a binding is deprecated does not resolve it -@test !Base.isdeprecated(M29962, :sin) && !Base.isbindingresolved(M29962, :sin) - # @locals using Base: @locals let diff --git a/test/show.jl b/test/show.jl index 75f04c1e02096c..99bb14df15b234 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1903,15 +1903,6 @@ end b = IOBuffer() show(IOContext(b, :module => @__MODULE__), TypeA) @test String(take!(b)) == "TypeA" - - # issue #26354; make sure testing for symbol visibility doesn't cause - # spurious binding resolutions - show(IOContext(b, :module => TestShowType), Base.Pair) - @test !Base.isbindingresolved(TestShowType, :Pair) - @test String(take!(b)) == "Core.Pair" - show(IOContext(b, :module => TestShowType), Base.Complex) - @test Base.isbindingresolved(TestShowType, :Complex) - @test String(take!(b)) == "Complex" end @testset "typeinfo" begin diff --git a/test/staged.jl b/test/staged.jl index f3dbdcd73d8115..6811bc05a9b68b 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -312,7 +312,7 @@ end :(global x33243 = 2) end @test_throws ErrorException f33243() -global x33243 +global x33243::Any @test f33243() === 2 @test x33243 === 2 diff --git a/test/syntax.jl b/test/syntax.jl index f29dd4978d3097..ef8999c6b86377 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2652,10 +2652,10 @@ using ..Mod end @test Mod3.f(10) == 21 @test !isdefined(Mod3, :func) -@test_throws ErrorException("invalid method definition in Mod3: function Mod3.f must be explicitly imported to be extended") Core.eval(Mod3, :(f(x::Int) = x)) +@test_throws ErrorException("invalid method definition in Mod3: function Mod.f must be explicitly imported to be extended") Core.eval(Mod3, :(f(x::Int) = x)) @test !isdefined(Mod3, :always_undef) # resolve this binding now in Mod3 -@test_throws ErrorException("invalid method definition in Mod3: exported function Mod.always_undef does not exist") Core.eval(Mod3, :(always_undef(x::Int) = x)) -@test_throws ErrorException("cannot declare Mod3.always_undef constant; it was already declared as an import") Core.eval(Mod3, :(const always_undef = 3)) +@test Core.eval(Mod3, :(always_undef(x::Int) = x)) == invokelatest(getglobal, Mod3, :always_undef) +@test Core.eval(Mod3, :(const always_undef = 3)) == invokelatest(getglobal, Mod3, :always_undef) @test_throws ErrorException("cannot declare Mod3.f constant; it was already declared as an import") Core.eval(Mod3, :(const f = 3)) @test_throws ErrorException("cannot declare Mod.maybe_undef constant; it was already declared global") Core.eval(Mod, :(const maybe_undef = 3)) @@ -4046,3 +4046,9 @@ function fs56711() return f end @test !@isdefined(x_should_not_be_defined) + +# Test that importing twice is allowed without warning +@test_nowarn baremodule ImportTwice + import ..Base + using .Base: zero, zero +end diff --git a/test/worlds.jl b/test/worlds.jl index 48fb6593d3a379..6f0a5e8bbfa1ed 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 + 2 +@test tls_world_age() == wc265 + 1 (function () global wc265_41332b = Task(tls_world_age) @eval f265(::Any) = 1.0 @@ -115,15 +115,15 @@ wc265_41332a = Task(tls_world_age) global wc265_41332d = Task(tls_world_age) nothing end)() -@test wc265 + 10 == get_world_counter() == tls_world_age() +@test wc265 + 9 == get_world_counter() == tls_world_age() schedule(wc265_41332a) schedule(wc265_41332b) schedule(wc265_41332c) schedule(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) +@test wc265 + 7 == fetch(wc265_41332b) +@test wc265 + 9 == fetch(wc265_41332c) +@test wc265 + 7 == fetch(wc265_41332d) chnls, tasks = Base.channeled_tasks(2, wfunc) t265 = tasks[1] @@ -509,3 +509,18 @@ struct FooBackdated FooBackdated() = new(FooBackdated[]) end @test Base.invoke_in_world(before_backdate_age, isdefined, @__MODULE__, :FooBackdated) + +# Test that ambiguous binding intersect the using'd binding's world ranges +module AmbigWorldTest + using Test + module M1; export x; end + module M2; export x; end + using .M1, .M2 + Core.eval(M1, :(x=1)) + Core.eval(M2, :(x=2)) + @test_throws UndefVarError x + @test convert(Core.Binding, GlobalRef(@__MODULE__, :x)).partitions.min_world == max( + convert(Core.Binding, GlobalRef(M1, :x)).partitions.min_world, + convert(Core.Binding, GlobalRef(M2, :x)).partitions.min_world + ) +end