Skip to content

Variants with some @unboxed constructors skip the untagged variant checks #9020

Closed

Description

@cknitt

A variant whose type is not @unboxed, but with @unboxed on some constructors (a "mixed" variant), skips the checks that an untagged variant gets. Several such declarations compile, but their matches can't tell the cases apart:

// Two number cases
type t1 = | @unboxed A(int) | @unboxed B(int) | C
// f(B(5)) returns "a"

// A payload of unknown kind beside another payload
type t2 = | @unboxed A(list<int>) | B({x: int}) | C
// f(A(list{1})) returns "b"

// An untagged object beside a tagged block, which is an object too
type t4 = | @unboxed A({x: int}) | B({y: int}) | C
// f(A({x: 1})) returns "b" at run time

with f = x => switch x { | A(_) => "a" | B(_) => "b" | C => "c" }. For t1 the generated check is typeof x === "number" ? "a" : "b".

There is also a false positive. An untagged case's @as doesn't change its representation, yet it is compared as a literal with the tags of tagged cases:

type t3 = | @unboxed @as("x") A(int) | @as("x") B({y: int})
// Error: This untagged variant definition is invalid: Duplicate literal x.

Observed on master (d53b8ae). Cause: Ast_untagged_variants.check_invariant runs the payload-kind checks only when the type itself is @unboxed (is_untagged_def). Otherwise it compares every block's tag as a literal, including those of untagged cases.

Expected:

  • the kind checks apply whenever a variant has an untagged case;
  • tagged blocks count as one object case;
  • only tagged cases' tags are compared for duplicates.

t1, t2 and t4 would then be errors, and t3 would compile.

Found by a review of the untagged variant work (#8950).

Activity

  1. added theissue type on Oct 24, 2026
  2. added a commit that references this issue on Oct 24, 2026
  3. added a commit that references this issue on Oct 25, 2026
  4. added a commit that references this issue on Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions