diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-20 12:06:28 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-20 12:06:28 +0000 |
| commit | e5d8c4219cfe5629a3ce4dbff01406a1817a788f (patch) | |
| tree | c600b4f6713877b748dbf677a405dc1fbafae8df | |
| parent | db690b368f7e05b242f2e775f620f35ab0df5bc3 (diff) | |
Reference binding...
| -rw-r--r-- | README.md | 20 | ||||
| -rw-r--r-- | src/ast/ast.h | 1 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 84 | ||||
| -rw-r--r-- | src/parser/parser_core.c | 4 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 20 | ||||
| -rw-r--r-- | tests/features/test_match_ref.zc | 17 |
6 files changed, 128 insertions, 18 deletions
@@ -273,6 +273,21 @@ match shape { Rect(w, h) => print(f"Area: {w*h}"), Point => print("Point") } + +#### Reference Binding +To inspect a value without taking ownership (moving it), use the `ref` keyword in the pattern. This is essential for types that implement Move Semantics (e.g. `Option`, `Result`, non-Copy structs). + +```zc +var opt = Some(NonCopyVal{...}); +match opt { + Some(ref x) => { + // 'x' is a pointer to the value inside 'opt' + // 'opt' is NOT moved/consumed here + print(x.field); + }, + None => {} +} +``` ``` #### Loops @@ -418,7 +433,10 @@ autofree var types = malloc(1024); Zen C prevents double-free errors by enforcing **Move Semantics** for non-trivial types (structs) by default. - **Move by Default**: Assigning a struct variable transfers ownership. The original variable becomes invalid. -- **Copy Trait**: Implement the `Copy` trait to opt-out of move semantics for simple types (e.g. `Point`). +- **Management Strategies**: + - **`Copy` Trait**: Opt-out of move semantics for simple types. + - **`ref` Binding**: Use `match val { Some(ref x) => ... }` to inspect without moving. + - **Smart Derives**: `@derive(Eq)` uses references for comparison to avoid moves. ```zc struct Mover { val: int; } diff --git a/src/ast/ast.h b/src/ast/ast.h index 2233b09..508a247 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -303,6 +303,7 @@ struct ASTNode char *pattern; char *binding_name; int is_destructuring; + int is_ref; // New: Supports 'ref' binding (Some(ref x)) ASTNode *guard; ASTNode *body; int is_default; diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index b25c79c..32fbdcf 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -246,38 +246,89 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { if (strstr(g_config.cc, "tcc")) { - fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, - c->match_case.binding_name, id); + if (c->match_case.is_ref) + { + fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id, + c->match_case.binding_name, id); + } + else + { + fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, + c->match_case.binding_name, id); + } } else { - fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id); + if (c->match_case.is_ref) + { + fprintf(out, "ZC_AUTO %s = &_m_%d.val; ", c->match_case.binding_name, id); + } + else + { + fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id); + } } } - if (is_result) + else if (is_result) // FIX: Changed 'if' to 'else if' to match original logic structure + // if needed, but original code had implicit fallthrough checks? No, + // checks match pattern. { if (strcmp(c->match_case.pattern, "Ok") == 0) { if (strstr(g_config.cc, "tcc")) { - fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, - c->match_case.binding_name, id); + if (c->match_case.is_ref) + { + fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id, + c->match_case.binding_name, id); + } + else + { + fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, + c->match_case.binding_name, id); + } } else { - fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id); + if (c->match_case.is_ref) + { + fprintf(out, "ZC_AUTO %s = &_m_%d.val; ", c->match_case.binding_name, + id); + } + else + { + fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, + id); + } } } else { if (strstr(g_config.cc, "tcc")) { - fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id, - c->match_case.binding_name, id); + if (c->match_case.is_ref) + { + fprintf(out, "__typeof__(&_m_%d.err) %s = &_m_%d.err; ", id, + c->match_case.binding_name, id); + } + else + { + fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id, + c->match_case.binding_name, id); + } } else { - fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name, id); + if (c->match_case.is_ref) + { + fprintf(out, "ZC_AUTO %s = &_m_%d.err; ", c->match_case.binding_name, + id); + } + else + { + fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name, + id); + } } } } @@ -292,7 +343,18 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, { f = c->match_case.pattern; } - fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f); + // Generic struct destructuring (e.g. MyStruct_Variant) + // Assuming data union or accessible field. + // Original: _m_%d.data.%s + if (c->match_case.is_ref) + { + fprintf(out, "ZC_AUTO %s = &_m_%d.data.%s; ", c->match_case.binding_name, id, + f); + } + else + { + fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f); + } } } diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c index acab268..3c2805c 100644 --- a/src/parser/parser_core.c +++ b/src/parser/parser_core.c @@ -591,8 +591,8 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char * else if (fdef && fdef->type == NODE_STRUCT) { // Struct field: use _eq function, pass addresses - // self.field is L-value, other.field is L-value (auto-deref from pointer) - // We need addresses of them: &self.field, &other.field + // self.field is L-value, other.field is L-value (auto-deref from + // pointer) We need addresses of them: &self.field, &other.field sprintf(cmp, "%s__eq(&self.%s, &other.%s)", ft, fn, fn); } else diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index fc34a21..7433525 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -217,11 +217,11 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l) break; } - // --- 1. Parse Patterns (with OR and range support) --- + // Parse Patterns (with OR and range support) // Patterns can be: // - Single value: 1 - // - OR patterns: 1 || 2 or 1 or 2 - // - Range patterns: 1..5 or 1..=5 + // - OR patterns: 1 || 2 or 1 or 2 or 1, 2 + // - Range patterns: 1..5 or 1..=5 or 1..<5 char patterns_buf[1024]; patterns_buf[0] = 0; int pattern_count = 0; @@ -292,11 +292,22 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l) char *binding = NULL; int is_destructure = 0; - // --- 2. Handle Destructuring: Ok(v) --- + int is_ref = 0; + + // Handle Destructuring: Ok(v) // (Only allowed if we matched a single pattern, e.g. "Result::Ok(val)") if (!is_default && pattern_count == 1 && lexer_peek(l).type == TOK_LPAREN) { lexer_next(l); // eat ( + + // Check for 'ref' keyword + if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 3 && + strncmp(lexer_peek(l).start, "ref", 3) == 0) + { + lexer_next(l); // eat 'ref' + is_ref = 1; + } + Token b = lexer_next(l); if (b.type != TOK_IDENT) { @@ -348,6 +359,7 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l) c->match_case.pattern = pattern; c->match_case.binding_name = binding; c->match_case.is_destructuring = is_destructure; + c->match_case.is_ref = is_ref; // Store is_ref flag c->match_case.guard = guard; c->match_case.body = body; c->match_case.is_default = is_default; diff --git a/tests/features/test_match_ref.zc b/tests/features/test_match_ref.zc new file mode 100644 index 0000000..9734ffe --- /dev/null +++ b/tests/features/test_match_ref.zc @@ -0,0 +1,17 @@ + +enum MyOption<T> { + Some(T), + None +} + +test "match_ref_int" { + var r = MyOption<int>::Some(42); + match r { + MyOption::Some(ref i) => { + // i is int* + assert(*i == 42, "int ref check failed"); + *i = 100; + }, + MyOption::None => assert(false, "fail") + } +} |
