resolver: Borrow ownership branch inputs

587e7cb31c2cb5d0a65f4580153cd53bb1fa6be599e693ca7fa53dc26315e5d7
Pass ownership environments by pointer and copy only the selected branch
into the output. Merge availability in a local bitset so validation
completes before an aliased output changes either input.

Preserve branch diagnostics, terminated-path selection, and availability
bits outside the active scope.

Assisted-by: Codex:gpt-6-astra
Alexis Sellier committed ago 1 parent 6436757e
lib/std/lang/resolver.rad +24 -22
7510 7510
    }
7511 7511
    set env.available &= ~((1 as u64) << (index as u64));
7512 7512
}
7513 7513
7514 7514
/// Merge ownership availability across two live branches.
7515 +
/// Validate both inputs before writing to an output that can alias either input.
7515 7516
fn joinLinearBranches(
7516 7517
    checker: *mut LinearChecker,
7517 7518
    env: *mut LinearEnv,
7518 -
    left: LinearEnv,
7519 -
    right: LinearEnv,
7519 +
    left: *LinearEnv,
7520 +
    right: *LinearEnv,
7520 7521
    node: *ast::Node,
7521 7522
) throws (ResolveError) {
7522 7523
    if left.terminated and right.terminated {
7523 -
        set *env = left;
7524 +
        set *env = *left;
7524 7525
        set env.terminated = true;
7525 7526
        return;
7526 7527
    }
7527 7528
    if left.terminated {
7528 -
        set *env = right;
7529 +
        set *env = *right;
7529 7530
        return;
7530 7531
    }
7531 7532
    if right.terminated {
7532 -
        set *env = left;
7533 +
        set *env = *left;
7533 7534
        return;
7534 7535
    }
7535 7536
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
7536 -
    let mut result = left;
7537 +
    let mut available = left.available;
7537 7538
    for i in 0..left.len {
7538 -
        if linearBindingAvailable(&left, i) <> linearBindingAvailable(&right, i) {
7539 +
        if linearBindingAvailable(left, i) <> linearBindingAvailable(right, i) {
7539 7540
            let sym = left.symbols[i];
7540 7541
            let case SymbolData::Value { type: ty, .. } = sym.data
7541 7542
                else panic "joinLinearBranches: expected value symbol";
7542 7543
            if isLinear(ty) {
7543 7544
                throw emitError(
7544 7545
                    checker.resolver,
7545 7546
                    node,
7546 7547
                    ErrorKind::LinearBranchMismatch(sym.name),
7547 7548
                );
7548 7549
            }
7549 -
            set result.available &= ~((1 as u64) << (i as u64));
7550 +
            set available &= ~((1 as u64) << (i as u64));
7550 7551
        }
7551 7552
    }
7552 -
    set *env = result;
7553 +
    set *env = *left;
7554 +
    set env.available = available;
7553 7555
}
7554 7556
7555 7557
/// Require all available exact-use bindings to be consumed at a function exit.
7556 7558
fn finishLinearExit(
7557 7559
    checker: *mut LinearChecker,
7729 7731
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
7730 7732
    let mut elseEnv = base;
7731 7733
    if let branch = conditional.elseBranch {
7732 7734
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7733 7735
    }
7734 -
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7736 +
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7735 7737
}
7736 7738
7737 7739
/// Check an expression conditional and merge its ownership states.
7738 7740
fn checkLinearCondExpr(
7739 7741
    checker: *mut LinearChecker,
7746 7748
    let base = *env;
7747 7749
    let mut thenEnv = base;
7748 7750
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
7749 7751
    let mut elseEnv = base;
7750 7752
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
7751 -
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7753 +
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7752 7754
}
7753 7755
7754 7756
/// Check a match expression, including ownership transferred into patterns.
7755 7757
fn checkLinearMatch(
7756 7758
    checker: *mut LinearChecker,
7792 7794
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
7793 7795
        }
7794 7796
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
7795 7797
        try finishLinearScope(checker, &mut branch, bindingsStart);
7796 7798
        if haveResult {
7797 -
            try joinLinearBranches(checker, &mut result, result, branch, node);
7799 +
            try joinLinearBranches(checker, &mut result, &result, &branch, node);
7798 7800
        } else {
7799 7801
            set result = branch;
7800 7802
            set haveResult = true;
7801 7803
        }
7802 7804
    }
7947 7949
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
7948 7950
    let mut elseEnv = base;
7949 7951
    if let branch = conditional.elseBranch {
7950 7952
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7951 7953
    }
7952 -
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7954 +
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
7953 7955
}
7954 7956
7955 7957
/// Check one expression or statement under an ownership-use context.
7956 7958
fn checkLinearNode(
7957 7959
    checker: *mut LinearChecker,
8162 8164
                    LinearUse::Consume,
8163 8165
                );
8164 8166
                try joinLinearBranches(
8165 8167
                    checker,
8166 8168
                    &mut fallbackEnv,
8167 -
                    fallbackEnv,
8168 -
                    guardFallbackEnv,
8169 +
                    &fallbackEnv,
8170 +
                    &guardFallbackEnv,
8169 8171
                    binding.elseBranch,
8170 8172
                );
8171 8173
            }
8172 8174
            if let case ast::PatternKind::Binding = binding.pattern.kind {
8173 8175
                try addLinearPatternBindings(
8174 8176
                    checker,
8175 8177
                    &mut fallbackEnv,
8176 8178
                    binding.pattern.pattern,
8177 8179
                );
8178 8180
            }
8179 -
            try joinLinearBranches(checker, env, successEnv, fallbackEnv, node);
8181 +
            try joinLinearBranches(checker, env, &successEnv, &fallbackEnv, node);
8180 8182
        }
8181 8183
        case ast::NodeValue::Match(matchExpr) => {
8182 8184
            try checkLinearMatch(checker, env, node, matchExpr);
8183 8185
        }
8184 8186
        case ast::NodeValue::Try(tryExpr) => {
8192 8194
                if let binding = catchClause.binding {
8193 8195
                    try addLinearBinding(checker, &mut branch, binding);
8194 8196
                }
8195 8197
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
8196 8198
                try finishLinearScope(checker, &mut branch, start);
8197 -
                try joinLinearBranches(checker, env, *env, branch, node);
8199 +
                try joinLinearBranches(checker, env, env, &branch, node);
8198 8200
            }
8199 8201
        }
8200 8202
        case ast::NodeValue::While(whileStmt) => {
8201 8203
            enterLinearLoop(checker, env);
8202 8204
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8213 8215
                    checker,
8214 8216
                    &mut elseEnv,
8215 8217
                    elseBranch,
8216 8218
                    LinearUse::Discard,
8217 8219
                );
8218 -
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8220 +
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8219 8221
            }
8220 8222
        }
8221 8223
        case ast::NodeValue::WhileLet(whileStmt) => {
8222 8224
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8223 8225
                isLinear(subjectTy)
8245 8247
                let mut guardExit = bodyEnv;
8246 8248
                try finishLinearScope(checker, &mut guardExit, start);
8247 8249
                try joinLinearBranches(
8248 8250
                    checker,
8249 8251
                    &mut conditionExit,
8250 -
                    conditionExit,
8251 -
                    guardExit,
8252 +
                    &conditionExit,
8253 +
                    &guardExit,
8252 8254
                    guard,
8253 8255
                );
8254 8256
            }
8255 8257
            setLinearLoopNaturalExit(checker, &conditionExit);
8256 8258
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8264 8266
                    checker,
8265 8267
                    &mut elseEnv,
8266 8268
                    elseBranch,
8267 8269
                    LinearUse::Discard,
8268 8270
                );
8269 -
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8271 +
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8270 8272
            }
8271 8273
        }
8272 8274
        case ast::NodeValue::For(forStmt) => {
8273 8275
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8274 8276
                if isLinear(iterableTy) {
8300 8302
                    checker,
8301 8303
                    &mut elseEnv,
8302 8304
                    elseBranch,
8303 8305
                    LinearUse::Discard,
8304 8306
                );
8305 -
                try joinLinearBranches(checker, env, base, elseEnv, node);
8307 +
                try joinLinearBranches(checker, env, &base, &elseEnv, node);
8306 8308
            }
8307 8309
        }
8308 8310
        case ast::NodeValue::Loop { body } => {
8309 8311
            let base = *env;
8310 8312
            enterLinearLoop(checker, env);