diff options
| author | Mel <mel@rnrd.eu> | 2026-05-30 23:04:48 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-05-30 23:04:48 +0200 |
| commit | dd9f8fccf405af709c1d655dbec4be1561e990c0 (patch) | |
| tree | 5f05eff13d01df0e1bd365e0c31a9b4b69284bdc | |
| parent | 4ec376b67d605910cb7757dc2e15a28a942f59ca (diff) | |
| download | catskill-dd9f8fccf405af709c1d655dbec4be1561e990c0.tar.zst catskill-dd9f8fccf405af709c1d655dbec4be1561e990c0.zip | |
Define name resolution behavior, clean-up langauge object resolution
Signed-off-by: Mel <mel@rnrd.eu>
| -rw-r--r-- | boot/ir.c | 12 | ||||
| -rw-r--r-- | boot/lower.c | 215 | ||||
| -rw-r--r-- | boot/transpile.c | 4 | ||||
| -rw-r--r-- | boot/visit/ir.c | 2 |
4 files changed, 135 insertions, 98 deletions
diff --git a/boot/ir.c b/boot/ir.c index af631f8..f1ae951 100644 --- a/boot/ir.c +++ b/boot/ir.c @@ -589,6 +589,18 @@ struct Unit Array(struct Lower_Error) lower_errors; }; +struct Type* +unit_get_type(struct Unit* unit, Type_Id id) +{ + return *array_at(struct Type*, &unit->types.entries, id); +} + +struct Function* +unit_get_function(struct Unit* unit, Function_Id id) +{ + return *array_at(struct Function*, &unit->functions.entries, id); +} + REGION(struct Type, type) REGION(struct Function, function) REGION(struct Statement, statement) diff --git a/boot/lower.c b/boot/lower.c index 4d00c5d..df5fd45 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -45,6 +45,24 @@ struct Lower_Local_Lookup_Capture struct Function* owner; }; +enum Name_Resolution_Kind +{ + NAME_UNRESOLVED, + NAME_LOCAL, + NAME_CAPTURE, + NAME_FUNCTION, +}; + +// the result of a name lookup within a scope. +struct Name_Resolution +{ + enum Name_Resolution_Kind kind; + struct Type_Ref type; + // depending on type, either the resolved function, or when + // resolved to a capture, the function the captured local belongs. + struct Function* function; +}; + // what kind of scope is this? // declared on every lexical scope so we can make sure that the scopes are balanced. enum Scope_Type @@ -120,8 +138,8 @@ lower_format_cycle_chain( for (uint k = 0; k < n; ++k) { Type_Id current_id = *array_at(Type_Id, chain, k); Type_Id next_id = *array_at(Type_Id, chain, (k + 1) % n); - struct Type* current = *array_at(struct Type*, &unit->types.entries, current_id); - struct Type* next = *array_at(struct Type*, &unit->types.entries, next_id); + struct Type* current = unit_get_type(unit, current_id); + struct Type* next = unit_get_type(unit, next_id); uint line, column; source_position_from_span(source.source, current->span, &line, &column); @@ -203,28 +221,22 @@ lower_error_to_diagnostic(struct Lower_Error* err, struct Unit* unit, struct Sou } } -bool -lower_type_lookup_by_name(struct Unit* unit, struct String name, Type_Id* out_id) +struct Type* +lower_type_lookup_by_name(struct Unit* unit, struct String name) { FOR_EACH_ARRAY (struct Type_Name_To_Id, mapping, &unit->types.by_name) { - if (string_equals(mapping->name, name)) { - *out_id = mapping->id; - return true; - } + if (string_equals(mapping->name, name)) return unit_get_type(unit, mapping->id); } - return false; + return nil; } -bool -lower_function_lookup_by_name(struct Unit* unit, struct String name, Function_Id* out_id) +struct Function* +lower_function_lookup_by_name(struct Unit* unit, struct String name) { FOR_EACH_ARRAY (struct Function_Name_To_Id, mapping, &unit->functions.by_name) { - if (string_equals(mapping->name, name)) { - *out_id = mapping->id; - return true; - } + if (string_equals(mapping->name, name)) return unit_get_function(unit, mapping->id); } - return false; + return nil; } // registers a basic type object for a primitive, so other types @@ -512,9 +524,9 @@ lower_intern_type_ref(struct Lower_Context* ctx, struct Tree_Type* tree_type) switch (current->type) { case TREE_TYPE_NAME: { - Type_Id id; - if (lower_type_lookup_by_name(ctx->unit, current->value.name.name, &id)) { - ref.type_id = id; + struct Type* type = lower_type_lookup_by_name(ctx->unit, current->value.name.name); + if (type) { + ref.type_id = type->id; } else { lower_push_error( ctx->unit, @@ -600,16 +612,16 @@ lower_match_type_decl( } // register a function shell object, only listing a name and assigning a unique identifier. -bool +// returns the freshly created function, or nil if a same-named entry already exists. +struct Function* lower_register_function_shell(struct Unit* unit, struct String name, struct Span span) { - Function_Id existing; - if (lower_function_lookup_by_name(unit, name, &existing)) { + if (lower_function_lookup_by_name(unit, name)) { lower_push_error( unit, (struct Lower_Error){ .kind = LOWER_ERROR_DUPLICATE_FUNCTION, .span = span, .name = name }); - return false; + return nil; } Function_Id id = array_length(&unit->functions.entries); @@ -618,7 +630,7 @@ lower_register_function_shell(struct Unit* unit, struct String name, struct Span struct Function_Name_To_Id mapping = { .name = name, .id = id }; array_push(&unit->functions.by_name, &mapping); - return true; + return fn; } // register a type shell object, only listing a name and assigning a unique identifier. @@ -626,8 +638,7 @@ lower_register_function_shell(struct Unit* unit, struct String name, struct Span bool lower_register_type_shell(struct Unit* unit, struct String name, struct Span span) { - Type_Id existing; - if (lower_type_lookup_by_name(unit, name, &existing)) { + if (lower_type_lookup_by_name(unit, name)) { lower_push_error( unit, (struct Lower_Error){ .kind = LOWER_ERROR_DUPLICATE_TYPE, .span = span, .name = name }); @@ -734,11 +745,11 @@ lower_fill_type_body(struct Lower_Context* ctx, struct Type* type, struct Tree_T switch (tree_type->type) { case TREE_TYPE_NAME: { type->kind = TYPE_ALIAS; - Type_Id target_id; - if (lower_type_lookup_by_name(ctx->unit, tree_type->value.name.name, &target_id)) { - type->value.alias.target_id = target_id; + struct Type* target = lower_type_lookup_by_name(ctx->unit, tree_type->value.name.name); + if (target) { + type->value.alias.target_id = target->id; // aliases always need their target's full definition. - array_push(&type->depends_on, &target_id); + array_push(&type->depends_on, &target->id); } else { lower_push_error( ctx->unit, @@ -856,22 +867,15 @@ lower_pass_1_fill_bodies(struct Lower_Context* ctx, struct Tree* tree) struct Tree_Expression* fn_expr; if (lower_match_function_decl(stmt, &name, &fn_expr)) { - Function_Id id; - if (lower_function_lookup_by_name(ctx->unit, name, &id)) { - struct Function* fn = - *array_at(struct Function*, &ctx->unit->functions.entries, id); - if (!fn->ast_body) lower_fill_function_signature(ctx, fn, fn_expr); - } + struct Function* fn = lower_function_lookup_by_name(ctx->unit, name); + if (fn && !fn->ast_body) lower_fill_function_signature(ctx, fn, fn_expr); continue; } struct Tree_Type* tree_type; if (lower_match_type_decl(stmt, &name, &tree_type)) { - Type_Id id; - if (lower_type_lookup_by_name(ctx->unit, name, &id)) { - struct Type* type = *array_at(struct Type*, &ctx->unit->types.entries, id); - if (type->kind == TYPE_NONE) lower_fill_type_body(ctx, type, tree_type); - } + struct Type* type = lower_type_lookup_by_name(ctx->unit, name); + if (type && type->kind == TYPE_NONE) lower_fill_type_body(ctx, type, tree_type); continue; } @@ -917,7 +921,6 @@ lower_pass_1(struct Lower_Context* ctx, struct Tree* tree) struct Block* lower_block(struct Lower_Context* ctx, struct Tree_Block* tree_block); 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); @@ -925,6 +928,7 @@ 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 Function* lower_current_function(struct Lower_Context* ctx); +struct Name_Resolution lower_resolve_name(struct Lower_Context* ctx, struct String name); struct Expression* lower_expression_integer_literal(struct Lower_Context* ctx, struct Tree_Expression* tree_expr) @@ -959,34 +963,37 @@ lower_expression_name(struct Lower_Context* ctx, struct Tree_Expression* tree_ex { 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, 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); - } - + struct Name_Resolution resolved = lower_resolve_name(ctx, name); + switch (resolved.kind) { // 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) { + case NAME_CAPTURE: { struct Function* fn = lower_current_function(ctx); // we walk the enclosing function chain to record every in-between capture. - while (fn && fn != lookup.owner) { - lower_record_capture(fn, name, lookup.type); + while (fn && fn != resolved.function) { + lower_record_capture(fn, name, resolved.type); fn = fn->parent_function; } 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); + // names that resolve to a catskill function become a function reference. + case NAME_FUNCTION: { + // since this path is only taken outside direct call-positions, + // the function will need a closure type to wrap it as a value. + Type_Id closure_type_id = lower_function_closure_type(ctx, resolved.function); + return ir_make_function_ref(resolved.function->id, closure_type_id, tree_expr->span); + } + + case NAME_LOCAL: + case NAME_UNRESOLVED: + // 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); + default: + failure("unexpected name resolution kind in `lower_expression_name`"); + } } // any groups are discarded in the lowered representation, their presence @@ -1208,13 +1215,23 @@ lower_expression_call(struct Lower_Context* ctx, struct Tree_Expression* tree_ex // only catskill-side functions are in the function table, calls to libc // and the like go through as positional with no named args allowed. struct Function* callee = nil; - Function_Id callee_id = 0; bool is_indirect = false; if (subject_tree && subject_tree->kind == TREE_EXPRESSION_NAME) { - if (lower_function_lookup_by_name(ctx->unit, subject_tree->value.name.name, &callee_id)) - callee = *array_at(struct Function*, &ctx->unit->functions.entries, callee_id); - else if (lower_name_is_function_typed_local(ctx, subject_tree->value.name.name)) - is_indirect = true; + struct Name_Resolution resolved = lower_resolve_name(ctx, subject_tree->value.name.name); + switch (resolved.kind) { + case NAME_LOCAL: + case NAME_CAPTURE: { + struct Type* t = unit_get_type(ctx->unit, resolved.type.type_id); + if (array_length(&resolved.type.mods) == 0 && t->kind == TYPE_FUNCTION) + is_indirect = true; + break; + } + case NAME_FUNCTION: + callee = resolved.function; + break; + case NAME_UNRESOLVED: + break; + } } else if (subject_tree) { // any subject that isn't a bare name (member, subscript, call result, closure, anything // that's not literally just a name) yields a fat closure value at the call site by default. @@ -1224,7 +1241,7 @@ lower_expression_call(struct Lower_Context* ctx, struct Tree_Expression* tree_ex // for direct calls to a known catskill function, build the reference // subject directly so we don't drag the function into value-context synthesis! struct Expression* subject = - callee ? ir_make_function_ref(callee_id, 0, subject_tree->span) + callee ? ir_make_function_ref(callee->id, 0, subject_tree->span) : lower_expression(ctx, subject_tree); Array(struct Call_Argument) arguments; @@ -1269,8 +1286,8 @@ lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tr }); return nil; } - Type_Id type_id; - if (!lower_type_lookup_by_name(ctx->unit, subject->value.name.name, &type_id)) { + struct Type* type = lower_type_lookup_by_name(ctx->unit, subject->value.name.name); + if (!type) { lower_push_error( ctx->unit, (struct Lower_Error){ @@ -1280,8 +1297,6 @@ lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tr }); return nil; } - - struct Type* type = *array_at(struct Type*, &ctx->unit->types.entries, type_id); Array(struct String) field_names = array_new(struct String, 16); if (type->kind == TYPE_STRUCTURE) { FOR_EACH_ARRAY (struct Field, f, &type->value.structure.fields) @@ -1292,7 +1307,7 @@ lower_expression_construct(struct Lower_Context* ctx, struct Tree_Expression* tr lower_resolve_construct_fields( ctx, tree_expr->span, &tree_expr->value.construct.argument_group, field_names, &fields); - return ir_make_construct(type_id, fields, tree_expr->span); + return ir_make_construct(type->id, fields, tree_expr->span); } // type-as-expression is only valid as the right-side of a top-level binding, @@ -1317,12 +1332,9 @@ lower_expression_function(struct Lower_Context* ctx, struct Tree_Expression* tre // NOTE: we are allowed to register functions whenever we want, // even while iterating over them, the pass will eventually get to them. - if (!lower_register_function_shell(ctx->unit, name, tree_expr->span)) return nil; - - Function_Id id; - lower_function_lookup_by_name(ctx->unit, name, &id); + struct Function* fn = lower_register_function_shell(ctx->unit, name, tree_expr->span); + if (!fn) return nil; - struct Function* fn = *array_at(struct Function*, &ctx->unit->functions.entries, id); fn->synthetic = true; fn->parent_function = lower_current_function(ctx); lower_fill_function_signature(ctx, fn, tree_expr); @@ -1343,7 +1355,7 @@ lower_expression_function(struct Lower_Context* ctx, struct Tree_Expression* tre // 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); + return ir_make_function_ref(fn->id, closure_type_id, tree_expr->span); } struct Expression* @@ -1357,7 +1369,9 @@ lower_expression_increment_decrement(struct Lower_Context* ctx, struct Tree_Expr (struct Lower_Error){ .kind = LOWER_ERROR_UNIMPLEMENTED, .span = tree_expr->span, - .detail = string_from_static_c_string("increment/decrement on non-trivial lvalue"), + .detail = string_from_static_c_string( + "increment/decrement on non-trivial " + "lvalue"), }); return nil; } @@ -1551,16 +1565,25 @@ lower_record_capture(struct Function* fn, struct String name, struct Type_Ref ty 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 -lower_name_is_function_typed_local(struct Lower_Context* ctx, struct String name) +// resolve a name from the current scope context. +struct Name_Resolution +lower_resolve_name(struct Lower_Context* ctx, struct String name) { - struct Type_Ref ref; - if (!lower_local_lookup(ctx, name, &ref)) return false; - if (array_length(&ref.mods) > 0) return false; // references are not callable! - struct Type* type = *array_at(struct Type*, &ctx->unit->types.entries, ref.type_id); - return type->kind == TYPE_FUNCTION; + struct Lower_Local_Lookup_Capture lookup = lower_local_lookup_capturing(ctx, name); + if (lookup.owner) { + return (struct Name_Resolution){ + .kind = lookup.captured ? NAME_CAPTURE : NAME_LOCAL, + .type = lookup.type, + .function = lookup.owner, + }; + } + + // by default functions can be shadowed by locals, hence why + // this function lookup is done after the local one. + struct Function* fn = lower_function_lookup_by_name(ctx->unit, name); + if (fn) return (struct Name_Resolution){ .kind = NAME_FUNCTION, .function = fn }; + + return (struct Name_Resolution){ .kind = NAME_UNRESOLVED }; } struct Statement* @@ -1744,7 +1767,9 @@ lower_statement_loop(struct Lower_Context* ctx, struct Tree_Statement* tree_stmt (struct Lower_Error){ .kind = LOWER_ERROR_UNIMPLEMENTED, .span = tree_stmt->span, - .detail = string_from_static_c_string("for-loop with multi-name declaration"), + .detail = string_from_static_c_string( + "for-loop with multi-name " + "declaration"), }); return nil; } @@ -1941,7 +1966,7 @@ lower_report_type_cycle(struct Unit* unit, struct _Array* chain, uint cycle_star } Type_Id origin_id = *array_at(Type_Id, chain, cycle_start); - struct Type* origin = *array_at(struct Type*, &unit->types.entries, origin_id); + struct Type* origin = unit_get_type(unit, origin_id); lower_push_error( unit, (struct Lower_Error){ @@ -1968,7 +1993,7 @@ lower_topological_sort_dependency_graph(struct Unit* unit) // for i, holds in-degree for type i. Array(uint) in_degree = array_new(uint, n); for (uint i = 0; i < n; ++i) { - struct Type* type = *array_at(struct Type*, &unit->types.entries, i); + struct Type* type = unit_get_type(unit, i); uint deg = array_length(&type->depends_on); array_push(&in_degree, °); } @@ -1980,7 +2005,7 @@ lower_topological_sort_dependency_graph(struct Unit* unit) array_push(&reverse_deps, &slot); } for (uint i = 0; i < n; ++i) { - struct Type* type = *array_at(struct Type*, &unit->types.entries, i); + struct Type* type = unit_get_type(unit, i); Type_Id me = (Type_Id)i; FOR_EACH_ARRAY (Type_Id, dep, &type->depends_on) { struct _Array* slot = array_at(struct _Array, &reverse_deps, *dep); @@ -2029,7 +2054,7 @@ lower_topological_sort_dependency_graph(struct Unit* unit) Type_Id current = start; while (true) { - struct Type* type = *array_at(struct Type*, &unit->types.entries, current); + struct Type* type = unit_get_type(unit, current); Type_Id next = (Type_Id)-1; FOR_EACH_ARRAY (Type_Id, dep, &type->depends_on) { diff --git a/boot/transpile.c b/boot/transpile.c index e0af3bf..af601f0 100644 --- a/boot/transpile.c +++ b/boot/transpile.c @@ -115,7 +115,7 @@ struct Type* transpile_type_at(struct Transpiler* transpiler, Type_Id id) { if (id >= array_length(&transpiler->unit->types.entries)) return nil; - return *array_at(struct Type*, &transpiler->unit->types.entries, id); + return unit_get_type(transpiler->unit, id); } // the c-side form of a primitive catskill type. @@ -946,7 +946,7 @@ transpile_unit(struct Transpiler* transpiler, struct Unit* unit) // pass 2: type definitions visit->table = &transpile_definition_visit_functions; FOR_EACH_ARRAY (Type_Id, ordered_id, &unit->type_emission_order) { - struct Type* type = *array_at(struct Type*, &unit->types.entries, *ordered_id); + struct Type* type = unit_get_type(unit, *ordered_id); VISIT(visit_type, type); } diff --git a/boot/visit/ir.c b/boot/visit/ir.c index ca32a1e..8a0297b 100644 --- a/boot/visit/ir.c +++ b/boot/visit/ir.c @@ -467,7 +467,7 @@ struct Type* printer_type_at(struct Printer* p, Type_Id id) { if (id >= array_length(&p->unit->types.entries)) return nil; - return *array_at(struct Type*, &p->unit->types.entries, id); + return unit_get_type(p->unit, id); } void |
