/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
JuliaLowering/src/bindings.jl
189 строк
7 KB
Em Chu
[JuliaLowering] Make `SyntaxTree` a standard tree (#62474)
29 июл 2026, 15:34
Не верифицирован
29 июл 2026, 15:34
92cca2d
Код
Авторство
О чём код?
""" Metadata about a binding """ mutable struct BindingInfo const id::IdTag # Unique integer identifying this binding const name::String const kind::Symbol # :local :global :argument :static_parameter const node_id::SyntaxTree const mod::Union{Nothing,Module} # Set when `kind === :global` type::Union{Nothing,SyntaxTree} # Type, for bindings declared like x::T = 10 lambda_id::Int # from scope resolution; 0 if unresolved is_const::Bool # Constant, cannot be reassigned is_ssa::Bool # Single assignment, defined before use is_internal::Bool # True for internal bindings generated by the compiler is_ambiguous_local::Bool # Local, but would be global in soft scope (ie, the REPL) unboxed::Bool # determined by binding_analysis.jl; flisp generally mutates `capt` instead # flisp: vinfo is_nospecialize::Bool # @nospecialize on this argument (only valid for kind == :argument) is_read::Bool is_called::Bool is_assigned::Bool is_assigned_once::Bool is_captured::Bool is_always_defined::Bool is_used_undef::Bool end """ Metadata about "entities" (variables, constants, etc) in the program. Each entity is associated to a unique integer id, the BindingId. A binding will be inferred for each *name* in the user's source program by symbolic analysis of the source. However, bindings can also be introduced programmatically during lowering or macro expansion: the primary key for bindings is the `BindingId` integer, not a name. """ struct Bindings info::Vector{BindingInfo} end function BindingInfo(bindings::Bindings, name::AbstractString, kind::Symbol, node_id::SyntaxTree; mod::Union{Nothing,Module} = nothing, type::Union{Nothing,SyntaxTree} = nothing, lambda_id::Int = 0, is_const::Bool = false, is_ssa::Bool = false, is_internal::Bool = false, is_ambiguous_local::Bool = false, unboxed::Bool = false, is_nospecialize::Bool = false, is_read::Bool = false, is_called::Bool = false, is_assigned::Bool = false, is_assigned_once::Bool = false, is_captured::Bool = false, is_always_defined::Bool = is_ssa || kind === :argument, is_used_undef::Bool = false) bid = next_binding_id(bindings) b = BindingInfo( bid, name, kind, node_id, mod, type, lambda_id, is_const, is_ssa, is_internal, is_ambiguous_local, unboxed, is_nospecialize, is_read, is_called, is_assigned, is_assigned_once, is_captured, is_always_defined, is_used_undef) add_binding(bindings, b) b end function Base.show(io::IO, binfo::BindingInfo) print(io, "BindingInfo(", binfo.id, ", ", repr(binfo.name), ", ", repr(binfo.kind), ", ", binfo.node_id) !isnothing(binfo.mod) && print(io, ", mod=", binfo.mod) !isnothing(binfo.type) && print(io, ", type=", binfo.type) print(io, ", lambda_id=", binfo.lambda_id) binfo.is_const && print(io, ", is_const=true") binfo.is_ssa && print(io, ", is_ssa=true") binfo.is_internal && print(io, ", is_internal=true") binfo.is_ambiguous_local && print(io, ", is_ambiguous_local=true") binfo.unboxed && print(io, ", unboxed=true") binfo.is_nospecialize && print(io, ", is_nospecialize=true") binfo.is_read && print(io, ", is_read=true") binfo.is_called && print(io, ", is_called=true") binfo.is_assigned && print(io, ", is_assigned=true") binfo.is_assigned_once && print(io, ", is_assigned_once=true") binfo.is_captured && print(io, ", is_captured=true") binfo.is_always_defined && print(io, ", is_always_defined=true") binfo.is_used_undef && print(io, ", is_used_undef=true") print(io, ")") end Bindings() = Bindings(Vector{BindingInfo}()) next_binding_id(bindings::Bindings) = length(bindings.info) + 1 function add_binding(bindings::Bindings, binding) if next_binding_id(bindings) != binding.id error("Use next_binding_id() to create a valid binding id") end push!(bindings.info, binding) end function syntax_id(ex::SyntaxTree) @jl_assert kind(ex) in KSet"BindingId SSAValue slot static_parameter label" ex ex.value::IdTag end function get_binding(bindings::Bindings, x)::BindingInfo id = x isa SyntaxTree ? syntax_id(x) : x bindings.info[id] end function get_binding(ctx::AbstractLoweringContext, x)::BindingInfo get_binding(ctx.bindings::Bindings, x) end function _new_binding(bindings::Bindings, srcref::SyntaxTree, name::AbstractString, kind::Symbol; kws...) # A binding is only useful when it shows up in the tree, so create its tree # node eagerly and share it among uses (see `binding_ex`) bid = next_binding_id(bindings) ex = @ast _ srcref bid::K"BindingId" b = BindingInfo(bindings, name, kind, ex; kws...) return b end # Create a new SSA binding function ssavar(ctx::AbstractLoweringContext, srcref, name="tmp") binding_ex(ctx, _new_binding(ctx.bindings, srcref, name, :local; is_ssa=true, is_internal=true)) end # Create a new local mutable binding or lambda argument function new_local_binding(ctx::AbstractLoweringContext, srcref, name; kind=:local, kws...) @jl_assert kind === :local || kind === :argument srcref nameref = newleaf(srcref, K"Identifier", name) b = _new_binding(ctx.bindings, nameref, name, kind; is_internal=true, kws...) lbindings = current_lambda_bindings(ctx) if !isnothing(lbindings) init_lambda_binding(lbindings, b, false) end binding_ex(ctx, b) end function new_global_binding(ctx::AbstractLoweringContext, srcref, name, mod; kws...) nameref = newleaf(srcref, K"Identifier", name) binding_ex(ctx, _new_binding( ctx.bindings, nameref, name, :global; is_internal=true, mod=mod, kws...)) end function binding_ex(ctx::AbstractLoweringContext, b::BindingInfo) b.node_id end binding_ex(ctx, id::IdTag) = binding_ex(ctx, get_binding(ctx, id)) binding_type_ex(ctx::AbstractLoweringContext, b::BindingInfo) = b.type # One lambda's variables struct LambdaBindings # Binding ID of #self# self::IdTag # For finding the parent lambda in variable analysis scope_id::ScopeId # A map from every referenced local binding ID to whether the local is # captured (true) or native to this lambda (false). References in inner # lambdas count: `inner.locals_capt[id]` implies `haskey(locals_capt, id)` # TODO: If we use scope ID as a lambda ID and give BindingInfo a field # noting which lambda it belongs to, we could just make this a BitSet of # vars present, where we tell if a binding is captured by comparing # this.scope_id with the BindingInfo's scope_id. locals_capt::Dict{IdTag,Bool} end LambdaBindings(self::IdTag = 0, scope_id::ScopeId = 0) = LambdaBindings(self, scope_id, Dict{IdTag,Bool}()) function init_lambda_binding(bindings::LambdaBindings, b::BindingInfo, capt::Bool) bindings.locals_capt[b.id] = capt b.lambda_id = bindings.scope_id end function lambda_bindings(st::SyntaxTree) @jl_assert kind(st) === K"LambdaBindings" st st.value::LambdaBindings end