Fix optional record value patterns

d8ccda79795212f25efe26d98848880d5e0ddb7b19c7909555b5bc9c829e05ef
Optional aggregate matching rejected non-nil record value patterns as
unsupported, and the binder assumed every record literal pattern
belonged to a union. Valid optional record comparisons therefore
failed lowering or entered union-only payload handling.

Use the resolver's optional-lifted structural comparison and skip
union payload binding for record value patterns that introduce no
bindings.
Alexis Sellier committed ago 1 parent 03643fc5
lib/std/lang/lower.rad +8 -4
2420 2420
2421 2421
            if isNil { // Optional aggregate: `nil` means tag is zero.
2422 2422
                let tagReg = tvalTagReg(self, base);
2423 2423
                try emitBr(self, tagReg, fallthrough, matchBlock);
2424 2424
            } else {
2425 -
                if isAggregateType(subject.bindType) {
2426 -
                    // TODO: Why?
2427 -
                    throw LowerError::Unsupported;
2428 -
                }
2425 +
                // `lowerExpr` applies the resolver's optional-lift coercion,
2426 +
                // materializing the tagged representation for value patterns.
2429 2427
                let pattVal = try lowerExpr(self, pattern);
2430 2428
                let pattReg = emitValToReg(self, pattVal);
2431 2429
                let eq = try lowerOptionalEq(self, subject.bindType, base, pattReg, 0);
2432 2430
                let eqReg = emitValToReg(self, eq);
2433 2431
3361 3359
fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) {
3362 3360
    // No fields to bind (e.g., `{ .. }`).
3363 3361
    if lit.fields.len == 0 {
3364 3362
        return;
3365 3363
    }
3364 +
    // Optional value patterns were already compared structurally by
3365 +
    // `emitPatternMatch`; unlike union record patterns, they have no payload
3366 +
    // bindings to extract here.
3367 +
    if let case MatchSubjectKind::OptionalAggregate = subject.kind {
3368 +
        return;
3369 +
    }
3366 3370
    // Get the union type info from the subject.
3367 3371
    let case MatchSubjectKind::Union(unionInfo) = subject.kind
3368 3372
        else panic "bindRecordPatternFields: expected union subject";
3369 3373
3370 3374
    // Get the variant index from the pattern node.
test/tests/optional.record.value.match.rad added +30 -0
1 +
//! returns: 0
2 +
//! Matching an optional record against a record value must compile and compare structurally.
3 +
4 +
record Pair {
5 +
    a: i32,
6 +
    b: i32,
7 +
}
8 +
9 +
fn classify(value: ?Pair) -> i32 {
10 +
    match value {
11 +
        case Pair { a: 3, b: 4 } => return 1,
12 +
        case nil => return 2,
13 +
        _ => return 3,
14 +
    }
15 +
}
16 +
17 +
@default fn main() -> i32 {
18 +
    let pair: Pair = Pair { a: 3, b: 4 };
19 +
    if classify(pair) <> 1 {
20 +
        return 1;
21 +
    }
22 +
    if classify(nil) <> 2 {
23 +
        return 2;
24 +
    }
25 +
    let other: Pair = Pair { a: 3, b: 5 };
26 +
    if classify(other) <> 3 {
27 +
        return 3;
28 +
    }
29 +
    return 0;
30 +
}