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.
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 | + | } |