summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-21 16:15:55 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-21 16:15:55 +0000
commita1efe2cdde2237083ffff825f5b2dbb7442aa419 (patch)
treee9ab190e007f0be5671207652acfd299c91a11b7
parent8144aef45d5db22ab2895b41448cd76bf01e05cc (diff)
Fix for #77
-rw-r--r--src/parser/parser_stmt.c44
-rw-r--r--tests/features/test_match_composition.zc112
2 files changed, 154 insertions, 2 deletions
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index 7873d51..4577405 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -339,8 +339,48 @@ ASTNode *parse_match(ParserContext *ctx, Lexer *l)
enter_scope(ctx);
if (binding)
{
- // If ref binding, mark as pointer type so auto-deref (->) works
- add_symbol(ctx, binding, is_ref ? "void*" : "unknown", NULL);
+ // Try to infer binding type from enum variant payload
+ char *binding_type = is_ref ? "void*" : "unknown";
+ Type *binding_type_info = NULL;
+
+ // Look up the enum variant to get its payload type
+ EnumVariantReg *vreg = find_enum_variant(ctx, pattern);
+ if (vreg)
+ {
+ // Find the enum definition
+ ASTNode *enum_def = find_struct_def(ctx, vreg->enum_name);
+ if (enum_def && enum_def->type == NODE_ENUM)
+ {
+ // Find the specific variant
+ ASTNode *v = enum_def->enm.variants;
+ while (v)
+ {
+ // Match by variant name (pattern suffix after last _)
+ char *v_full =
+ xmalloc(strlen(vreg->enum_name) + strlen(v->variant.name) + 2);
+ sprintf(v_full, "%s_%s", vreg->enum_name, v->variant.name);
+ if (strcmp(v_full, pattern) == 0 && v->variant.payload)
+ {
+ // Found the variant, extract payload type
+ binding_type_info = v->variant.payload;
+ binding_type = type_to_string(v->variant.payload);
+ if (is_ref)
+ {
+ // For ref bindings, make it a pointer to the payload type
+ char *ptr_type = xmalloc(strlen(binding_type) + 2);
+ sprintf(ptr_type, "%s*", binding_type);
+ binding_type = ptr_type;
+ }
+ free(v_full);
+ break;
+ }
+ free(v_full);
+ v = v->next;
+ }
+ }
+ }
+
+ add_symbol(ctx, binding, binding_type, binding_type_info);
}
ASTNode *body;
diff --git a/tests/features/test_match_composition.zc b/tests/features/test_match_composition.zc
new file mode 100644
index 0000000..f25b90f
--- /dev/null
+++ b/tests/features/test_match_composition.zc
@@ -0,0 +1,112 @@
+
+struct Inner {
+ value: int;
+}
+
+// Named composition (struct has nested field)
+struct NamedWrapper {
+ use inner: Inner;
+}
+
+// Mixin composition (fields are flattened)
+struct MixinWrapper {
+ use Inner;
+}
+
+enum NamedResult {
+ Ok(NamedWrapper),
+ Err(int)
+}
+
+enum MixinResult {
+ Ok(MixinWrapper),
+ Err(int)
+}
+
+// Test match binding with named composition - field access
+test "match_binding_named_composition" {
+ var inner = Inner { value: 42 };
+ var wrapper = NamedWrapper { inner: inner };
+ var result = NamedResult::Ok(wrapper);
+
+ match result {
+ NamedResult::Ok(w) => {
+ var val = w.inner.value;
+ assert(val == 42, "Named composition field access failed");
+ },
+ NamedResult::Err(e) => assert(false, "Should not be Err")
+ }
+}
+
+// Test match binding with mixin composition - field access
+test "match_binding_mixin_composition" {
+ var wrapper = MixinWrapper { value: 77 };
+ var result = MixinResult::Ok(wrapper);
+
+ match result {
+ MixinResult::Ok(w) => {
+ var val = w.value;
+ assert(val == 77, "Mixin composition field access failed");
+ },
+ MixinResult::Err(e) => assert(false, "Should not be Err")
+ }
+}
+
+// Test match binding with mixin - f-string interpolation
+test "match_binding_mixin_fstring" {
+ var wrapper = MixinWrapper { value: 88 };
+ var result = MixinResult::Ok(wrapper);
+
+ match result {
+ MixinResult::Ok(w) => {
+ println "{w.value}";
+ assert(w.value == 88, "F-string with mixin failed");
+ },
+ MixinResult::Err(e) => assert(false, "Should not be Err")
+ }
+}
+
+// Test match binding with ref - mixin composition
+test "match_binding_ref_mixin" {
+ var wrapper = MixinWrapper { value: 33 };
+ var result = MixinResult::Ok(wrapper);
+
+ match result {
+ MixinResult::Ok(ref w) => {
+ assert(w.value == 33, "Ref binding with mixin failed");
+ w.value = 44;
+ },
+ MixinResult::Err(e) => assert(false, "Should not be Err")
+ }
+
+ // Verify modification persisted
+ match result {
+ MixinResult::Ok(w) => {
+ assert(w.value == 44, "Ref modification did not persist");
+ },
+ MixinResult::Err(e) => {}
+ }
+}
+
+// Test match binding with ref - named composition
+test "match_binding_ref_named" {
+ var inner = Inner { value: 55 };
+ var wrapper = NamedWrapper { inner: inner };
+ var result = NamedResult::Ok(wrapper);
+
+ match result {
+ NamedResult::Ok(ref w) => {
+ assert(w.inner.value == 55, "Ref binding with named composition failed");
+ w.inner.value = 66;
+ },
+ NamedResult::Err(e) => assert(false, "Should not be Err")
+ }
+
+ // Verify modification persisted
+ match result {
+ NamedResult::Ok(w) => {
+ assert(w.inner.value == 66, "Named composition ref modification did not persist");
+ },
+ NamedResult::Err(e) => {}
+ }
+}