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