aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-26 04:15:12 +0200
committerMel <mel@rnrd.eu>2026-05-26 04:15:12 +0200
commitbf82cbd2a7d4e234313e253cb6f7dd351b2c5c5a (patch)
tree42dcf198deee1dae3c9b3586dc37e6dd23938244 /boot
parent900b412b052a0f745f0853d6703e4bafbb0a3ce5 (diff)
downloadcatskill-bf82cbd2a7d4e234313e253cb6f7dd351b2c5c5a.tar.zst
catskill-bf82cbd2a7d4e234313e253cb6f7dd351b2c5c5a.zip
Locate and pass through captured locals to stateful closures
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
-rw-r--r--boot/ir.c101
-rw-r--r--boot/lower.c171
-rw-r--r--boot/transpile.c116
-rw-r--r--boot/visit/ir.c51
4 files changed, 348 insertions, 91 deletions
diff --git a/boot/ir.c b/boot/ir.c
index 164d9a4..f86d63f 100644
--- a/boot/ir.c
+++ b/boot/ir.c
@@ -137,6 +137,13 @@ struct Param
struct Type_Ref type;
};
+// one captured local in a closing function.
+struct Capture
+{
+ struct String name;
+ struct Type_Ref type; // referenced by capture
+};
+
// minimal lowered statement set.
enum Statement_Kind
{
@@ -239,7 +246,7 @@ enum Expression_Kind
EXPRESSION_FLOAT_LITERAL,
EXPRESSION_STRING_LITERAL,
EXPRESSION_BOOLEAN_LITERAL,
- EXPRESSION_NAME,
+ EXPRESSION_REFERENCE,
EXPRESSION_UNARY_OPERATION,
EXPRESSION_BINARY_OPERATION,
EXPRESSION_SIZEOF_OPERATION,
@@ -249,7 +256,6 @@ enum Expression_Kind
EXPRESSION_CAST,
EXPRESSION_CONSTRUCT,
EXPRESSION_INCREMENT_DECREMENT,
- EXPRESSION_FUNCTION_REF,
};
struct Expression_Integer_Literal
@@ -272,11 +278,56 @@ struct Expression_Bool_Literal
bool value;
};
-struct Expression_Name
+enum Reference_Kind
+{
+ REFERENCE_NONE,
+ REFERENCE_NAME,
+ REFERENCE_FUNCTION,
+ REFERENCE_CAPTURE,
+};
+
+// a simple, by-name reference to some language object,
+// requiring no special treatment.
+struct Reference_Name
{
struct String name;
};
+// a reference to a function, either a named, thin one,
+// or a fat, synthetic one.
+// when a reference points to a thin named function (basically any
+// normal function that's not a closure) and is not a simple, direct call,
+// we will have to call out to a wrapping thunk, to match the fat
+// calling convention of closures.
+struct Reference_Function
+{
+ Function_Id function_id;
+ // matching closure type for the function's signature.
+ Type_Id closure_type_id;
+};
+
+// a reference to a captured local from another, enclosing
+// scope within a closure function.
+// will be gathered from the closure state.
+struct Reference_Capture
+{
+ struct String name;
+};
+
+union Reference_Value
+{
+ struct Reference_Name name;
+ struct Reference_Function function;
+ struct Reference_Capture capture;
+};
+
+// a reference to some other language object.
+struct Expression_Reference
+{
+ enum Reference_Kind kind;
+ union Reference_Value value;
+};
+
struct Expression_Unary_Operator
{
enum Unary_Operation operation;
@@ -356,21 +407,13 @@ struct Expression_Increment_Decrement
struct Expression* subject;
};
-// reference to a known catskill function in a value context.
-struct Expression_Function_Ref
-{
- Function_Id function_id;
- // matching synthetic closure type for the function's signature.
- Type_Id closure_type_id;
-};
-
union Expression_Value
{
struct Expression_Integer_Literal integer_literal;
struct Expression_Float_Literal float_literal;
struct Expression_String_Literal string_literal;
struct Expression_Bool_Literal bool_literal;
- struct Expression_Name name;
+ struct Expression_Reference reference;
struct Expression_Unary_Operator unary_operator;
struct Expression_Binary_Operator binary_operator;
struct Expression_Sizeof_Operator sizeof_operator;
@@ -380,7 +423,6 @@ union Expression_Value
struct Expression_Cast cast;
struct Expression_Construct construct;
struct Expression_Increment_Decrement increment_decrement;
- struct Expression_Function_Ref function_ref;
};
struct Expression
@@ -420,6 +462,12 @@ struct Function
// synthesized lazily on first reference in value context, or 0.
Type_Id closure_type_id;
+ // locals captured from enclosing function scopes, filled during body
+ // lowering. (only valid for stateful lambdas)
+ Array(struct Capture) captures;
+ // synthetic structure containing all captured locals. (only stateful lambdas)
+ Type_Id capture_state_type_id;
+
// tree-side header which produced this function node.
// required for synthesizing the closure type after function is lowered.
struct Tree_Function_Header* ast_header;
@@ -428,6 +476,11 @@ struct Function
// to collect all top-declarations.
struct Tree_Block* ast_body;
struct Block* body;
+
+ // has this function been processed to completion already?
+ // synthetic lambdas get their bodies lowered directly when encountered,
+ // unlike other function declarations, to get access to the current local stack.
+ bool completed;
};
struct Type_Hash_To_Id
@@ -561,6 +614,7 @@ function_new(Function_Id id, struct String name)
*function = (struct Function){
.id = id,
.name = name,
+ .captures = array_new(struct Capture, 4),
};
return function;
}
@@ -636,8 +690,9 @@ struct Expression*
ir_make_name(struct String name, struct Span span)
{
union Expression_Value v = { 0 };
- v.name.name = name;
- return expression_new(EXPRESSION_NAME, v, span);
+ v.reference.kind = REFERENCE_NAME;
+ v.reference.value.name.name = name;
+ return expression_new(EXPRESSION_REFERENCE, v, span);
}
struct Expression*
@@ -729,12 +784,22 @@ ir_make_increment_decrement(
}
struct Expression*
+ir_make_capture_ref(struct String name, struct Span span)
+{
+ union Expression_Value v = { 0 };
+ v.reference.kind = REFERENCE_CAPTURE;
+ v.reference.value.capture.name = name;
+ return expression_new(EXPRESSION_REFERENCE, v, span);
+}
+
+struct Expression*
ir_make_function_ref(Function_Id function_id, Type_Id closure_type_id, struct Span span)
{
union Expression_Value v = { 0 };
- v.function_ref.function_id = function_id;
- v.function_ref.closure_type_id = closure_type_id;
- return expression_new(EXPRESSION_FUNCTION_REF, v, span);
+ v.reference.kind = REFERENCE_FUNCTION;
+ v.reference.value.function.function_id = function_id;
+ v.reference.value.function.closure_type_id = closure_type_id;
+ return expression_new(EXPRESSION_REFERENCE, v, span);
}
struct Type_Ref
diff --git a/boot/lower.c b/boot/lower.c
index ba83b39..81f39a5 100644
--- a/boot/lower.c
+++ b/boot/lower.c
@@ -33,6 +33,18 @@ struct Local_Variable
struct Type_Ref type; // TODO: type-check & fill out empty
};
+// result of looking up a local that may turn out to be a capture for
+// the function we are currently inside.
+struct Lower_Local_Lookup_Capture
+{
+ // was the local found in an enclosing function?
+ bool captured;
+ // the captured local's declared type
+ struct Type_Ref type;
+ // which function does this capture belong to?
+ struct Function* captured_into;
+};
+
// what kind of scope is this?
// declared on every lexical scope so we can make sure that the scopes are balanced.
enum Scope_Type
@@ -65,6 +77,14 @@ struct Scope
{
enum Scope_Type type;
Array(struct Local_Variable) variables;
+
+ // function this scope is lexically contained in.
+ // for function scopes, references the function itself,
+ // for block scopes, references the enclosing function.
+ // used to check whether references cross function scope boundary,
+ // signifying a closure capture.
+ struct Function* containing_function;
+
// TODO: add local closure functions & local types.
};
@@ -668,6 +688,46 @@ lower_function_closure_type(struct Lower_Context* ctx, struct Function* fn)
return ref.type_id;
}
+// synthesize the per-lambda state struct that carries pointers to each
+// captured local.
+// each captured local is stored by reference.
+// NOTE(mel): the transpiler pass will output this as a normal structure,
+// does it make sense to distinguish these state types as something else?
+// it muddles distinguishing a type that the user wrote, and a type we synthesized.
+Type_Id
+lower_function_capture_state_type(struct Lower_Context* ctx, struct Function* fn)
+{
+ if (fn->capture_state_type_id != 0) return fn->capture_state_type_id;
+ if (array_length(&fn->captures) == 0) return 0;
+
+ Type_Id id = array_length(&ctx->unit->types.entries);
+ struct String name = lower_synthesize_type_name(ctx);
+ struct Type* type = type_new(id, TYPE_STRUCTURE, name, fn->ast_header->span);
+ type->synthetic = true;
+ type->depends_on = array_new(Type_Id, 8);
+ type->value.structure.fields = array_new(struct Field, 8);
+
+ FOR_EACH_ARRAY (struct Capture, cap, &fn->captures) {
+ // each field carries a pointer to the captured local. peel any
+ // existing modifiers off the captured type and prepend a reference.
+ // TODO: correctly nest all modifiers here!
+ struct Type_Ref ptr = {
+ .type_id = cap->type.type_id,
+ .mods = array_new(enum Type_Modifier, 4),
+ };
+ enum Type_Modifier ref_mod = TYPE_MOD_REFERENCE;
+ array_push(&ptr.mods, &ref_mod);
+ FOR_EACH_ARRAY (enum Type_Modifier, mod, &cap->type.mods) array_push(&ptr.mods, mod);
+
+ struct Field f = { .name = cap->name, .type = ptr };
+ array_push(&type->value.structure.fields, &f);
+ }
+
+ array_push(&ctx->unit->types.entries, &type);
+ fn->capture_state_type_id = id;
+ return id;
+}
+
void
lower_fill_type_body(struct Lower_Context* ctx, struct Type* type, struct Tree_Type* tree_type)
{
@@ -858,6 +918,12 @@ struct Block* lower_block(struct Lower_Context* ctx, struct Tree_Block* tree_blo
struct Statement* lower_statement(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt);
struct Expression* lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr);
bool lower_name_is_function_typed_local(struct Lower_Context* ctx, struct String name);
+struct Lower_Local_Lookup_Capture
+lower_local_lookup_capturing(struct Lower_Context* ctx, struct String name);
+void lower_record_capture(struct Function* fn, struct String name, struct Type_Ref type);
+void lower_push_scope_function(struct Lower_Context* ctx, struct Function* owner);
+void lower_pop_scope(struct Lower_Context* ctx, enum Scope_Type expected);
+void lower_declare_local(struct Lower_Context* ctx, struct String name, struct Type_Ref type);
struct Expression*
lower_expression_integer_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr)
@@ -890,16 +956,31 @@ lower_expression_boolean_literal(struct Lower_Context* ctx, struct Tree_Expressi
struct Expression*
lower_expression_name(struct Lower_Context* ctx, struct Tree_Expression* tree_expr)
{
+ struct String name = tree_expr->value.name.name;
+
// names that resolve to a catskill function become a function reference.
Function_Id fn_id;
- if (lower_function_lookup_by_name(ctx->unit, tree_expr->value.name.name, &fn_id)) {
+ if (lower_function_lookup_by_name(ctx->unit, name, &fn_id)) {
// since this path is only taken outside direct call-positions,
// the function will need a closure type to wrap it as a value.
struct Function* fn = *array_at(struct Function*, &ctx->unit->functions.entries, fn_id);
Type_Id closure_type_id = lower_function_closure_type(ctx, fn);
return ir_make_function_ref(fn_id, closure_type_id, tree_expr->span);
}
- return ir_make_name(tree_expr->value.name.name, tree_expr->span);
+
+ // locals that were defined in an upper function scope are captured.
+ struct Lower_Local_Lookup_Capture lookup = lower_local_lookup_capturing(ctx, name);
+ if (lookup.captured) {
+ lower_record_capture(lookup.captured_into, name, lookup.type);
+ return ir_make_capture_ref(name, tree_expr->span);
+ }
+
+ // unresolved names will continue being bare names.
+ // this is required to account for all non-language objects that might
+ // come from c, or the runtime, or some c-header include we can't see.
+ // the backend will note any name reference which is still unresolved
+ // after taking every source into account.
+ return ir_make_name(name, tree_expr->span);
}
// any groups are discarded in the lowered representation, their presence
@@ -1239,9 +1320,22 @@ lower_expression_function(struct Lower_Context* ctx, struct Tree_Expression* tre
fn->synthetic = true;
lower_fill_function_signature(ctx, fn, tree_expr);
+ // lower the body inline so name lookups inside see the enclosing
+ // function's scope and can detect captures.
+ lower_push_scope_function(ctx, fn);
+ FOR_EACH_ARRAY (struct Param, param, &fn->params)
+ lower_declare_local(ctx, param->name, param->type);
+ fn->body = lower_block(ctx, fn->ast_body);
+ lower_pop_scope(ctx, SCOPE_TYPE_FUNCTION);
+
+ fn->completed = true; // mark the function as fully lowered.
+
// a function literal is always in value context, we can synthesize
// the closure type eagerly right here.
Type_Id closure_type_id = lower_function_closure_type(ctx, fn);
+ // if the body captured anything, also synthesize the state type.
+ lower_function_capture_state_type(ctx, fn);
+
return ir_make_function_ref(id, closure_type_id, tree_expr->span);
}
@@ -1312,6 +1406,15 @@ lower_expression(struct Lower_Context* ctx, struct Tree_Expression* tree_expr)
}
}
+// look up the function whose body we are currently inside. (if there is one)
+struct Function*
+lower_current_function(struct Lower_Context* ctx)
+{
+ if (ctx->scope_stack.length == 0) return nil;
+ struct Scope* top = array_at(struct Scope, &ctx->scope_stack, ctx->scope_stack.length - 1);
+ return top->containing_function;
+}
+
// push a fresh lexical scope onto the lowering stack.
// do not forget to also pop this scope once we leave it!
void
@@ -1320,6 +1423,21 @@ lower_push_scope(struct Lower_Context* ctx, enum Scope_Type type)
struct Scope s = {
.type = type,
.variables = array_new(struct Local_Variable, 16),
+ // block scopes inherit the function from their upper scope
+ .containing_function = lower_current_function(ctx),
+ };
+ array_push(&ctx->scope_stack, &s);
+}
+
+// push a function scope.
+void
+lower_push_scope_function(struct Lower_Context* ctx, struct Function* owner)
+{
+ struct Scope s = {
+ .type = SCOPE_TYPE_FUNCTION,
+ .variables = array_new(struct Local_Variable, 16),
+ // the owner of this scope is the function that created it
+ .containing_function = owner,
};
array_push(&ctx->scope_stack, &s);
}
@@ -1387,6 +1505,44 @@ lower_local_lookup(struct Lower_Context* ctx, struct String name, struct Type_Re
return false;
}
+// walk the scope stack looking for a local, which could be captured.
+// if the local was declared in some enclosing function that's not us,
+// the local becomes captured by our closure function.
+struct Lower_Local_Lookup_Capture
+lower_local_lookup_capturing(struct Lower_Context* ctx, struct String name)
+{
+ struct Function* current = lower_current_function(ctx);
+
+ for (uint i = ctx->scope_stack.length; i > 0; --i) {
+ struct Scope* scope = array_at(struct Scope, &ctx->scope_stack, i - 1);
+ FOR_EACH_ARRAY (struct Local_Variable, var, &scope->variables) {
+ if (!string_equals(var->name, name)) continue;
+
+ bool from_enclosing = current != nil && scope->containing_function != current;
+ return (struct Lower_Local_Lookup_Capture){
+ .captured = from_enclosing,
+ .type = var->type,
+ .captured_into = from_enclosing ? current : nil,
+ };
+ }
+ }
+
+ // no capture.
+ return (struct Lower_Local_Lookup_Capture){ 0 };
+}
+
+// add a captured name to a function's capture list.
+void
+lower_record_capture(struct Function* fn, struct String name, struct Type_Ref type)
+{
+ // prevent duplication, closed-over locals are often referenced multiple times.
+ FOR_EACH_ARRAY (struct Capture, c, &fn->captures) {
+ if (string_equals(c->name, name)) return;
+ }
+ struct Capture cap = { .name = name, .type = type };
+ array_push(&fn->captures, &cap);
+}
+
// is the named local a function-typed value?
// (used to decide direct vs. indirect call dispatch when the call subject is a bare name.)
bool
@@ -1735,18 +1891,21 @@ void
lower_pass_2(struct Lower_Context* ctx)
{
FOR_EACH_ARRAY (struct Function*, fn, &ctx->unit->functions.entries) {
+ // skip functions which have already been processed.
+ // synthetic lambdas get lowered eagerly when encountered to gain
+ // access to the local stack at their location.
+ if ((*fn)->completed) continue;
if (!(*fn)->ast_body) continue;
- // push a function-level scope on top of the persistent top-level
+ // push a function-level scope on top of the persistent top-level unit
// scope, holding the parameter names.
- // the function body will also push a new block scope,
- // separating the parameters and the local declarations.
- lower_push_scope(ctx, SCOPE_TYPE_FUNCTION);
+ lower_push_scope_function(ctx, *fn);
FOR_EACH_ARRAY (struct Param, param, &(*fn)->params) {
lower_declare_local(ctx, param->name, param->type);
}
(*fn)->body = lower_block(ctx, (*fn)->ast_body);
+ (*fn)->completed = true;
lower_pop_scope(ctx, SCOPE_TYPE_FUNCTION);
}
diff --git a/boot/transpile.c b/boot/transpile.c
index 9b8f420..ac480cb 100644
--- a/boot/transpile.c
+++ b/boot/transpile.c
@@ -331,12 +331,15 @@ transpile_function_emit_signature(struct Visit* visit, struct Function* function
{
TRANSPILE_PREAMBLE
+ bool has_captures = function->capture_state_type_id != 0;
+
VISIT(visit_type_ref, &function->return_type);
TRANSPILE_WRITE(" %s(", transpile_function_c_name(function));
bool need_separator = false;
if (function->synthetic) {
- // synthetic functions implicitly take in state
- TRANSPILE_WRITE("void* state");
+ // synthetic functions implicitly take in state.
+ // TODO(mel): can we avoid state_void?
+ TRANSPILE_WRITE("void* %s", has_captures ? "state_void" : "state");
need_separator = true;
}
@@ -386,13 +389,18 @@ transpile_visit_function_body(struct Visit* visit, struct Function* function)
return;
}
- // add a void cast for the implicit state argument, in case synthetic
- // function does not need to use it
if (function->synthetic) {
TRANSPILE_WRITE(" {\n");
transpiler->indent_level++;
transpile_emit_indent(transpiler);
- TRANSPILE_WRITE("(void)state;\n");
+ if (function->capture_state_type_id != 0) {
+ // TODO(mel): can we avoid this?
+ struct Type* state_type =
+ transpile_type_at(transpiler, function->capture_state_type_id);
+ TRANSPILE_WRITE("struct %s* state = state_void;\n", string_c_str(state_type->name));
+ } else {
+ TRANSPILE_WRITE("(void)state;\n");
+ }
FOR_EACH_ARRAY (struct Statement*, statement, &function->body->statements)
VISIT(visit_statement, *statement);
transpiler->indent_level--;
@@ -459,11 +467,65 @@ transpile_visit_expression_boolean_literal(struct Visit* visit, struct Expressio
TRANSPILE_WRITE("%s", expression->value.bool_literal.value ? "true" : "false");
}
+// emit a reference.
+// the reference's inner kind. callers in subject-of-call position
+// short-circuit FUNCTION references before reaching this handler.
void
-transpile_visit_expression_name(struct Visit* visit, struct Expression* expression)
+transpile_visit_expression_reference(struct Visit* visit, struct Expression* expression)
{
TRANSPILE_PREAMBLE
- TRANSPILE_WRITE("%s", string_c_str(expression->value.name.name));
+ struct Expression_Reference* ref = &expression->value.reference;
+ switch (ref->kind) {
+ case REFERENCE_NAME:
+ TRANSPILE_WRITE("%s", string_c_str(ref->value.name.name));
+ return;
+
+ case REFERENCE_CAPTURE:
+ TRANSPILE_WRITE("(*state->%s)", string_c_str(ref->value.capture.name));
+ return;
+
+ case REFERENCE_FUNCTION: {
+ struct Function* fn = *array_at(
+ struct Function*, &transpiler->unit->functions.entries,
+ ref->value.function.function_id);
+ struct Type* closure_type =
+ transpile_type_at(transpiler, ref->value.function.closure_type_id);
+
+ const ascii* call_name;
+ struct String thunk_name = string_empty();
+ if (fn->synthetic) {
+ // synthetic lambdas already carry the fat signature
+ call_name = transpile_function_c_name(fn);
+ } else {
+ // named functions go through their thunk converting their
+ // thin calling convention, to the fat lambda convention.
+ thunk_name = transpile_function_thunk_name(fn);
+ call_name = string_c_str(thunk_name);
+ }
+
+ TRANSPILE_WRITE("(struct %s){ .state = ", string_c_str(closure_type->name));
+ if (fn->capture_state_type_id != 0) {
+ // closure with closed-over state, construct a state value.
+ struct Type* state_type = transpile_type_at(transpiler, fn->capture_state_type_id);
+ TRANSPILE_WRITE("&(struct %s){ ", string_c_str(state_type->name));
+ bool first = true;
+ FOR_EACH_ARRAY (struct Capture, cap, &fn->captures) {
+ if (!first) TRANSPILE_WRITE(", ");
+ TRANSPILE_WRITE(".%s = &%s", string_c_str(cap->name), string_c_str(cap->name));
+ first = false;
+ }
+ TRANSPILE_WRITE(" }");
+ } else {
+ TRANSPILE_WRITE("nil");
+ }
+ TRANSPILE_WRITE(", .call = %s }", call_name);
+ return;
+ }
+
+ case REFERENCE_NONE:
+ TRANSPILE_WRITE("/* invalid reference */");
+ return;
+ }
}
void
@@ -533,11 +595,14 @@ transpile_visit_expression_call(struct Visit* visit, struct Expression* expressi
struct Expression* subject = expression->value.call.subject;
bool is_indirect = expression->value.call.is_indirect;
- if (!is_indirect && subject && subject->kind == EXPRESSION_FUNCTION_REF) {
+ bool subject_is_function_ref =
+ subject && subject->kind == EXPRESSION_REFERENCE
+ && subject->value.reference.kind == REFERENCE_FUNCTION;
+ if (!is_indirect && subject_is_function_ref) {
// direct calls emit the subject bare
struct Function* fn = *array_at(
struct Function*, &transpiler->unit->functions.entries,
- subject->value.function_ref.function_id);
+ subject->value.reference.value.function.function_id);
TRANSPILE_WRITE("%s", transpile_function_c_name(fn));
} else if (is_indirect) {
// route through the fat closure value's call slot, passing state
@@ -620,36 +685,6 @@ transpile_visit_expression_construct(struct Visit* visit, struct Expression* exp
}
void
-transpile_visit_expression_function_ref(struct Visit* visit, struct Expression* expression)
-{
- TRANSPILE_PREAMBLE
-
- struct Function* fn = *array_at(
- struct Function*, &transpiler->unit->functions.entries,
- expression->value.function_ref.function_id);
-
- // we only get here when the function reference is explicitly a *value*,
- // any direct calls are filtered out earlier.
- struct Type* closure_type =
- transpile_type_at(transpiler, expression->value.function_ref.closure_type_id);
-
- const ascii* call_name;
- struct String thunk_name = string_empty();
- if (fn->synthetic) {
- // synthetic functions are already fat-by-default, can call directly
- call_name = transpile_function_c_name(fn);
- } else {
- // thin named functions go through their generated thunk to match the
- // fat calling convention
- thunk_name = transpile_function_thunk_name(fn);
- call_name = string_c_str(thunk_name);
- }
-
- TRANSPILE_WRITE(
- "(struct %s){ .state = nil, .call = %s }", string_c_str(closure_type->name), call_name);
-}
-
-void
transpile_visit_expression_increment_decrement(struct Visit* visit, struct Expression* expression)
{
TRANSPILE_PREAMBLE
@@ -839,7 +874,7 @@ struct Visit_Table transpile_body_visit_functions = {
.visit_expression_float_literal = transpile_visit_expression_float_literal,
.visit_expression_string_literal = transpile_visit_expression_string_literal,
.visit_expression_boolean_literal = transpile_visit_expression_boolean_literal,
- .visit_expression_name = transpile_visit_expression_name,
+ .visit_expression_reference = transpile_visit_expression_reference,
.visit_expression_unary_operation = transpile_visit_expression_unary_operation,
.visit_expression_binary_operation = transpile_visit_expression_binary_operation,
.visit_expression_sizeof_operation = transpile_visit_expression_sizeof_operation,
@@ -849,7 +884,6 @@ struct Visit_Table transpile_body_visit_functions = {
.visit_expression_cast = transpile_visit_expression_cast,
.visit_expression_construct = transpile_visit_expression_construct,
.visit_expression_increment_decrement = transpile_visit_expression_increment_decrement,
- .visit_expression_function_ref = transpile_visit_expression_function_ref,
};
// walk a lowered translation unit and emit c source into the transpiler's output.
diff --git a/boot/visit/ir.c b/boot/visit/ir.c
index fa17d9c..ca32a1e 100644
--- a/boot/visit/ir.c
+++ b/boot/visit/ir.c
@@ -53,7 +53,7 @@ struct Visit_Table
void (*visit_expression_float_literal)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_string_literal)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_boolean_literal)(struct Visit* visitor, struct Expression* expr);
- void (*visit_expression_name)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_reference)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_unary_operation)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_binary_operation)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_sizeof_operation)(struct Visit* visitor, struct Expression* expr);
@@ -63,7 +63,6 @@ struct Visit_Table
void (*visit_expression_cast)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_construct)(struct Visit* visitor, struct Expression* expr);
void (*visit_expression_increment_decrement)(struct Visit* visitor, struct Expression* expr);
- void (*visit_expression_function_ref)(struct Visit* visitor, struct Expression* expr);
void (*visit_block)(struct Visit* visitor, struct Block* block);
void (*visit_type_ref)(struct Visit* visitor, struct Type_Ref* ref);
@@ -260,8 +259,8 @@ walk_expression(struct Visit* visit, struct Expression* expr)
case EXPRESSION_BOOLEAN_LITERAL:
VISIT(visit_expression_boolean_literal, expr);
break;
- case EXPRESSION_NAME:
- VISIT(visit_expression_name, expr);
+ case EXPRESSION_REFERENCE:
+ VISIT(visit_expression_reference, expr);
break;
case EXPRESSION_UNARY_OPERATION:
VISIT(visit_expression_unary_operation, expr);
@@ -290,9 +289,6 @@ walk_expression(struct Visit* visit, struct Expression* expr)
case EXPRESSION_INCREMENT_DECREMENT:
VISIT(visit_expression_increment_decrement, expr);
break;
- case EXPRESSION_FUNCTION_REF:
- VISIT(visit_expression_function_ref, expr);
- break;
case EXPRESSION_NONE:
break;
default:
@@ -304,7 +300,7 @@ WALK_LEAF_FUNCTION(walk_expression_integer_literal, struct Expression*);
WALK_LEAF_FUNCTION(walk_expression_float_literal, struct Expression*);
WALK_LEAF_FUNCTION(walk_expression_string_literal, struct Expression*);
WALK_LEAF_FUNCTION(walk_expression_boolean_literal, struct Expression*);
-WALK_LEAF_FUNCTION(walk_expression_name, struct Expression*);
+WALK_LEAF_FUNCTION(walk_expression_reference, struct Expression*);
void
walk_expression_unary_operation(struct Visit* visit, struct Expression* expr)
@@ -366,8 +362,6 @@ walk_expression_increment_decrement(struct Visit* visit, struct Expression* expr
VISIT(visit_expression, expr->value.increment_decrement.subject);
}
-WALK_LEAF_FUNCTION(walk_expression_function_ref, struct Expression*);
-
void
walk_block(struct Visit* visit, struct Block* block)
{
@@ -409,7 +403,7 @@ struct Visit_Table walk_functions = {
.visit_expression_float_literal = walk_expression_float_literal,
.visit_expression_string_literal = walk_expression_string_literal,
.visit_expression_boolean_literal = walk_expression_boolean_literal,
- .visit_expression_name = walk_expression_name,
+ .visit_expression_reference = walk_expression_reference,
.visit_expression_unary_operation = walk_expression_unary_operation,
.visit_expression_binary_operation = walk_expression_binary_operation,
.visit_expression_sizeof_operation = walk_expression_sizeof_operation,
@@ -419,7 +413,6 @@ struct Visit_Table walk_functions = {
.visit_expression_cast = walk_expression_cast,
.visit_expression_construct = walk_expression_construct,
.visit_expression_increment_decrement = walk_expression_increment_decrement,
- .visit_expression_function_ref = walk_expression_function_ref,
.visit_block = walk_block,
.visit_type_ref = walk_type_ref,
@@ -836,10 +829,27 @@ printer_visit_expression_boolean_literal(struct Visit* visit, struct Expression*
}
void
-printer_visit_expression_name(struct Visit* visit, struct Expression* expr)
+printer_visit_expression_reference(struct Visit* visit, struct Expression* expr)
{
PRINTER_PREAMBLE
- PRINT("(name %s)", string_c_str(expr->value.name.name));
+ struct Expression_Reference* ref = &expr->value.reference;
+ switch (ref->kind) {
+ case REFERENCE_NAME:
+ PRINT("(name %s)", string_c_str(ref->value.name.name));
+ break;
+ case REFERENCE_FUNCTION: {
+ struct Function* fn = *array_at(
+ struct Function*, &p->unit->functions.entries, ref->value.function.function_id);
+ PRINT("(fn-ref %s)", string_c_str(fn->name));
+ break;
+ }
+ case REFERENCE_CAPTURE:
+ PRINT("(capture %s)", string_c_str(ref->value.capture.name));
+ break;
+ case REFERENCE_NONE:
+ PRINT("(reference none)");
+ break;
+ }
}
void
@@ -950,16 +960,6 @@ printer_visit_expression_increment_decrement(struct Visit* visit, struct Express
}
void
-printer_visit_expression_function_ref(struct Visit* visit, struct Expression* expr)
-{
- PRINTER_PREAMBLE
-
- struct Function* fn = *array_at(
- struct Function*, &p->unit->functions.entries, expr->value.function_ref.function_id);
- PRINT("(fn-ref %s)", string_c_str(fn->name));
-}
-
-void
printer_visit_block(struct Visit* visit, struct Block* block)
{
PRINTER_PREAMBLE
@@ -1087,7 +1087,7 @@ struct Visit_Table printer_visit_functions = {
.visit_expression_float_literal = printer_visit_expression_float_literal,
.visit_expression_string_literal = printer_visit_expression_string_literal,
.visit_expression_boolean_literal = printer_visit_expression_boolean_literal,
- .visit_expression_name = printer_visit_expression_name,
+ .visit_expression_reference = printer_visit_expression_reference,
.visit_expression_unary_operation = printer_visit_expression_unary_operation,
.visit_expression_binary_operation = printer_visit_expression_binary_operation,
.visit_expression_sizeof_operation = printer_visit_expression_sizeof_operation,
@@ -1097,7 +1097,6 @@ struct Visit_Table printer_visit_functions = {
.visit_expression_cast = printer_visit_expression_cast,
.visit_expression_construct = printer_visit_expression_construct,
.visit_expression_increment_decrement = printer_visit_expression_increment_decrement,
- .visit_expression_function_ref = printer_visit_expression_function_ref,
.visit_block = printer_visit_block,
.visit_type_ref = printer_visit_type_ref,