Skip to content

Allow dead enum match carriers after mixed arm moves #101

Description

@itsfuad

Problem

Statement match currently rejects normal enum inspection code when one arm moves an ownership-tracked payload and other arms only read/copy, even when the carrier is not used after the match.

Example:

enum Result<T> {
    Ok: { value: T },
    Error: { message: str },
    Pending
}

fn main() {
    let result = Result<i32>::Error{ message = "failed" };

    match result {
        Result<i32>::Ok{ value = payload } => {
            println(payload);
        }
        Result<i32>::Error{ message = message } => {
            println(message);
        }
        Result<i32>::Pending => {
            println(2);
        }
    }
}

Current diagnostic:

T0007 ownership state differs across control-flow paths

Expected behavior

This should compile when result is dead after the match. Only later use of result should require all reachable arms to leave a usable carrier state.

Required ownership model

  • Keep current direct-carrier move rule for move-only payload fields.
  • Keep rejecting move-only extraction from partial carriers: enum fields, indexes, pointees, temporaries, and other non-direct places.
  • At a match join, distinguish values needed after the join from values that are dead after the join.
  • Allow mixed arm ownership states only for carriers with no later use and no pending whole-carrier cleanup obligation.
  • Preserve per-arm cleanup:
    • moved/listed fields handled by arm body ownership;
    • omitted owned fields dropped on consuming arms;
    • untouched carriers cleaned normally on preserving arms;
    • no duplicate whole-carrier cleanup.
  • If the carrier is used after match, keep rejecting mixed ownership states or require reinitialization on consuming arms.

Polonius relationship

Rust's Polonius work is useful design input because it makes facts location-sensitive across CFG paths. This issue is not Polonius itself: it is ownership/drop-state convergence and cleanup planning for match.

Validation

  • Add a positive runtime fixture where each arm prints/uses its payload and the carrier is not used after match.
  • Add a negative fixture where the same mixed-state match is followed by use(result).
  • Add regression for explicit _ discard of an owned field.
  • Preserve existing positive fixtures for reinitialization and terminating consuming arms.
  • Preserve partial-carrier move rejection fixtures.
  • Add focused ownership tests proving cleanup is emitted exactly once per path.

Activity

  1. moved this from Todo to In Progress in Peeper Roadmapon Aug 25, 2026
  2. itsfuad commented on Aug 25, 2026

    @itsfuad
    MemberAuthor

    Implemented by 63e4288 on PR #98. Dead direct match carriers now normalize ownership and cleanup across falling arms when not live after the shared join. Live-after-match use, drop-bearing overwrite obligations, and partial-carrier moves remain rejected. Focused tests, full Go tests, vet, race, compiler build, full x_test, and PR CI pass. Keeping issue open and project item In Progress until containing PR #98 merges.

  3. moved this from In Progress to Done in Peeper Roadmapon Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions