Semantic Diff

#9021 Check variants with some @unboxed constructors like untagged ones

1 commit

d53b8ae6743e34997e61bf84f7166092a3d6ec56..pr-9021

ef7c8c1bc · cknitt + Claude · Oct 24, 2026, 12:33 UTC

Check variants with some @unboxed constructors like untagged ones

Why

The checks that keep the cases of an untagged variant apart ran only when the type was @unboxed. A variant with @unboxed on some constructors could have two number cases, an unknown payload beside another, or an untagged object beside a tagged block, and its matches then picked the wrong case (#9020). The @as of an untagged case was meanwhile compared with tags, which it does not set.

The payload kinds are checked as soon as one case is untagged, tagged blocks count as one object case, and only tagged cases' tags are compared for duplicates.

addedDiagnostic two number cases in a mixed variant

type t = | @unboxed A(int) | @unboxed B(int) | C
diagnostic
Before
After
none; `B(5)` matches `A`This untagged variant definition is invalid: At most one case can be a number type (int or float).

addedDiagnostic untagged object beside a tagged block

type t = | @unboxed A({x: int}) | B({y: int}) | C
diagnostic
Before
After
none; `A({x: 1})` matches `B`This untagged variant definition is invalid: At most one case can be an object type.

removedDiagnostic an untagged case's @as

type t = | @unboxed @as("x") A(int) | @as("x") B({y: int}) | C
diagnostic
Before
After
This untagged variant definition is invalid: Duplicate literal x.none

Fixes #9020