/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
base/scopedvalues.jl
310 строк
7 KB
Shuhei Kadowaki
Base: Remove dead stores and unused bindings flagged by JETLS (#62534)
28 июл 2026, 15:03
Не верифицирован
28 июл 2026, 15:03
8ddf7d8
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license module ScopedValues export ScopedValue, LazyScopedValue, with, @with, ScopedThunk public get using Base: AbstractScopedValue, Scope, ScopeStorage """ LazyScopedValue{T}(f::OncePerProcess{T}) A scoped value that uses an `OncePerProcess{T}` to lazily compute its default value when none has been set in the current scope. Unlike `ScopedValue`, the default is not evaluated at construction time but only when first accessed. # Examples ```julia-repl julia> using Base.ScopedValues; julia> const editor = LazyScopedValue(OncePerProcess(() -> ENV["JULIA_EDITOR"])); julia> editor[] "vim" julia> with(editor => "emacs") do editor[] end "emacs" julia> editor[] "vim" ``` !!! compat "Julia 1.13" LazyScopedValue requires Julia 1.13+. """ mutable struct LazyScopedValue{T} <: AbstractScopedValue{T} const getdefault::OncePerProcess{T} end """ ScopedValue(x) Create a container that propagates values across dynamic scopes. Use [`with`](@ref) to create and enter a new dynamic scope. Values can only be set when entering a new dynamic scope, and the value referred to will be constant during the execution of a dynamic scope. Dynamic scopes are propagated across tasks. # Examples ```jldoctest julia> using Base.ScopedValues; julia> const sval = ScopedValue(1); julia> sval[] 1 julia> with(sval => 2) do sval[] end 2 julia> sval[] 1 ``` !!! compat "Julia 1.11" Scoped values were introduced in Julia 1.11. In Julia 1.8+ a compatible implementation is available from the package ScopedValues.jl. """ mutable struct ScopedValue{T} <: AbstractScopedValue{T} # NOTE this struct must be defined as mutable one since it's used as a key of # `ScopeStorage` dictionary and thus needs object identity const hasdefault::Bool # this field is necessary since isbitstype `default` field may be initialized with undefined value const default::T ScopedValue{T}() where T = new{T}(false) ScopedValue{T}(val) where T = new{T}(true, val) ScopedValue(val::T) where T = new{T}(true, val) end Base.eltype(::AbstractScopedValue{T}) where {T} = T hasdefault(val::ScopedValue) = val.hasdefault hasdefault(val::LazyScopedValue) = true hasdefault(val::Base.CancelTokenKey) = true getdefault(val::ScopedValue) = val.hasdefault ? val.default : throw(KeyError(val)) getdefault(val::LazyScopedValue) = val.getdefault() getdefault(val::Base.CancelTokenKey) = nothing """ isassigned(val::ScopedValue) Test whether a `ScopedValue` has an assigned value. See also: [`ScopedValues.with`](@ref), [`ScopedValues.@with`](@ref), [`ScopedValues.get`](@ref). # Examples ```jldoctest julia> using Base.ScopedValues julia> a = ScopedValue(1); b = ScopedValue{Int}(); julia> isassigned(a) true julia> isassigned(b) false ``` """ function Base.isassigned(val::AbstractScopedValue) hasdefault(val) && return true scope = Core.current_scope()::Union{Scope, Nothing} scope === nothing && return false return haskey((scope::Scope).values, val) end function Base.show(io::IO, scope::Scope) print(io, Scope, "(") first = true for (key, value) in scope.values if first first = false else print(io, ", ") end print(io, isa(key, ScopedValue) ? ScopedValue{eltype(key)} : typeof(key), "@") show(io, Base.objectid(key)) print(io, " => ") show(IOContext(io, :typeinfo => eltype(key)), value) end print(io, ")") end """ get(val::ScopedValue{T})::Union{Nothing, Some{T}} get(val::LazyScopedValue{T})::Union{Nothing, Some{T}} If the scoped value isn't set and doesn't have a default value, return `nothing`. Otherwise returns `Some{T}` with the current value. See also: [`ScopedValues.with`](@ref), [`ScopedValues.@with`](@ref), [`ScopedValues.ScopedValue`](@ref). # Examples ```jldoctest julia> using Base.ScopedValues julia> a = ScopedValue(42); b = ScopedValue{Int}(); julia> ScopedValues.get(a) Some(42) julia> isnothing(ScopedValues.get(b)) true ``` """ function get(val::AbstractScopedValue{T}) where {T} scope = Core.current_scope()::Union{Scope, Nothing} if scope === nothing !hasdefault(val) && return nothing return Some{T}(getdefault(val)) end scope = scope::Scope if hasdefault(val) return Some{T}(Base.get(Base.Fix1(getdefault, val), scope.values, val)::T) else v = Base.KeyValue.get(scope.values, val) v === nothing && return nothing return Some{T}(something(v)::T) end end function Base.getindex(val::AbstractScopedValue{T})::T where T maybe = get(val) maybe === nothing && throw(KeyError(val)) return something(maybe)::T end function Base.show(io::IO, val::AbstractScopedValue) if isa(val, ScopedValue) print(io, ScopedValue) print(io, '{', eltype(val), '}') else print(io, typeof(val)) end print(io, '(') v = get(val) if v !== nothing show(IOContext(io, :typeinfo => eltype(val)), something(v)) end print(io, ')') end """ @with (var::ScopedValue{T} => val)... expr Macro version of `with`. The expression `@with var=>val expr` evaluates `expr` in a new dynamic scope with `var` set to `val`. `val` will be converted to type `T`. `@with var=>val expr` is equivalent to `with(var=>val) do expr end`, but `@with` avoids creating a closure. See also: [`ScopedValues.with`](@ref), [`ScopedValues.ScopedValue`](@ref), [`ScopedValues.get`](@ref). # Examples ```jldoctest julia> using Base.ScopedValues julia> const a = ScopedValue(1); julia> f(x) = a[] + x; julia> @with a=>2 f(10) 12 julia> @with a=>3 begin x = 100 f(x) end 103 ``` """ macro with(exprs...) if length(exprs) > 1 ex = last(exprs) exprs = exprs[1:end-1] elseif length(exprs) == 1 ex = only(exprs) exprs = () else error("@with expects at least one argument") end exprs = map(esc, exprs) Expr(:tryfinally, esc(ex), nothing, :(Scope(Core.current_scope()::Union{Nothing, Scope}, $(exprs...)))) end """ with(f, (var::ScopedValue{T} => val)...) Execute `f` in a new dynamic scope with `var` set to `val`. `val` will be converted to type `T`. See also: [`ScopedValues.@with`](@ref), [`ScopedValues.ScopedValue`](@ref), [`ScopedValues.get`](@ref). # Examples ```jldoctest julia> using Base.ScopedValues julia> a = ScopedValue(1); julia> f(x) = a[] + x; julia> f(10) 11 julia> with(a=>2) do f(10) end 12 julia> f(10) 11 julia> b = ScopedValue(2); julia> g(x) = a[] + b[] + x; julia> with(a=>10, b=>20) do g(30) end 60 julia> with(() -> a[] * b[], a=>3, b=>4) 12 ``` """ function with(f, pair::Pair{<:AbstractScopedValue}, rest::Pair{<:AbstractScopedValue}...) @with(pair, rest..., f()) end with(@nospecialize(f)) = f() macro enter_scope(scope, expr) Expr(:tryfinally, esc(expr), nothing, :(Scope($(esc(scope))::Union{Nothing, Scope}))) end """ ScopedThunk(f) Create a callable that records the current dynamic scope, i.e. all current `ScopedValue`s, along with `f`. When the callable is invoked, it runs `f` in the recorded dynamic scope. """ struct ScopedThunk{F} f::F scope::Union{Nothing, Scope} ScopedThunk{F}(f) where {F} = new{F}(f, Core.current_scope()) end ScopedThunk(f) = ScopedThunk{typeof(f)}(f) (sf::ScopedThunk)() = @enter_scope sf.scope sf.f() end # module ScopedValues