diff --git a/ezc/src/codegen/codegen.c b/ezc/src/codegen/codegen.c index ea1b1cb..c2524ce 100644 --- a/ezc/src/codegen/codegen.c +++ b/ezc/src/codegen/codegen.c @@ -853,6 +853,19 @@ static void emit_func_declaration(CodeGen *cg, AstNode *node, bool is_main) { cg->indent++; AstNode *prev_func = cg->current_func; cg->current_func = node; + + /* Emit named return variable declarations */ + if (node->data.func_decl.return_names) { + for (int i = 0; i < node->data.func_decl.return_type_count; i++) { + if (node->data.func_decl.return_names[i]) { + emit_indent(cg); + emitf(cg, "%s %s;\n", + ez_type_to_c_cg(cg, node->data.func_decl.return_types[i]), + node->data.func_decl.return_names[i]); + } + } + } + if (node->data.func_decl.body) { emit_block(cg, node->data.func_decl.body); /* Emit ensure cleanup at end of function (for implicit returns) */ diff --git a/ezc/src/parser/ast.h b/ezc/src/parser/ast.h index 995132b..8f3df48 100644 --- a/ezc/src/parser/ast.h +++ b/ezc/src/parser/ast.h @@ -248,6 +248,7 @@ struct AstNode { Param *params; int param_count; const char **return_types; + const char **return_names; /* Named return params (NULL if unnamed) */ int return_type_count; AstNode *body; int visibility; /* 0 = public, 1 = private */ diff --git a/ezc/src/parser/parser.c b/ezc/src/parser/parser.c index 96f06d2..dd2b52a 100644 --- a/ezc/src/parser/parser.c +++ b/ezc/src/parser/parser.c @@ -670,13 +670,16 @@ static AstNode *parse_func_declaration(Parser *p) { /* Return type(s) */ node->data.func_decl.return_type_count = 0; node->data.func_decl.return_types = NULL; + node->data.func_decl.return_names = NULL; if (peek_token_is(p, TOK_ARROW)) { next_token(p); /* skip -> */ next_token(p); - int ret_cap = 4; + int ret_cap = 8; node->data.func_decl.return_types = arena_alloc(p->arena, sizeof(const char *) * ret_cap); + node->data.func_decl.return_names = arena_alloc(p->arena, sizeof(const char *) * ret_cap); + memset(node->data.func_decl.return_names, 0, sizeof(const char *) * ret_cap); if (cur_token_is(p, TOK_LPAREN)) { /* Multiple/named return types: @@ -706,31 +709,41 @@ static AstNode *parse_func_declaration(Parser *p) { } if (cur_token_is(p, TOK_IDENT) && peek_token_is(p, TOK_IDENT) && !is_type) { - /* Named return: name type — skip name, use type */ + /* Named return: name type — store both */ + const char *ret_name = p->cur_token.literal; next_token(p); - node->data.func_decl.return_types[node->data.func_decl.return_type_count++] = - p->cur_token.literal; + int idx = node->data.func_decl.return_type_count; + node->data.func_decl.return_names[idx] = ret_name; + node->data.func_decl.return_types[idx] = p->cur_token.literal; + node->data.func_decl.return_type_count++; } else if (cur_token_is(p, TOK_IDENT) && peek_token_is(p, TOK_COMMA) && !is_type) { - /* Shared type: (x, y int) — count names, assign same type to all */ - int shared = 1; + /* Shared type: (x, y int) — collect names, assign same type */ + const char *names[16]; + int shared = 0; + names[shared++] = p->cur_token.literal; while (peek_token_is(p, TOK_COMMA)) { next_token(p); /* skip comma */ next_token(p); /* next name */ - shared++; + names[shared++] = p->cur_token.literal; + if (shared >= 16) break; if (!peek_token_is(p, TOK_COMMA)) break; } /* cur is last name, peek should be the shared type */ if (peek_token_is(p, TOK_IDENT)) { next_token(p); for (int s = 0; s < shared; s++) { - node->data.func_decl.return_types[node->data.func_decl.return_type_count++] = - p->cur_token.literal; + int idx = node->data.func_decl.return_type_count; + node->data.func_decl.return_names[idx] = names[s]; + node->data.func_decl.return_types[idx] = p->cur_token.literal; + node->data.func_decl.return_type_count++; } } } else { - /* Plain type */ - node->data.func_decl.return_types[node->data.func_decl.return_type_count++] = - p->cur_token.literal; + /* Plain type (no name) */ + int idx = node->data.func_decl.return_type_count; + node->data.func_decl.return_names[idx] = NULL; + node->data.func_decl.return_types[idx] = p->cur_token.literal; + node->data.func_decl.return_type_count++; } if (peek_token_is(p, TOK_COMMA)) { next_token(p); diff --git a/ezc/src/typechecker/typechecker.c b/ezc/src/typechecker/typechecker.c index 56abc05..1fae860 100644 --- a/ezc/src/typechecker/typechecker.c +++ b/ezc/src/typechecker/typechecker.c @@ -356,14 +356,12 @@ static void check_statement(TypeChecker *tc, AstNode *node) { resolve_expr(tc, node->data.expr_stmt.expr); break; - case NODE_BLOCK_STMT: { - Scope *inner = scope_create(tc->current_scope); - Scope *outer = tc->current_scope; - tc->current_scope = inner; + case NODE_BLOCK_STMT: + /* Inline blocks (from multi-var expansion) share parent scope. + * Only control flow blocks (if, for, etc.) create new scopes, + * and those are handled by their own cases. */ check_block(tc, node); - tc->current_scope = outer; break; - } case NODE_IF_STMT: resolve_expr(tc, node->data.if_stmt.condition); @@ -428,6 +426,16 @@ static void check_statement(TypeChecker *tc, AstNode *node) { scope_define(func_scope, p->name, ptype, p->mutable); } + /* Define named return variables in function scope */ + if (node->data.func_decl.return_names) { + for (int i = 0; i < node->data.func_decl.return_type_count; i++) { + if (node->data.func_decl.return_names[i]) { + EzType *rt = type_from_name(node->data.func_decl.return_types[i]); + scope_define(func_scope, node->data.func_decl.return_names[i], rt, true); + } + } + } + check_block(tc, node->data.func_decl.body); tc->current_scope = outer; break;