diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-20 10:49:40 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-20 10:49:40 +0000 |
| commit | f027a812707d68ca0690b7544175b9f302dd57ad (patch) | |
| tree | 8ca82006fa3213215ff7b70bec5e49e8421413ff | |
| parent | 856198b2ea473a4fd2cd020e58db821265b21ca5 (diff) | |
Copy trait...
| -rw-r--r-- | README.md | 36 | ||||
| -rw-r--r-- | src/parser/parser.h | 2 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 18 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 2 | ||||
| -rw-r--r-- | std/mem.zc | 6 | ||||
| -rw-r--r-- | tests/features/test_copy_trait.zc | 41 |
6 files changed, 96 insertions, 9 deletions
@@ -189,6 +189,8 @@ struct Flags { } ``` +> **Note**: Structs use [Move Semantics](#move-semantics--copy-safety) by default. Fields can be accessed via `.` even on pointers (Auto-Dereference). + #### Enums Tagged unions (Sum types) capable of holding data. ```zc @@ -347,6 +349,9 @@ These operators are built-in language features and cannot be overloaded directly | `?.` | Safe Navigation | `ptr?.field` accesses field only if `ptr` is not NULL | | `?` | Try Operator | `res?` returns error if present (Result/Option types) | +**Auto-Dereference**: +Pointer field access (`ptr.field`) and method calls (`ptr.method()`) automatically dereference the pointer, equivalent to `(*ptr).field`. + ### 7. Printing and String Interpolation Zen C provides versatile options for printing to the console, including keywords and concise shorthands. @@ -409,6 +414,35 @@ Automatically free the variable when scope exits. autofree var types = malloc(1024); ``` +#### Move Semantics & Copy Safety +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`). + +```zc +struct Mover { val: int; } + +fn main() { + var a = Mover { val: 1 }; + var b = a; // 'a' moved to 'b' + // print(a.val); // Error: Use of moved value 'a' +} +``` + +**Opt-in Copy**: + +```zc +struct Point { x: int; y: int; } +impl Copy for Point {} + +fn main() { + var p1 = Point { x: 1, y: 2 }; + var p2 = p1; // p1 copied to p2 + // p1 is still valid +} +``` + #### RAII / Drop Trait Implement `Drop` to run cleanup logic automatically. ```zc @@ -513,6 +547,8 @@ impl Drop for Resource { } ``` +> **Note:** If a variable is moved, `drop` is NOT called on the original variable. It adheres to [Move Semantics](#move-semantics--copy-safety). + #### Composition Use `use` to embed other structs. You can either mix them in (flatten fields) or name them (nest fields). diff --git a/src/parser/parser.h b/src/parser/parser.h index f82edf4..5757fbc 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -430,7 +430,7 @@ ASTNode *parse_impl_trait(ParserContext *ctx, Lexer *l); ASTNode *parse_test(ParserContext *ctx, Lexer *l); // Move semantics helpers -int is_type_copy(Type *t); +int is_type_copy(ParserContext *ctx, Type *t); void check_move_usage(ParserContext *ctx, ASTNode *node, Token t); ASTNode *parse_include(ParserContext *ctx, Lexer *l); ASTNode *parse_import(ParserContext *ctx, Lexer *l); diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index cc647c7..97d6218 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -9,7 +9,7 @@ Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name); -int is_type_copy(Type *t) +int is_type_copy(ParserContext *ctx, Type *t) { if (!t) { @@ -38,7 +38,11 @@ int is_type_copy(Type *t) return 1; case TYPE_STRUCT: - // Structs are MOVE by default + // Structs are MOVE by default unless they implement Copy + if (check_impl(ctx, "Copy", t->name)) + { + return 1; + } return 0; case TYPE_ARRAY: @@ -1829,7 +1833,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) } } - if (!is_type_copy(t)) + if (!is_type_copy(ctx, t)) { Symbol *s = find_symbol_entry(ctx, arg->var_ref.name); if (s) @@ -1960,7 +1964,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) } } - if (!is_type_copy(t)) + if (!is_type_copy(ctx, t)) { Symbol *s = find_symbol_entry(ctx, arg->var_ref.name); if (s) @@ -2246,7 +2250,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) } } - if (!is_type_copy(t)) + if (!is_type_copy(ctx, t)) { Symbol *s = find_symbol_entry(ctx, arg->var_ref.name); if (s) @@ -3347,7 +3351,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) } } - if (!is_type_copy(t)) + if (!is_type_copy(ctx, t)) { Symbol *s = find_symbol_entry(ctx, arg->var_ref.name); if (s) @@ -3753,7 +3757,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) } } - if (!is_type_copy(t)) + if (!is_type_copy(ctx, t)) { Symbol *s = find_symbol_entry(ctx, rhs->var_ref.name); if (s) diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 8fa0a1e..fc34a21 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1248,7 +1248,7 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) t = s->type_info; } } - if (!is_type_copy(t)) + if (!is_type_copy(ctx, t)) { Symbol *s = find_symbol_entry(ctx, init->var_ref.name); if (s) @@ -15,6 +15,12 @@ trait Drop { fn drop(self); } +trait Copy {} + +trait Clone { + // fn clone(self) -> Self; +} + struct Box<T> { ptr: T*; } diff --git a/tests/features/test_copy_trait.zc b/tests/features/test_copy_trait.zc new file mode 100644 index 0000000..120dc5d --- /dev/null +++ b/tests/features/test_copy_trait.zc @@ -0,0 +1,41 @@ + +trait Copy {} + +struct Point { + x: int; + y: int; +} + +impl Copy for Point {} + +struct Mover { + val: int; +} + +fn test_copy_trait() { + var p1 = Point { x: 10, y: 20 }; + var p2 = p1; // Copy, not move + + // Both should be valid + assert(p1.x == 10, "p1 invalid after copy"); + assert(p2.x == 10, "p2 invalid after copy"); + + // Modify p2, p1 should be unchanged + p2.x = 30; + assert(p1.x == 10, "p1 changed after p2 modification"); + assert(p2.x == 30, "p2 modification failed"); +} + +fn test_move_default() { + var m1 = Mover { val: 1 }; + var m2 = m1; // Moved + + // Uncommenting this should cause compile error + // var m3 = m1; +} + +fn main() { + test_copy_trait(); + test_move_default(); + "Copy Trait Works!"; +} |
