The coercion turbofish's follow rule #231
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/231-the-coercion-turbofish-s-follow-rule"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The coercion turbofish's follow rule
Along the way:
Touches 19 files:
docs/03-types.md,docs/README.md,docs/examples/bounded_coercion_is_written/Main.ck,docs/examples/bounded_coercion_needs_grouping/Main.ck,docs/examples/bounded_coercion_needs_grouping/expect,docs/examples/bounded_coercion_needs_grouping/project.ckand 13 more.Approving. I would have split
src/rust/crates/ck_parser/src/path.rsdifferently, but not enough to hold this up.@ -140,3 +229,3 @@| SyntaxKind::Or => {}// Anything else — not a generic-argument list: it is a comparison._ => return false,_ => return None,The catch-all arm means a variant added later compiles and lands here without a word. Can the match on
_name every variant so the compiler tells us?, at src/rust/crates/ck_parser/src/path.rs:231.Named them all. A new variant is a compile error in
src/rust/crates/ck_parser/src/path.rsnow, which is where we want to find out.Second read on
src/rust/crates/ck_parser/src/path.rs. Nothing to add.`x::Name<…>` is the §1b coercion surface and the arguments are part of it — "it starts with a letter and names the WHOLE type". Every receiver could carry them except the one people write: on a bare identifier, `a::String<128>` was E0146 / E0048 / E0050 / E0036, because `::Name` is uniform with `Mod::member` and a path segment has nowhere to put type arguments, so the `<` went back to the expression parser as less-than. Bare `String` covered most uses precisely because an alias carries its bound, which is why the gap bit hardest where no alias exists: every `List<T, N>`, every `String<N>` off 64. The cell recording it argued this was an owner call about the grammar and not a mechanical fix, and it was right: the one-line relaxation is measurably wrong. `parse_bare_angle_generic_args` already admits a bare-angle list on a segment — that is how `Add<f32, f32, f32>::add` parses — gated on the matching `>` being followed by `::`. Relaxing that to admit a TERMINAL list eats two(a < b, c > d) // two comparisons in one argument list whose seven tokens are every one of them tokens a type-argument list may contain. That is a miscompile of a comparison, traded for a pair of parentheses. THE RULE, AND WHY IT IS EXACT RATHER THAN CAREFUL A terminal `<…>` on a `::`-reached segment is a coercion target iff the token after the matching `>` cannot BEGIN an expression: `)` `}` `]` `|)` `,` `;` a declaration keyword end-of-file The proof is one sentence and it is not a heuristic with headroom: a real `>` is the binary comparison operator, so it always takes a right operand, so it is always followed by a token that STARTS an expression. The two sets are disjoint by construction — no comparison can be read as a type-argument list, and no coercion standing in one of those positions can be read as a comparison. `(`, `.`, `[` and `{` are deliberately OUT: they continue or open an expression, so a coercion the reader means to call into keeps its grouping. The declaration keywords come from `is_top_level_decl_start` — the same list top-level recovery syncs on, so a new keyword joins the set by existing — with exactly ONE subtraction, which is what makes the disjointness claim literally true instead of nearly true: `trait` is also a VALUE head (`trait.method(…)`, the operator-witness inside an `operator` body), so it terminates only when no `.` follows. Two further narrowings: never the HEAD segment (which puts the program above out of reach of the rule entirely) and never in PATTERN position, where there is no value to coerce. It is DECLARED before it is implemented. `Grammar.ckx` gains `coercion_tail` (`'::' <target: type_atom>`) beside `turbofish`, carrying the follow set and the disjointness argument; the parser reads the rule off that page rather than inventing a second copy of it. NOTHING DOWNSTREAM MOVED, which is the other half of why this shape was chosen. The parser DECLINES the segment and leaves the whole `::` tail for the postfix loop, so `a::String<128>` builds the exact `CoercionExpr` that `(a)::String<128>` already built — `ck_types::value_turbofish::synth_coercion`, untouched. `b.s::T<n>`, `xs[i]::T<n>` and `f()::T<n>` were already reaching that fold and are unchanged. CELLS. `coercion_turbofish_with_type_arguments_needs_parentheses` is fixed and renamed to `..._on_an_identifier` (`runs: 42`); its prose no longer argues a fork, it records how the fork was closed. `list_coercion_turbofish_widens_a_local` drops its parentheses and its "THE PARENTHESES ARE NOT STYLE" paragraph. Three new cells pin the rule from all three sides: `two_comparisons_in_an_argument_list_stay_comparisons` (the counter-example, still `runs: 42`), `coercion_turbofish_ends_an_expression_bodied_fn` (the two follow members that are not punctuation — the next declaration and end-of-file — on one page), and `coercion_turbofish_under_a_method_call_needs_parentheses` (the set's closed OTHER side: `.` is out, and the day that ruling changes this cell says so). CELL_COUNT 454 -> 457, BROKEN_CELL_COUNT 47 -> 46. Two stale claims went with it, both live and both now false: `ck_types`'s `coercion_target_segment` said "the parser cannot carry generic arguments on a path segment", and `rebound_hint`'s doc said the parentheses it prints are mandatory because `x::String<64>` does not parse. It does now. The message keeps the parentheses — `value` stands for whatever the author wrote, and an operator expression or a continued coercion still needs them — but for the true reason. The book follows: ch.3's bounded-coercion section loses its parentheses, `bounded_coercion_is_written` grows the four bare receivers (name, field, index, call) plus the grouped spelling, and `bounded_coercion_needs_grouping` is a new fixture pinning that `.` is outside the follow set. 37 -> 38 expectations. Gates: 4 legs green (1371 unit, 106 corpus apps, 457 cells, 38 book expectations); test-integration ALL GREEN — 251 passed, 1 ignored, and the allow-listed `r3_no_file_exceeds_loc_cap` standing red unchanged. fmt clean, clippy clean. No conformance golden moved: the change adds a spelling and removes none.