Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
8225d2a
WIP: calm-hawk-173
briansrls Apr 29, 2026
b78a4ef
WIP: calm-hawk-173
briansrls Apr 29, 2026
86d64d2
docs(r2): record deferral-predicate audit debt; cite #1179 ratificati…
briansrls Apr 29, 2026
1d82d8f
WIP: calm-hawk-173
briansrls Apr 29, 2026
c1349d0
WIP: calm-hawk-173
briansrls Apr 29, 2026
4563572
WIP: calm-hawk-173
briansrls Apr 29, 2026
443c727
WIP: calm-hawk-173
briansrls Apr 29, 2026
ba7863d
docs(dsl): fix ci_runner note after extdeps method-template removal (…
briansrls Apr 29, 2026
0d7c5c2
chore(v3): SG-0 receipt for Director cycle-5 rebase pass (#1142 c4341…
briansrls Apr 29, 2026
a717b9a
chore: refresh PR head for merge-state reconciliation (no file changes)
briansrls Apr 29, 2026
ccbc721
fix(dsl): single v2 facade for v3.std.emit_model (P2 duplicate author…
briansrls Apr 29, 2026
ec5c279
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
b14134d
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
913dd1e
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
9976a2b
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
735e059
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
a654099
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
b400567
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
dbd1e04
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
201350b
WIP: calm-hawk-173
briansrls Apr 29, 2026
20ddb29
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
1ffcad4
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
f67c514
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
d975431
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
d79d583
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
321d612
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
13d14f5
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
f745a61
WIP: calm-hawk-173
briansrls Apr 29, 2026
7669c5e
WIP: calm-hawk-173
briansrls Apr 29, 2026
79eef96
WIP: calm-hawk-173
briansrls Apr 29, 2026
066ff70
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
5e3a8b7
WIP: calm-hawk-173
briansrls Apr 29, 2026
db47b8a
WIP: calm-hawk-173
briansrls Apr 29, 2026
fad0685
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
45c19f2
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
345ae6c
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
749867b
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
e713618
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
6e18678
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
357696b
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
199c846
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 29, 2026
6e256ce
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
4a3be51
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
1800f87
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
b2de66a
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
bbfedbc
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
bf715f7
WIP: calm-hawk-173
briansrls Apr 30, 2026
bda1e2f
WIP: calm-hawk-173
briansrls Apr 30, 2026
3728a44
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
152b448
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
0851cb7
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
f0496dc
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
96a7080
WIP: calm-hawk-173
briansrls Apr 30, 2026
f64b5af
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
e3376b9
WIP: calm-hawk-173
briansrls Apr 30, 2026
cd8a6f7
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
7882e9a
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
373145f
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
a1f4dde
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
c6a7259
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
aa1df96
WIP: calm-hawk-173
briansrls Apr 30, 2026
98b1f74
WIP: calm-hawk-173
briansrls Apr 30, 2026
d4b7522
WIP: calm-hawk-173
briansrls Apr 30, 2026
2540d0d
chore: apply cargo fmt
briansrls Apr 30, 2026
029aa8f
WIP: calm-hawk-173
briansrls Apr 30, 2026
6ae0916
chore: apply cargo fmt
briansrls Apr 30, 2026
3a77b24
WIP: calm-hawk-173
briansrls Apr 30, 2026
4867e4c
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
8411d92
chore: refresh parse corpus manifest for .v3-only brace-fn parse gate
briansrls Apr 30, 2026
9ada495
test: align prereq-2 brace match fixture with sum payload (Cell { n: …
briansrls Apr 30, 2026
0019a4d
WIP: calm-hawk-173
briansrls Apr 30, 2026
7ff35f9
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
80333df
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
bd4750c
test(v3): pin malformed brace-fn probe falls back to FnExternalBody
briansrls Apr 30, 2026
14a228a
test(v3): pin R14 on multi-stmt and malformed brace-fn lowers
briansrls Apr 30, 2026
8f5e398
test(v3): retarget R14 fn gate on multi-stmt FnExternalBody fixture
briansrls Apr 30, 2026
30bbbec
WIP: calm-hawk-173
briansrls Apr 30, 2026
886a137
Merge remote-tracking branch 'origin/main' into session/calm-hawk-173
briansrls Apr 30, 2026
1486d18
fix(v3): propagate brace-fn parse_expr errors except let-first bodies
briansrls Apr 30, 2026
4a9ea9b
docs(parse): clarify brace-fn parse_err vs partial-expr fallback
briansrls Apr 30, 2026
687ce43
fix(v3): align brace-fn literal lookahead with data + map bodies
briansrls Apr 30, 2026
9b7143c
test(v3): pin empty {} brace-fn body parses as Record literal
briansrls Apr 30, 2026
e0fdfe0
docs(parse): correct looks_like_record_literal lookahead description
briansrls Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 150 additions & 28 deletions src/v3/compiler/parse_parser_body.txt
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,16 @@ impl<'a> Parser<'a> {
})
}

/// 3-token lookahead for the record-literal disambiguation in
/// `parse_data_item`. Returns `true` when the next three tokens
/// are `{`, then `Ident`, then `:` — the unambiguous start of a
/// record literal field. Empty `{}` (LBrace immediately followed
/// by RBrace) also classifies as a record literal so zero-field
/// carriers like `data x: ProgramInput = {}` lower to
/// `ValueBody::Structural { fields: [] }` rather than falling
/// into the brace-skip / `Unparsed` path. `{` followed by a
/// non-identifier or `{ ident` without a colon still returns
/// false and the caller falls back to the brace-skip path.
/// 3-token lookahead for record-literal disambiguation (`parse_data_item`,
/// brace-bodied `fn` on `.v3`). Returns `true` when:
///
/// - **`{}`**: `LBrace` immediately followed by `RBrace` (empty record).
/// - **`{ label: expr`**: `LBrace`, then a field label token (`Ident(_)` or any
/// token accepted via `soft_keyword_ident_spelling`, e.g. `KwType` for
/// `type:`), then `:` — same label set as `parse_field_label`.
///
/// Otherwise returns `false` so callers can try map-literal lookahead or
/// other paths.
fn looks_like_record_literal(&self) -> bool {
let t0 = self.tokens.get(self.pos);
let t1 = self.tokens.get(self.pos + 1);
Expand Down Expand Up @@ -528,6 +528,38 @@ impl<'a> Parser<'a> {
})
}

/// User `.v3` modules parse brace-bodied `fn` bodies as real
/// `SurfaceExpr` trees (single expression, record literal, or
/// `parse_expr` + `FnExternalBody` fallback for multi-statement
/// blocks). Authority `.dag` sources (`dsl/std`, `src/v3/std`,
/// compiler/spec fixtures, etc.) keep the legacy brace-skip →
/// `FnExternalBody` path so bootstrap snapshots and staged std
/// `ArrowBody::Unparsed` contracts stay byte-stable until a
/// dedicated bootstrap regen lane opts the corpus in.
fn fn_brace_body_parse_as_expression(&self) -> bool {
self.file.ends_with(".v3")
}

/// After `{`, `parse_expr` fails immediately on `KwLet` (statements are not
/// expressions). Only that shape uses `FnExternalBody` + brace-skip on
/// `parse_expr` `Err` — other failures are malformed single-expression probes
/// and must surface the parser diagnostic (fail-closed at parse time).
fn fn_brace_body_expr_err_falls_back_to_external(&self, first_inner_token_pos: usize) -> bool {
matches!(
self.tokens
.get(first_inner_token_pos)
.map(|t| &t.kind),
Some(TokenKind::KwLet)
)
}

/// Brace-bodied `fn` **record** lookahead: must stay aligned with
/// `parse_field_label` / `parse_record_literal` (same entry shapes as
/// `parse_data_item`: `{}`, `{ ident: expr`, `{ type: expr`, …).
fn fn_brace_body_looks_like_record_literal(&self) -> bool {
self.looks_like_record_literal()
}

fn parse_fn_item(&mut self) -> Result<SurfaceItem, Diagnostic> {
let fn_kw = self.expect_kind(TokenKind::KwFn)?;
let name = self.parse_ident()?;
Expand All @@ -553,24 +585,114 @@ impl<'a> Parser<'a> {
})
}
TokenKind::LBrace => {
// Block-body scaffold form: `fn f(x) -> T { body }`.
// The body is brace-skipped and preserved as a span;
// the declaration it lowers to carries
// `ArrowBody::Unparsed(body_span)` so its signature
// flows forward and callers can type-check against
// it, but the body stays scaffolded until the M2+
// surface grammar covers match/pipe/lambda/etc.
let open = self.peek().span.clone();
let end = self.skip_brace_balanced()?;
let body_span = SourceSpan::new(self.file, open.byte_start, end);
Ok(SurfaceItem::FnExternalBody {
name,
type_params,
params,
return_type,
body_span,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
})
// Brace-bodied `fn f(x) -> T { body }`.
//
// Non-`.v3` sources (staged `.dag` corpus): preserve the
// legacy `FnExternalBody` + brace-skip path — see
// `fn_brace_body_parse_as_expression`.
//
// `.v3` user sources: when `{` begins a record literal (`{}` or
// `{ label: expr, ... }`, including `{ type: ... }` via
// `parse_field_label` soft keywords) or a string-key **map**
// literal (`{ "key": expr, ... }` — same disambiguation order as
// `parse_data_item`), parse that surface form. Otherwise probe a
// single `SurfaceExpr` through the closing `}`.
//
// Otherwise try a single complete `SurfaceExpr` through the
// closing `}`. If the expression parses but does not exhaust
// the brace (e.g. multi-`let` sequences), rewind and fall back to
// `FnExternalBody` + brace-skip. If `parse_expr` errors and the
// first inner token is `let`, use the same fallback (statement
// scaffold); otherwise propagate the parse diagnostic.
if !self.fn_brace_body_parse_as_expression() {
let open = self.peek().span.clone();
let end = self.skip_brace_balanced()?;
let body_span = SourceSpan::new(self.file, open.byte_start, end);
return Ok(SurfaceItem::FnExternalBody {
name,
type_params,
params,
return_type,
body_span,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
});
}
if self.fn_brace_body_looks_like_record_literal() {
let body_expr = self.parse_record_literal()?;
let end = expr_span(&body_expr).byte_end;
Ok(SurfaceItem::Fn {
name,
type_params,
params,
return_type,
body: body_expr,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
})
} else if self.looks_like_map_literal() {
let body_expr = self.parse_map_literal()?;
let end = expr_span(&body_expr).byte_end;
Ok(SurfaceItem::Fn {
name,
type_params,
params,
return_type,
body: body_expr,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
})
} else {
let checkpoint = self.pos;
self.expect_kind(TokenKind::LBrace)?;
let first_inner_token_pos = self.pos;
match self.parse_expr() {
Ok(body_expr) => {
if matches!(self.peek().kind, TokenKind::RBrace) {
let close = self.expect_kind(TokenKind::RBrace)?;
let end = close.span.byte_end;
Ok(SurfaceItem::Fn {
name,
type_params,
params,
return_type,
body: body_expr,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
})
} else {
self.pos = checkpoint;
let open = self.peek().span.clone();
let end = self.skip_brace_balanced()?;
let body_span = SourceSpan::new(self.file, open.byte_start, end);
Ok(SurfaceItem::FnExternalBody {
name,
type_params,
params,
return_type,
body_span,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
})
}
}
Err(diag) => {
if self.fn_brace_body_expr_err_falls_back_to_external(first_inner_token_pos)
{
self.pos = checkpoint;
let open = self.peek().span.clone();
let end = self.skip_brace_balanced()?;
let body_span = SourceSpan::new(self.file, open.byte_start, end);
Ok(SurfaceItem::FnExternalBody {
name,
type_params,
params,
return_type,
body_span,
span: SourceSpan::new(self.file, fn_kw.span.byte_start, end),
})
} else {
self.pos = checkpoint;
Err(diag)
}
}
}
}
}
other => Err(Diagnostic::ParseError {
message: format!("expected `=` or `{{` after fn return type, got {other:?}"),
Expand Down
6 changes: 5 additions & 1 deletion src/v3/compiler/src/dag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,11 @@ impl AtomPayload {
/// - **`Unparsed`** — surface-grammar lag (**case 1**) and **case 2c** the
/// `pipeline.dag` **`compile`** orchestrator (ordering authority). Used at
/// M1(2.7) for block-bodied `fn foo(x) -> T { body }` declarations in std/
/// files where the body contains match/pipe/lambda/ etc. **`pipeline.dag`
/// files where the body contains match/pipe/lambda/ etc. **Parser note
/// (Prereq-2 / #1248):** authority `.dag` sources still surface these as
/// `SurfaceItem::FnExternalBody` (brace-skip) so bootstrap snapshots stay
/// stable; user `.v3` modules may surface `SurfaceItem::Fn` for single-
/// expression brace bodies instead. **`pipeline.dag`
/// per-stage fns (case 2a)** parse as `FnExternalBody` → `Unparsed`, then
/// bootstrap rewrites those Arrow bodies to `ExternalRealization` before
/// inference — so `Unparsed` does not persist for those stages in a
Expand Down
4 changes: 3 additions & 1 deletion src/v3/compiler/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ pub(crate) fn finalize_strict_user_lower_range(dag: &mut Dag, strict_from: usize
// `ArrowBody::Unparsed` and `Data` / `ValueBody::Unparsed`
// are load-bearing scaffolds for the std/bootstrap files
// whose bodies the M1(2.8) parser cannot yet lower
// (match / record literals / lambdas / etc.).
// (match / record literals / lambdas / etc.), and for staged `.dag`
// brace `fn` bodies that intentionally stay on the `FnExternalBody`
// parse path (see `Parser::fn_brace_body_parse_as_expression`).
// User-range declarations that rely on the scaffold are
// fail-closed: ordinary user code has no business shipping
// an opaque body the compiler cannot validate. Without this
Expand Down
Loading
Loading