Skip to content

Commit

Permalink
Move args & sparams into lambda form and remove LambdaInfo
Browse files Browse the repository at this point in the history
This form where `K"lambda"` has four children [args, static_parameters,
body, ret_var] feels more natural as it keeps AST pieces within the AST
rather than as auxiliary attributes. These pieces do still need special
treatment in scope resolution, but lambdas are already special there.
  • Loading branch information
c42f committed Oct 12, 2024
1 parent dcbe20c commit 645eaff
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 46 deletions.
20 changes: 7 additions & 13 deletions src/desugaring.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
# Lowering Pass 2 - syntax desugaring

struct LambdaInfo
# TODO: Make SyntaxList concretely typed?
args::SyntaxList
static_parameters::SyntaxList
ret_var::Union{Nothing,SyntaxTree}
is_toplevel_thunk::Bool
end

struct DesugaringContext{GraphType} <: AbstractLoweringContext
graph::GraphType
bindings::Bindings
Expand All @@ -22,7 +14,7 @@ function DesugaringContext(ctx)
value=Any, name_val=String,
scope_type=Symbol, # :hard or :soft
var_id=IdTag,
lambda_info=LambdaInfo)
is_toplevel_thunk=Bool)
DesugaringContext(graph, ctx.bindings, ctx.scope_layers, ctx.current_layer.mod)
end

Expand Down Expand Up @@ -1324,11 +1316,10 @@ function expand_function_def(ctx, ex, docs)
if kind(name) == K"::"
if numchildren(name) == 1
farg = @ast ctx name [K"::"
new_mutable_var(ctx, name, "#self#")
"#self#"::K"Placeholder"
name[1]
]
else
TODO("Fixme type")
farg = name
end
else
Expand All @@ -1340,7 +1331,7 @@ function expand_function_def(ctx, ex, docs)
func_var_assignment = @ast ctx name [K"=" func_var [K"method" function_name]]
end
farg = @ast ctx name [K"::"
new_mutable_var(ctx, name, "#self#")
"#self#"::K"Placeholder"
[K"call"
"Typeof"::K"core"
func_var
Expand Down Expand Up @@ -1401,8 +1392,11 @@ function expand_function_def(ctx, ex, docs)
[K"method"
function_name
method_metadata
[K"lambda"(body, lambda_info=LambdaInfo(arg_names, typevar_names, ret_var, false))
[K"lambda"(body, is_toplevel_thunk=false)
[K"block" arg_names...]
[K"block" typevar_names...]
body
ret_var # might be `nothing` and hence removed
]
]
if !isnothing(docs)
Expand Down
10 changes: 5 additions & 5 deletions src/eval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ end

# Convert SyntaxTree to the CodeInfo+Expr data stuctures understood by the
# Julia runtime
function to_code_info(ex, mod, funcname, nargs, slots)
function to_code_info(ex, mod, funcname, slots)
input_code = children(ex)
code = Any[to_lowered_expr(mod, ex) for ex in input_code]

Expand All @@ -111,6 +111,7 @@ function to_code_info(ex, mod, funcname, nargs, slots)
# - call site @assume_effects
ssaflags = zeros(UInt32, length(code))

nargs = sum((s.kind==:argument for s in slots), init=0)
slotnames = Vector{Symbol}(undef, length(slots))
slot_rename_inds = Dict{String,Int}()
slotflags = Vector{UInt8}(undef, length(slots))
Expand Down Expand Up @@ -212,12 +213,11 @@ function to_lowered_expr(mod, ex)
TODO(ex, "Convert SyntaxTree to Expr")
end
elseif k == K"code_info"
funcname = ex.lambda_info.is_toplevel_thunk ?
funcname = ex.is_toplevel_thunk ?
"top-level scope" :
"none" # FIXME
nargs = length(ex.lambda_info.args)
ir = to_code_info(ex[1], mod, funcname, nargs, ex.slots)
if ex.lambda_info.is_toplevel_thunk
ir = to_code_info(ex[1], mod, funcname, ex.slots)
if ex.is_toplevel_thunk
Expr(:thunk, ir)
else
ir
Expand Down
23 changes: 13 additions & 10 deletions src/linear_ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -919,37 +919,40 @@ end

struct Slot
name::String
kind::Symbol
# <- todo: flags here etc
end

function compile_lambda(outer_ctx, ex)
lambda_info = ex.lambda_info
# TODO: Add assignments for reassigned arguments to body using lambda_info.args
ctx = LinearIRContext(outer_ctx, lambda_info.is_toplevel_thunk, ex.lambda_locals, lambda_info.ret_var)
compile_body(ctx, ex[1])
lambda_args = ex[1]
static_parameters = ex[2]
ret_var = numchildren(ex) == 4 ? ex[4] : nothing
# TODO: Add assignments for reassigned arguments to body using lambda_args
ctx = LinearIRContext(outer_ctx, ex.is_toplevel_thunk, ex.lambda_locals, ret_var)
compile_body(ctx, ex[3])
slots = Vector{Slot}()
slot_rewrites = Dict{IdTag,Int}()
for arg in lambda_info.args
for arg in children(lambda_args)
if kind(arg) == K"Placeholder"
# Unused functions arguments like: `_` or `::T`
push!(slots, Slot(arg.name_val))
push!(slots, Slot(arg.name_val, :argument))
else
@assert kind(arg) == K"BindingId"
id = arg.var_id
info = lookup_binding(ctx.bindings, id)
@assert info.kind == :local || info.kind == :argument
push!(slots, Slot(info.name))
push!(slots, Slot(info.name, :argument))
slot_rewrites[id] = length(slots)
end
end
# Sorting the lambda locals is required to remove dependence on Dict iteration order.
for id in sort(collect(ex.lambda_locals))
info = lookup_binding(ctx.bindings, id)
@assert info.kind == :local
push!(slots, Slot(info.name))
push!(slots, Slot(info.name, :local))
slot_rewrites[id] = length(slots)
end
for (i,arg) in enumerate(lambda_info.static_parameters)
for (i,arg) in enumerate(children(static_parameters))
@assert kind(arg) == K"BindingId"
id = arg.var_id
info = lookup_binding(ctx.bindings, id)
Expand All @@ -960,7 +963,7 @@ function compile_lambda(outer_ctx, ex)
code = renumber_body(ctx, ctx.code, slot_rewrites)
makenode(ctx, ex, K"code_info",
makenode(ctx, ex[1], K"block", code),
lambda_info=lambda_info,
is_toplevel_thunk=ex.is_toplevel_thunk,
slots=slots
)
end
Expand Down
45 changes: 27 additions & 18 deletions src/scope_analysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,10 @@ end
# Analyze identifier usage within a scope, adding all newly discovered
# identifiers to ctx.bindings and returning a lookup table from identifier
# names to their variable IDs
function analyze_scope(ctx, ex, scope_type, lambda_info)
function analyze_scope(ctx, ex, scope_type, is_toplevel_global_scope=false,
lambda_args=nothing, lambda_static_parameters=nothing)
parentscope = isempty(ctx.scope_stack) ? nothing : ctx.scope_stack[end]
is_outer_lambda_scope = kind(ex) == K"lambda"
is_toplevel_global_scope = !isnothing(lambda_info) && lambda_info.is_toplevel_thunk
in_toplevel_thunk = is_toplevel_global_scope ||
(!is_outer_lambda_scope && parentscope.in_toplevel_thunk)

Expand Down Expand Up @@ -231,9 +231,9 @@ function analyze_scope(ctx, ex, scope_type, lambda_info)
end
end
end
if !isnothing(lambda_info)
add_lambda_args(lambda_info.args, :argument)
add_lambda_args(lambda_info.static_parameters, :static_parameter)
if !isnothing(lambda_args)
add_lambda_args(lambda_args, :argument)
add_lambda_args(lambda_static_parameters, :static_parameter)
end

global_keys = Set(first(g) for g in globals)
Expand Down Expand Up @@ -410,20 +410,26 @@ function _resolve_scopes(ctx, ex::SyntaxTree)
update_binding!(ctx, id; is_always_defined=true)
makeleaf(ctx, ex, K"TOMBSTONE")
elseif k == K"lambda"
lambda_info = ex.lambda_info
scope = analyze_scope(ctx, ex, nothing, lambda_info)
is_toplevel_thunk = ex.is_toplevel_thunk
scope = analyze_scope(ctx, ex, nothing, is_toplevel_thunk,
children(ex[1]), children(ex[2]))

push!(ctx.scope_stack, scope)
arg_bindings = _resolve_scopes(ctx, lambda_info.args)
sparm_bindings = _resolve_scopes(ctx, lambda_info.static_parameters)
body = _resolve_scopes(ctx, only(children(ex)))
arg_bindings = _resolve_scopes(ctx, ex[1])
sparm_bindings = _resolve_scopes(ctx, ex[2])
body = _resolve_scopes(ctx, ex[3])
ret_var = numchildren(ex) == 4 ? _resolve_scopes(ctx, ex[4]) : nothing
pop!(ctx.scope_stack)
# TODO: add a lambda locals field to lambda_info or make a new struct
# containing the additional info ??
new_info = LambdaInfo(arg_bindings, sparm_bindings,
lambda_info.ret_var, lambda_info.is_toplevel_thunk)
makenode(ctx, ex, K"lambda", body; lambda_info=new_info, lambda_locals=scope.lambda_locals)

@ast ctx ex [K"lambda"(lambda_locals=scope.lambda_locals,
is_toplevel_thunk=is_toplevel_thunk)
arg_bindings
sparm_bindings
body
ret_var
]
elseif k == K"scope_block"
scope = analyze_scope(ctx, ex, ex.scope_type, nothing)
scope = analyze_scope(ctx, ex, ex.scope_type)
push!(ctx.scope_stack, scope)
body = SyntaxList(ctx)
for e in children(ex)
Expand Down Expand Up @@ -521,8 +527,11 @@ function _resolve_scopes(ctx, exs::AbstractVector)
end

function resolve_scopes(ctx::ScopeResolutionContext, ex)
thunk = makenode(ctx, ex, K"lambda", ex;
lambda_info=LambdaInfo(SyntaxList(ctx), SyntaxList(ctx), nothing, true))
thunk = @ast ctx ex [K"lambda"(is_toplevel_thunk=true)
[K"block"]
[K"block"]
ex
]
return _resolve_scopes(ctx, thunk)
end

Expand Down

0 comments on commit 645eaff

Please sign in to comment.