compiler: Return initialized try call results

49408f75f6f8ffcbaa8d0f22045bc88deb2e6f1f66dfdc60f2b0245cd962dd23
Alexis Sellier committed ago 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 +
}