diff --git a/src/desugaring.jl b/src/desugaring.jl index 585cc2c..63a585f 100644 --- a/src/desugaring.jl +++ b/src/desugaring.jl @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) diff --git a/src/eval.jl b/src/eval.jl index cb55439..0111d3e 100644 --- a/src/eval.jl +++ b/src/eval.jl @@ -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] @@ -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)) @@ -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 diff --git a/src/linear_ir.jl b/src/linear_ir.jl index e84ce0b..3595f87 100644 --- a/src/linear_ir.jl +++ b/src/linear_ir.jl @@ -919,26 +919,29 @@ 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 @@ -946,10 +949,10 @@ function compile_lambda(outer_ctx, ex) 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) @@ -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 diff --git a/src/scope_analysis.jl b/src/scope_analysis.jl index 1d51416..e8be823 100644 --- a/src/scope_analysis.jl +++ b/src/scope_analysis.jl @@ -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) @@ -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) @@ -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) @@ -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