Second draft: Sum Type by Struct

Meta jared771 at gmail.com
Wed Sep 9 16:35:19 UTC 2026


On Wednesday, 9 September 2026 at 10:16:04 UTC, Nick Treleaven 
wrote:
> On Saturday, 5 September 2026 at 16:51:20 UTC, Meta wrote:
>> On Saturday, 5 September 2026 at 16:02:08 UTC, Nick Treleaven 
>> wrote:
>>> Not being able to use `return` in a match *expression* is 
>>> understandable. Not having a match *statement* which can use 
>>> `return` seems like a big omission IMO.
>>
>> You can have arms with return, break, continue, throw, etc. if 
>> you allow their type to resolve to noreturn, and they would 
>> refer to the innermost enclosing control flow construct (or 
>> otherwise it's an error - match/switch expressions themselves 
>> cannot break out early).
>
> When `match` is used as a statement it is fine to `break` out 
> of the `match`. Also, supporting statement arm matching is 
> important even when `match` is an expression, otherwise you 
> can't conditionally return in an arm:
>
> ```d
> E3 foo()
> {
>     E4 result = e1.match {
>         (T v) {
>             statements;
>             if (e2)
>                 return e3; // return from `foo`
>             else
>                 break e4; // e4 is the result of this arm
>         }
>         // more cases
>     }
>     statements;
> }
> ```
>
>> It's basically saying "execution will not continue past this 
>> point, so this expression does not need to have a value". This 
>> isn't in Rikki's proposal, but it's perfectly sound from a 
>> type theory perspective. I imagine in DMD it'd require the 
>> creating of a "DivergeExpression" AST node or something.
>
> But would that allow arbitrary statements in a match arm?

No, just return, break, throw, continue, and assert, treated as 
expressions of type noreturn. Break, throw and continue would 
only be valid if the match is nested inside a control flow 
construct that supports those keywords (and likewise return, 
throw and assert for their respective valid contexts).

I.e.

```d
void f()
{
     auto var = expr.match {
         (int n) => break, // error
         (bool b) => return, // okay, returns from f
         (string s) => s.toLowerCase(),
     };
}

And var would be typed as string because the break and return 
arms are of type noreturn, and noreturn <: string.

> Rikki's DIP lowers to a chain of conditional expressions to 
> avoid complicating/rewriting the backend. Presumably that is so 
> a `match` expression works anywhere a normal expression works. 
> But is that overkill? Is it good practice to write a `match` 
> expression as a subexpression? I think usually it is not.

Probably not. I'd have to see specific cases to judge.

> A DIP could just support match expressions for the common 
> statement cases:
>
> ```d
> T v = e.match { ... };
> return e.match { ... };
> ```
> So the AST for those could be `class MatchExpInitializer : 
> ExpInitializer` and `class ReturnMatchStatement : 
> ReturnStatement`. Those could have AST lowerings without 
> touching the backend, yet contain statements in the match arms.

I don't know exactly which contexts ExpInitializer occurs in, 
although I have a pretty good idea. My POC for my own proposal 
(https://github.com/dlang/dmd/pull/23744) introduces a 
SwitchExpression that subclasses Expression.




More information about the dip.development mailing list