Skip to content

C gen: make variants unboxed#278

Merged
osa1 merged 24 commits intomainfrom
variant_types
Feb 5, 2026
Merged

C gen: make variants unboxed#278
osa1 merged 24 commits intomainfrom
variant_types

Conversation

@osa1
Copy link
Member

@osa1 osa1 commented Feb 1, 2026

In prep for value types, this makes variant types unboxed.

For example, [E1, E2, E3] generates this C struct:

typedef struct Variant_E1_E2_E3 {
    uint64_t _tag;
    union {
        E1* _0;
        E2* _1;
        E3* _2;
    } _alt;
} Variant_E1_E2_E3;

This struct is then used directly, instead of as a pointer.

The tag is the tag of the variant's wrapped value. E.g. if the variant value is
E1, the tag is the tag of E1. If E1 is a sum type, then it's the tag of
the constructor.

(Because each constructor (rather than type) has a unique constructor, we can
use the constructor's type as variant tags.)

This means that we always check the tag once when matching a variant value,
regardless of the values in the variant. (sums or products, boxed or unboxed)

Variants are still assigned heap object indices, but those indices are not used
in runtime. They're used in compile time to refer to variant types during
dependency analysis (and probably other places too).

Variant type refinements: we now annotate AST nodes with refined types of
variant binders. The generated code then unpacks the original value and packs
again as the value expected by the binder. Example:

type E1
type E2
type E3

test(x: [E1, E2, E3]) [E2, E3]:
    match x:
        ~E1: panic("")
        other: other

Compiled to: (relevant parts)

else if (({
   _1 = ({
     Variant_E2_E3 _t6;
     if ((_t4)._tag == TAG_E2) {
       _t6._tag = TAG_E2;
       _t6._alt._0 = (_t4)._alt._1;
     } else if ((_t4)._tag == TAG_E3) {
       _t6._tag = TAG_E3;
       _t6._alt._1 = (_t4)._alt._2;
     } else {
       fprintf(stderr, "Invalid variant conversion\n");
       exit(1);
     }
     _t6;
   });
   1;
 })) {
  ...
}

This part of the generated code is not properly indented/formatted yet, example
above formatted manually with clang-format.

We should generate variant conversions outlined, in a separate function.

The "invalid variant conversion" is there to make debugging easier. That branch
won't be taken unless there's a bug in the compiler.

Fixes #279.

@osa1 osa1 changed the title C gen: refactor variants in prep for value types C gen: make variants unboxed Feb 5, 2026
@osa1 osa1 marked this pull request as ready for review February 5, 2026 21:27
@osa1 osa1 merged commit f824cdb into main Feb 5, 2026
26 checks passed
@osa1 osa1 deleted the variant_types branch February 5, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should inferred types of variable patterns be updated after refinement?

1 participant