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
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); |