Skip to content

Emitter: refuse an unresolved variant pattern that would render as a bare (catch-all) identifier - #12755

Merged
gunbai-bot[bot] merged 2 commits into
mainfrom
session/fierce-cat-462-pattern-refuse
Sep 30, 2026
Merged

gunbai-bot[bot] merged 2 commits into
mainfrom
session/fierce-cat-462-pattern-refuse

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #12733, as the parent lane asked: turn the emitter's widening fallback into a refusal.

Defect (DESIGN §5, a failure arm that widens). v1.compiler.emit_rust rust_variant_path_opt answered an unresolved parent enum with the bare variant name. In Rust, a bare identifier in pattern position that names nothing in scope is a fresh binding. So Add => … without BinOp::* in scope matches every value, and rustc only warns about it.

Change

  • refuse_unresolved_bare_variant_pattern now wraps the three pattern producers: emit_variant_pattern, emit_variant_pattern_rc_aware and variant_pattern_shape_for.
  • When the parent is unresolved and the render is a bare identifier (no {, ( or ::), the emitter writes compile_error! at the arm, naming the variant and the scrutinee.
  • Optional variants keep their Some/None lowering.

Census: the first cut was too broad, and that measurement is why the rule is keyed on the render. Refusing every unresolved parent broke both emitted crates.

  • The broken arms were v2.std.node EdgeLabel Named { name: _ }, matched over unstamped scrutinees in v2.std.compilers.body_lowering and v2.compiler.emit_produced.
  • Named is also declared by std.algebra and extdeps.formats.spice, so its parent never resolves.
  • Those arms render Named { .. }, a struct pattern that Rust resolves by path and never binds.

With the narrowed rule, measured locally:

  • gunbc test //gunbc/instruments:v2-native-cli: exit_status=0 warning_count=0.
  • gunbc test //gunbc/instruments:self-host: exit_status=0 warning_count=0.
  • No emitted file takes the refusal.

Discriminating red. The generated --lib test unresolved_variant_pattern_refuses_instead_of_binding_bare (v1.compiler.compiler_tests_rust ct_unresolved_variant_pattern_refuses_test):

  • Calls all three producers with no parent and requires the refusal.
  • Carries three controls: stamped parent renders BinOp::Add, optional Absent renders std::option::Option::None, and a fielded Named still renders Named { .. }.
  • Is green locally. With variant_pattern_parent_unresolved forced to false in the mirror it fails on the unresolved assertion, so it is a real red.

No accepted source program can produce this state: the typechecker refuses a variant name that is declared twice in one closure. So the red is authored at the unit boundary.

RFM: gunbc.recurring_failure_mode unresolved_variant_pattern_renders_as_a_catch_all_binding.

  • Found at: silent.
  • Now: mechanically preventable. It is held by the rust_unit_tests step, which is still off the merge path (rust_unit_tests_off_the_merge_path).
  • Ceiling: structurally impossible, once MatchPattern VariantPattern carries a resolved parent identity.
  • No live wrong-arm specimen exists: the original 52-warning report did not reproduce (see Enumerate BinOp arms in rust_refusing_int_operator_helper (NFR burn-down) #12733).

Follow-up, not in this PR: unstamped scrutinees with an ambiguous variant name. The census found v2.std.node EdgeLabel Named arms whose parent enum never resolves.

  • Named is declared in three modules (v2.std.node, std.algebra, extdeps.formats.spice), so unique_variant_parent answers none.
  • The scrutinee in v2.std.compilers.body_lowering and v2.compiler.emit_produced carries no type stamp, so the emitter can't pick the parent from it.
  • These arms render correctly today only because the braced Named { .. } resolves through the authored import. The emitter never establishes which Named it means.
  • Trigger: match-pattern resolution stamps VariantPattern.parent_enum (or the scrutinee type) for every arm. When that lands, these arms render as EdgeLabel::Named { .. } and the unresolved-parent population for this site goes to zero.

Regen: run locally (ulimit -v 24G, -j4, private target dir) until rebuild_packages=0. Only the installed mirrors are committed.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 2 commits September 30, 2026 06:17
…refuses instead of binding as a catch-all

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…2-pattern-refuse

# Conflicts:
#	src/v1/stage0/src/compiler_tests.rs
#	src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs
@gunbai-bot

gunbai-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

On the non-blocking point raised in review 73104 and review 73114 (rust_pattern_is_bare_identifier searches the rendered string, and pattern_parent_enum is recomputed): agreed that carrying the render shape as a typed value is cleaner. I'm not changing it here. The string check is exact for the three shapes these producers emit (bare, braced, parenthesised) plus path-qualified. The clean fix is the follow-up this PR's description already names: once VariantPattern.parent_enum is stamped for every arm, the Absent arm, the recomputation and this check all go away together. Doing half of that now would be churn that the follow-up deletes.
— sent from fierce-cat-462

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 453511b Sep 30, 2026
5 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/fierce-cat-462-pattern-refuse branch September 30, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants