diff options
| author | SAJJA EASWAR <eshwarsajja20@gmail.com> | 2026-01-24 13:05:12 +0530 |
|---|---|---|
| committer | SAJJA EASWAR <eshwarsajja20@gmail.com> | 2026-01-24 13:05:12 +0530 |
| commit | 863118c95caac0d69a35f6ae4d2e83844734a8a1 (patch) | |
| tree | a7e1b096b1194b6f5bac079979189d6b473a93ff | |
| parent | f8e6dd33e93474024bd3678d5a98477254ab65a2 (diff) | |
Fix regressions: derive(Eq) for pointers and mixed type comparisons
| -rw-r--r-- | src/parser/parser_core.c | 28 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 127 | ||||
| -rw-r--r-- | tests/generics/test_generics_struct.zc | 4 |
3 files changed, 140 insertions, 19 deletions
diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c index 7468bfb..ff5dd1c 100644 --- a/src/parser/parser_core.c +++ b/src/parser/parser_core.c @@ -591,22 +591,34 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char * } char cmp[256]; - ASTNode *fdef = find_struct_def(ctx, ft); - if (fdef && fdef->type == NODE_ENUM) + // Detect pointer using type_info OR string check (fallback) + int is_ptr = 0; + if (f->type_info && f->type_info->kind == TYPE_POINTER) { - // Enum field: compare tags (pointer access via auto-deref) + is_ptr = 1; + } + // Fallback: check if type string ends with '*' + if (!is_ptr && ft && strchr(ft, '*')) + { + is_ptr = 1; + } + + // Only look up struct def for non-pointer types + ASTNode *fdef = is_ptr ? NULL : find_struct_def(ctx, ft); + + if (!is_ptr && fdef && fdef->type == NODE_ENUM) + { + // Enum field: compare tags sprintf(cmp, "self.%s.tag == other.%s.tag", fn, fn); } - else if (fdef && fdef->type == NODE_STRUCT) + else if (!is_ptr && 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 + // Struct field: use __eq function sprintf(cmp, "%s__eq(&self.%s, &other.%s)", ft, fn, fn); } else { - // Primitive or unknown: use == (auto-deref) + // Primitive, POINTER, or unknown: use == sprintf(cmp, "self.%s == other.%s", fn, fn); } strcat(body, cmp); diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 32a054e..98b1c5d 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -5076,6 +5076,32 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) char *struct_name = resolve_struct_name_from_type(ctx, lt, &is_lhs_ptr, &allocated_name); + // If we are comparing pointers with == or !=, do NOT rewrite to .eq() + // We want pointer equality, not value equality (which requires dereferencing) + // But strict check: Only if BOTH are pointers. If one is value, we might need rewrite. + if (is_lhs_ptr && struct_name && + (strcmp(bin->binary.op, "==") == 0 || strcmp(bin->binary.op, "!=") == 0)) + { + int is_rhs_ptr = 0; + char *r_alloc = NULL; + char *r_name = + resolve_struct_name_from_type(ctx, rhs->type_info, &is_rhs_ptr, &r_alloc); + if (r_alloc) + { + free(r_alloc); + } + + if (is_rhs_ptr) + { + // Both are pointers: Skip rewrite to allow pointer comparison + if (allocated_name) + { + free(allocated_name); + } + struct_name = NULL; + } + } + if (struct_name) { char mangled[256]; @@ -5247,8 +5273,15 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) } } + int lhs_is_num = + is_integer_type(lhs->type_info) || lhs->type_info->kind == TYPE_F32 || + lhs->type_info->kind == TYPE_F64 || lhs->type_info->kind == TYPE_FLOAT; + int rhs_is_num = + is_integer_type(rhs->type_info) || rhs->type_info->kind == TYPE_F32 || + rhs->type_info->kind == TYPE_F64 || rhs->type_info->kind == TYPE_FLOAT; + if (!skip_check && !type_eq(lhs->type_info, rhs->type_info) && - !(is_integer_type(lhs->type_info) && is_integer_type(rhs->type_info))) + !(lhs_is_num && rhs_is_num)) { char msg[256]; sprintf(msg, "Type mismatch in comparison: cannot compare '%s' and '%s'", t1, @@ -5333,16 +5366,92 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (!is_ptr_arith && !alias_match) { - char msg[256]; - sprintf(msg, "Type mismatch in binary operation '%s'", bin->binary.op); + // Allow assigning 0 to pointer (NULL) + int is_null_assign = 0; + if (strcmp(bin->binary.op, "=") == 0) + { + int lhs_is_ptr = (lhs->type_info->kind == TYPE_POINTER || + lhs->type_info->kind == TYPE_STRING || + (t1 && strstr(t1, "*") != NULL)); + if (lhs_is_ptr && rhs->type == NODE_EXPR_LITERAL && + rhs->literal.int_val == 0) + { + is_null_assign = 1; + } + } - char suggestion[512]; - sprintf(suggestion, - "Left operand has type '%s', right operand has type '%s'\n = " - "note: Consider casting one operand to match the other", - t1, t2); + if (!is_null_assign) + { + // Check for arithmetic promotion (Int * Float, etc) + int lhs_is_num = is_integer_type(lhs->type_info) || + lhs->type_info->kind == TYPE_F32 || + lhs->type_info->kind == TYPE_F64 || + lhs->type_info->kind == TYPE_FLOAT; + int rhs_is_num = is_integer_type(rhs->type_info) || + rhs->type_info->kind == TYPE_F32 || + rhs->type_info->kind == TYPE_F64 || + rhs->type_info->kind == TYPE_FLOAT; + + int valid_arith = 0; + if (lhs_is_num && rhs_is_num) + { + if (strcmp(bin->binary.op, "+") == 0 || + strcmp(bin->binary.op, "-") == 0 || + strcmp(bin->binary.op, "*") == 0 || + strcmp(bin->binary.op, "/") == 0) + { + valid_arith = 1; + // Result is the float type if one is float + if (lhs->type_info->kind == TYPE_F64 || + rhs->type_info->kind == TYPE_F64) + { + bin->type_info = lhs->type_info->kind == TYPE_F64 + ? lhs->type_info + : rhs->type_info; + } + else if (lhs->type_info->kind == TYPE_F32 || + rhs->type_info->kind == TYPE_F32 || + lhs->type_info->kind == TYPE_FLOAT || + rhs->type_info->kind == TYPE_FLOAT) + { + // Pick the float type. If both float, pick lhs. + if (lhs->type_info->kind == TYPE_F32 || + lhs->type_info->kind == TYPE_FLOAT) + { + bin->type_info = lhs->type_info; + } + else + { + bin->type_info = rhs->type_info; + } + } + else + { + // Both int (but failed equality check previously? - rare + // but possible if diff int types) If diff int types, we + // usually allow it in C (promotion). For now, assume LHS + // dominates or standard promotion. + bin->type_info = lhs->type_info; + } + } + } - zpanic_with_suggestion(op, msg, suggestion); + if (!valid_arith) + { + char msg[256]; + sprintf(msg, "Type mismatch in binary operation '%s'", + bin->binary.op); + + char suggestion[512]; + sprintf( + suggestion, + "Left operand has type '%s', right operand has type '%s'\n = " + "note: Consider casting one operand to match the other", + t1, t2); + + zpanic_with_suggestion(op, msg, suggestion); + } + } } } } diff --git a/tests/generics/test_generics_struct.zc b/tests/generics/test_generics_struct.zc index 929b4ce..58c2cd3 100644 --- a/tests/generics/test_generics_struct.zc +++ b/tests/generics/test_generics_struct.zc @@ -23,7 +23,7 @@ struct Rc<T> { } impl Rc<T> { - fn new(value: T) -> Self { + fn new(_value: T) -> Self { var inner: RcInner<T>* = 0; return Self { inner: inner @@ -32,5 +32,5 @@ impl Rc<T> { } test "test_rc_pointer_instantiation" { - var value = Rc<i32>::new(4); + var _value = Rc<i32>::new(4); } |
