compiler: Return initialized try call results
49408f75f6f8ffcbaa8d0f22045bc88deb2e6f1f66dfdc60f2b0245cd962dd23
1 parent
fb2692fb
lib/std/lang/lower.rad
+24 -15
| 6420 | 6420 | try emitTrapIfLt(self, il::Type::W32, capVal, lenVal); |
|
| 6421 | 6421 | } |
|
| 6422 | 6422 | return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal); |
|
| 6423 | 6423 | } |
|
| 6424 | 6424 | ||
| 6425 | + | /// Prepare a throwing call and record deferred session writes when required. |
|
| 6426 | + | /// The caller supplies an empty initialization slot. |
|
| 6427 | + | unsafe fn prepareTryCall 'arena 'phase 'function ( |
|
| 6428 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6429 | + | node: *ast::Node, |
|
| 6430 | + | call: ast::Call, |
|
| 6431 | + | initialization: &mut ?SessionInitialization |
|
| 6432 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6433 | + | let extra = resolver::nodeData(self.low.resolver, node).extra; |
|
| 6434 | + | if let case resolver::NodeExtra::SessionAllocation(allocation) = extra { |
|
| 6435 | + | let prepared = try prepareSessionAllocation(self, call, allocation); |
|
| 6436 | + | set *initialization = prepared; |
|
| 6437 | + | return prepared.result; |
|
| 6438 | + | } |
|
| 6439 | + | if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = extra { |
|
| 6440 | + | return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex); |
|
| 6441 | + | } |
|
| 6442 | + | if let case resolver::NodeExtra::MethodCall { method } = extra { |
|
| 6443 | + | return try lowerMethodCall(self, node, call, method); |
|
| 6444 | + | } |
|
| 6445 | + | return try lowerCall(self, node, call); |
|
| 6446 | + | } |
|
| 6447 | + | ||
| 6425 | 6448 | /// Lower a `try` expression. |
|
| 6426 | 6449 | unsafe fn lowerTry 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6427 | 6450 | let case ast::NodeValue::Call(callExpr) = t.expr.value else { |
|
| 6428 | 6451 | throw LowerError::ExpectedCall; |
|
| 6429 | 6452 | }; |
| 6435 | 6458 | ||
| 6436 | 6459 | // Type of the try expression, which is either the return type of the function |
|
| 6437 | 6460 | // if successful, or an optional of it, if using `try?`. |
|
| 6438 | 6461 | let tryExprTy = try typeOf(self, node); |
|
| 6439 | 6462 | // Check for trait method dispatch or standalone method call. |
|
| 6440 | - | let mut resVal: il::Val = undefined; |
|
| 6441 | 6463 | let mut initialization: ?SessionInitialization = nil; |
|
| 6442 | - | let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra; |
|
| 6443 | - | if let case resolver::NodeExtra::SessionAllocation(allocation) = callNodeExtra { |
|
| 6444 | - | let prepared = try prepareSessionAllocation(self, callExpr, allocation); |
|
| 6445 | - | set initialization = prepared; |
|
| 6446 | - | set resVal = prepared.result; |
|
| 6447 | - | } else if let case resolver::NodeExtra::TraitMethodCall { |
|
| 6448 | - | traitInfo, methodIndex |
|
| 6449 | - | } = callNodeExtra { |
|
| 6450 | - | set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex); |
|
| 6451 | - | } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra { |
|
| 6452 | - | set resVal = try lowerMethodCall(self, t.expr, callExpr, method); |
|
| 6453 | - | } else { |
|
| 6454 | - | set resVal = try lowerCall(self, t.expr, callExpr); |
|
| 6455 | - | } |
|
| 6464 | + | let resVal = try prepareTryCall(self, t.expr, callExpr, &mut initialization); |
|
| 6456 | 6465 | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 6457 | 6466 | return il::Val::Undef; |
|
| 6458 | 6467 | } |
|
| 6459 | 6468 | let base = emitValToReg(self, resVal); // The result value. |
|
| 6460 | 6469 | let tagReg = resultTagReg(self, base); // The result tag. |
test/tests/try.dispatch.results.rad
added
+57 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Receiver contribution to successful results. |
|
| 4 | + | record Source: Copy { |
|
| 5 | + | /// Value added to successful calls. |
|
| 6 | + | base: u32, |
|
| 7 | + | } |
|
| 8 | + | ||
| 9 | + | /// Ordinary throwing call. |
|
| 10 | + | fn attempt(value: u32, accepted: bool) -> u32 throws (u32) { |
|
| 11 | + | if not accepted { |
|
| 12 | + | throw value; |
|
| 13 | + | } |
|
| 14 | + | return value; |
|
| 15 | + | } |
|
| 16 | + | ||
| 17 | + | /// Standalone throwing method. |
|
| 18 | + | fn (source: &Source) direct(value: u32, accepted: bool) -> u32 throws (u32) { |
|
| 19 | + | return source.base + try attempt(value, accepted); |
|
| 20 | + | } |
|
| 21 | + | ||
| 22 | + | /// Dynamically dispatched throwing operation. |
|
| 23 | + | trait Attempt { |
|
| 24 | + | /// Return the receiver contribution or fail. |
|
| 25 | + | fn (&Attempt) invoke(value: u32, accepted: bool) -> u32 throws (u32); |
|
| 26 | + | } |
|
| 27 | + | ||
| 28 | + | instance Attempt for Source { |
|
| 29 | + | /// Propagate ordinary-call errors through trait dispatch. |
|
| 30 | + | fn (source: &Source) invoke(value: u32, accepted: bool) -> u32 throws (u32) { |
|
| 31 | + | return source.base + try attempt(value, accepted); |
|
| 32 | + | } |
|
| 33 | + | } |
|
| 34 | + | ||
| 35 | + | /// Compare try-optional success and failure across dispatch modes. |
|
| 36 | + | @default unsafe fn main() -> u32 { |
|
| 37 | + | let source = Source { base: 100 }; |
|
| 38 | + | let dynamic: *unsafe opaque Attempt = &source; |
|
| 39 | + | for value in 0..8 { |
|
| 40 | + | let ordinary = try? attempt(value, true); |
|
| 41 | + | let direct = try? source.direct(value, true); |
|
| 42 | + | let indirect = try? dynamic.invoke(value, true); |
|
| 43 | + | let ordinaryValue = ordinary else 999; |
|
| 44 | + | let directValue = direct else 999; |
|
| 45 | + | let indirectValue = indirect else 999; |
|
| 46 | + | assert ordinaryValue == value; |
|
| 47 | + | assert directValue == value + 100; |
|
| 48 | + | assert indirectValue == value + 100; |
|
| 49 | + | let ordinaryError = try? attempt(value, false); |
|
| 50 | + | let directError = try? source.direct(value, false); |
|
| 51 | + | let indirectError = try? dynamic.invoke(value, false); |
|
| 52 | + | assert ordinaryError == nil; |
|
| 53 | + | assert directError == nil; |
|
| 54 | + | assert indirectError == nil; |
|
| 55 | + | } |
|
| 56 | + | return 0; |
|
| 57 | + | } |