summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--src/codegen/codegen.c2
-rw-r--r--src/parser/parser_core.c13
-rw-r--r--src/parser/parser_expr.c29
-rw-r--r--tests/features/test_copy_trait.zc12
-rw-r--r--tests/features/test_move_semantics.zc41
-rw-r--r--tests/features/test_smart_derive.zc26
-rw-r--r--tests/misc/test_edge_cases.zc6
8 files changed, 94 insertions, 45 deletions
diff --git a/README.md b/README.md
index c0275c7..cdba0f3 100644
--- a/README.md
+++ b/README.md
@@ -662,7 +662,15 @@ Decorate functions and structs to modify compiler behavior.
| `@weak` | Fn | Weak symbol linkage. |
| `@section("name")` | Fn | Place code in specific section. |
| `@noreturn` | Fn | Function does not return (e.g. exit). |
-| `@derived(...)` | Struct | Auto-implement traits (e.g. `Debug`). |
+| `@derive(...)` | Struct | Auto-implement traits. Supports `Debug`, `Eq` (Smart Derive), `Copy`, `Clone`. |
+
+### Smart Derives
+
+Zen C provides "Smart Derives" that respect Move Semantics:
+
+- **`@derive(Eq)`**: Generates an equality method that takes arguments by reference (`fn eq(self, other: T*)`).
+ - When comparing two non-Copy structs (`a == b`), the compiler automatically passes `b` by reference (`&b`) to avoid moving it.
+ - Recursive equality checks on fields also prefer pointer access to prevent ownership transfer.
### 14. Inline Assembly
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 01c8204..b25c79c 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -461,7 +461,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
fprintf(out, "%s__eq(&", base);
codegen_expression(ctx, node->binary.left, out);
- fprintf(out, ", ");
+ fprintf(out, ", &");
codegen_expression(ctx, node->binary.right, out);
fprintf(out, ")");
if (strcmp(node->binary.op, "!=") == 0)
diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c
index c3c91fe..acab268 100644
--- a/src/parser/parser_core.c
+++ b/src/parser/parser_core.c
@@ -585,17 +585,19 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char *
ASTNode *fdef = find_struct_def(ctx, ft);
if (fdef && fdef->type == NODE_ENUM)
{
- // Enum field: compare tags
+ // Enum field: compare tags (pointer access via auto-deref)
sprintf(cmp, "self.%s.tag == other.%s.tag", fn, fn);
}
else if (fdef && fdef->type == NODE_STRUCT)
{
- // Struct field: use _eq function
- sprintf(cmp, "%s__eq(&self.%s, other.%s)", ft, fn, fn);
+ // 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
+ sprintf(cmp, "%s__eq(&self.%s, &other.%s)", ft, fn, fn);
}
else
{
- // Primitive or unknown: use ==
+ // Primitive or unknown: use == (auto-deref)
sprintf(cmp, "self.%s == other.%s", fn, fn);
}
strcat(body, cmp);
@@ -610,7 +612,8 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char *
strcat(body, ";");
}
code = xmalloc(4096 + 1024);
- sprintf(code, "impl %s { fn eq(self, other: %s) -> bool { %s } }", name, name, body);
+ // Updated signature: other is a pointer T*
+ sprintf(code, "impl %s { fn eq(self, other: %s*) -> bool { %s } }", name, name, body);
}
else if (0 == strcmp(trait, "Debug"))
{
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index 97d6218..a79bb21 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -4115,9 +4115,34 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
}
+ // Handle RHS (Argument 2) Auto-Ref if needed
+ ASTNode *arg2 = rhs;
+ if (sig->total_args > 1 && sig->arg_types[1] &&
+ sig->arg_types[1]->kind == TYPE_POINTER)
+ {
+ Type *rt = rhs->type_info;
+ int is_rhs_ptr = (rt && rt->kind == TYPE_POINTER);
+ if (!is_rhs_ptr) // Need pointer, have value
+ {
+ int is_rvalue =
+ (rhs->type == NODE_EXPR_CALL || rhs->type == NODE_EXPR_BINARY ||
+ rhs->type == NODE_EXPR_STRUCT_INIT ||
+ rhs->type == NODE_EXPR_CAST || rhs->type == NODE_MATCH);
+
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&");
+ addr->unary.operand = rhs;
+ if (rt)
+ {
+ addr->type_info = type_new_ptr(rt);
+ }
+ arg2 = addr;
+ }
+ }
+
call->call.args = arg1;
- arg1->next = rhs;
- rhs->next = NULL;
+ arg1->next = arg2;
+ arg2->next = NULL;
call->type_info = sig->ret_type;
call->resolved_type = type_to_string(sig->ret_type);
diff --git a/tests/features/test_copy_trait.zc b/tests/features/test_copy_trait.zc
index 120dc5d..994ccee 100644
--- a/tests/features/test_copy_trait.zc
+++ b/tests/features/test_copy_trait.zc
@@ -12,7 +12,7 @@ struct Mover {
val: int;
}
-fn test_copy_trait() {
+test "copy_trait" {
var p1 = Point { x: 10, y: 20 };
var p2 = p1; // Copy, not move
@@ -24,18 +24,14 @@ fn test_copy_trait() {
p2.x = 30;
assert(p1.x == 10, "p1 changed after p2 modification");
assert(p2.x == 30, "p2 modification failed");
+
+ println "Copy Trait Works!";
}
-fn test_move_default() {
+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!";
-}
diff --git a/tests/features/test_move_semantics.zc b/tests/features/test_move_semantics.zc
index 70390c2..bf0d717 100644
--- a/tests/features/test_move_semantics.zc
+++ b/tests/features/test_move_semantics.zc
@@ -7,7 +7,7 @@ struct Mover {
val: int;
}
-fn test_basic_move() {
+test "basic_move" {
var p1 = Mover { val: 10 };
var p2 = p1; // p1 moved to p2
@@ -18,14 +18,14 @@ fn test_basic_move() {
// var p3 = p1;
}
-fn test_primitive_copy() {
+test "primitive_copy" {
var i = 10;
var j = i; // Copy
var k = i; // Copy again - should be valid
assert(k == 10, "Primitive copy failed");
}
-fn test_reassignment() {
+test "reassignment" {
var m1 = Mover { val: 1 };
var m2 = m1; // m1 moved
@@ -34,31 +34,22 @@ fn test_reassignment() {
assert(m3.val == 2, "Resurrection failed");
}
-fn test_func_arg(m: Mover) {
+fn consume(m: Mover) {
assert(m.val == 10, "Func arg failed");
}
-fn main() {
- test_basic_move();
- test_primitive_copy();
- test_reassignment();
-
+test "func_arg" {
var m = Mover { val: 10 };
- test_func_arg(m); // m moved
-
- // ** Negative Tests (Uncomment to verify) **
-
- // 1. Use after move (Assignment)
- // test_basic_move(); // See line 18 inside function
+ consume(m); // m moved
- // 2. Use after move (Call)
- // test_func_arg(m); // Should fail: Use of moved value 'm'
-
- // 3. Use after return
- /*
- fn fail_return(m: Mover) -> Mover {
- var m2 = m;
- return m; // Should fail: Use of moved value 'm'
- }
- */
+ // 2. Use after move (Call - Negative Test)
+ // consume(m); // Should fail: Use of moved value 'm'
+}
+
+/*
+// 3. Use after return (Negative Test)
+fn fail_return(m: Mover) -> Mover {
+ var m2 = m;
+ return m; // Should fail: Use of moved value 'm'
}
+*/
diff --git a/tests/features/test_smart_derive.zc b/tests/features/test_smart_derive.zc
new file mode 100644
index 0000000..705a12f
--- /dev/null
+++ b/tests/features/test_smart_derive.zc
@@ -0,0 +1,26 @@
+
+@derive(Eq)
+struct Container {
+ val: int;
+}
+
+// Ensure derived eq uses pointers by trying to use 'c2' after comparison
+test "eq_moves" {
+ var c1 = Container { val: 10 };
+ var c2 = Container { val: 10 };
+
+ // This should call Container__eq(&c1, &c2)
+ // If it passed by value, c2 would be moved here
+ if c1 == c2 {
+ // c2 must still be valid
+ assert(c2.val == 10, "c2 moved during equality check!");
+ } else {
+ assert(false, "c1 != c2");
+ }
+
+ // Explicitly verify c2 is still valid
+ c2.val = 20;
+ assert(c2.val == 20, "c2 invalid");
+
+ println "Smart Derive Eq Works!";
+}
diff --git a/tests/misc/test_edge_cases.zc b/tests/misc/test_edge_cases.zc
index f1a4cf5..0d50744 100644
--- a/tests/misc/test_edge_cases.zc
+++ b/tests/misc/test_edge_cases.zc
@@ -9,7 +9,7 @@ fn test_empty_struct() {
var e1 = Empty {};
var e2 = Empty {};
- if (!e1.eq(e2)) {
+ if (!e1.eq(&e2)) {
println "FAIL: Empty struct eq failed";
exit(1);
}
@@ -35,12 +35,12 @@ fn test_many_fields() {
var m2 = ManyFields { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8 };
var m3 = ManyFields { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 9 }; // h differs
- if (!m1.eq(m2)) {
+ if (!m1.eq(&m2)) {
println "FAIL: equal structs not detected as equal";
exit(1);
}
- if (m1.eq(m3)) {
+ if (m1.eq(&m3)) {
println "FAIL: different structs detected as equal";
exit(1);
}