feat: label with fallthrough default value (#6163)#6174
Draft
ggreif wants to merge 3 commits into
Draft
Conversation
Desugar `label x [: T] = <default> <body>` to
`label x [: T] do { <body>; <default> }`. The default is the value when no
`break x` fires; being the block's tail it is evaluated only on demand, like
the `else` branch of `if`. Replaces the mechanical `do { <body>; <default> }`
boilerplate.
The default operand is parsed at `exp_nullary` (literals, identifiers,
parenthesised expressions) — a higher level juxtaposed with the body is
LR-ambiguous (unary/binary operator overlap). Complex defaults such as
`#err "x"` or `?x` are written parenthesised.
The label's type still comes from the annotation T (the body, hence the
default and any `break` value, is checked against it). So:
- `label x : T = <default> ...` works for any default (`None <: T`);
- `label x = <default> ...` (compact, no annotation) gives the label
type (), so it typechecks only when the default fits () — i.e. a bottom
default like `return`/trap, not a non-bottom one.
Tests:
- test/run/label-default.mo: annotated x {non-bottom, bottom} and
compact x bottom, plus a check that the default is evaluated lazily.
- test/fail/label-default-compact.mo: compact + non-bottom default is a
type error (M0050/M0096).
printers.ml: handle the menhir-generated optional-default nonterminal in
syntax-error messages.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Cursor AI review👍 APPROVE — looks safe to merge
VerdictDecision: APPROVE Generated for commit 3d2a9c9 |
The `label` production gained an optional `('=' <exp_nullary>)?` default; update
the committed grammar.txt so the check-grammar CI step matches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #6163 — a labeled early-exit with a fallthrough default, so
replaces the mechanical
Desugaring (parser only)
label x [: T] = <default> <body>⟿label x [: T] do { <body>; <default> }The default is the value when no
break xfires. Being the block's tail it is evaluated only on demand — skipped once abreakexits the label, exactly like theelsebranch ofif.Default operand level
Parsed at
exp_nullary(literals, identifiers, parenthesised expressions). A higher level juxtaposed with the body is LR-ambiguous (unary/binary operator overlap —exp_bin/exp_unboth reintroduce menhir conflicts). Complex defaults are written parenthesised:(#err "not found"),(?x).Typing: which forms work
The label's type still comes from the annotation
T(the body — hence the default and anybreakvalue — is checked against it). There is no inference of the label type from the body, so:false)return …/trap,None)label x : T = D …None <: T)label x = D …Boolvs())None <: ())Without an annotation the label type is
(), so the compact form only typechecks when the default fits()(a bottom default). The compact-with-non-bottom form (type inferred from the default, as floated in the issue) would need a typechecker change and is not part of this parser-only PR.Tests
test/run/label-default.mo— annotated × {non-bottom, bottom} and compact × bottom, plus a check that the default is evaluated lazily. Passes[tc] [run] [run-ir] [run-low] [comp] [valid] [wasm-run].test/fail/label-default-compact.mo— pins compact + non-bottom as a type error (M0050/M0096).printers.mlhandles the menhir-generated optional-default nonterminal in syntax-error messages.🤖 Generated with Claude Code