Resolve optional match bindings by reference
8064a56d00e780ec8a9db5de7743527604708f64c8a97c9d2d02ab9bf9141c80
Matching through a pointer correctly recorded reference matching at the subject level, but optional-pattern resolution discarded that mode. The resolver typed a payload binding as a value while lowering produced a pointer, rejecting valid dereferences and creating inconsistent semantics. Thread `MatchBy` through optional match-prong resolution so payload bindings retain `Ref` or `MutRef` as appropriate.
1 parent
1ecd58a7
lib/std/lang/resolver.rad
+9 -4
| 4400 | 4400 | { |
|
| 4401 | 4401 | let subjectTy = try infer(self, sw.subject); |
|
| 4402 | 4402 | let subject = unwrapMatchSubject(subjectTy); |
|
| 4403 | 4403 | ||
| 4404 | 4404 | if let case Type::Optional(inner) = subject.effectiveTy { |
|
| 4405 | - | try resolveMatchOptional(self, node, sw, inner); |
|
| 4405 | + | try resolveMatchOptional(self, node, sw, inner, subject.by); |
|
| 4406 | 4406 | } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy { |
|
| 4407 | 4407 | try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by); |
|
| 4408 | 4408 | } else { |
|
| 4409 | 4409 | try resolveMatchGeneric(self, node, sw, subject.effectiveTy); |
|
| 4410 | 4410 | } |
| 4421 | 4421 | }; |
|
| 4422 | 4422 | return ty; |
|
| 4423 | 4423 | } |
|
| 4424 | 4424 | ||
| 4425 | 4425 | /// Analyze a `match` expression on an optional subject. |
|
| 4426 | - | fn resolveMatchOptional(self: *mut Resolver, node: *ast::Node, sw: ast::Match, innerTy: *Type) -> Type |
|
| 4427 | - | throws (ResolveError) |
|
| 4426 | + | fn resolveMatchOptional( |
|
| 4427 | + | self: *mut Resolver, |
|
| 4428 | + | node: *ast::Node, |
|
| 4429 | + | sw: ast::Match, |
|
| 4430 | + | innerTy: *Type, |
|
| 4431 | + | matchBy: MatchBy |
|
| 4432 | + | ) -> Type throws (ResolveError) |
|
| 4428 | 4433 | { |
|
| 4429 | 4434 | let subjectTy = Type::Optional(innerTy); |
|
| 4430 | 4435 | let prongs = sw.prongs; |
|
| 4431 | 4436 | let mut hasValue = false; |
|
| 4432 | 4437 | let mut hasNil = false; |
| 4455 | 4460 | // For optionals, a binding does *not* always match. |
|
| 4456 | 4461 | } |
|
| 4457 | 4462 | } |
|
| 4458 | 4463 | } |
|
| 4459 | 4464 | setProngCatchAll(self, prongNode, isCatchAll); |
|
| 4460 | - | set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, MatchBy::Value); |
|
| 4465 | + | set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy); |
|
| 4461 | 4466 | ||
| 4462 | 4467 | // Track coverage. Guarded prongs don't count as covering a case. |
|
| 4463 | 4468 | if prong.guard == nil { |
|
| 4464 | 4469 | if let case ast::ProngArm::Binding(_) = prong.arm { |
|
| 4465 | 4470 | if hasValue { |
test/tests/match.optional.ref.rad
added
+7 -0
| 1 | + | /// Match an optional aggregate through a reference. The value binding is a pointer. |
|
| 2 | + | fn matchOptionalRef(ptr: *?u32) -> u32 { |
|
| 3 | + | match ptr { |
|
| 4 | + | value => return *value, |
|
| 5 | + | case nil => return 0, |
|
| 6 | + | } |
|
| 7 | + | } |
test/tests/match.optional.ref.ril
added
+15 -0
| 1 | + | fn w32 $matchOptionalRef(w64 %0) { |
|
| 2 | + | @entry0 |
|
| 3 | + | jmp @arm1; |
|
| 4 | + | @arm1 |
|
| 5 | + | load w8 %1 %0 0; |
|
| 6 | + | br.ne w32 %1 0 @case2 @arm3; |
|
| 7 | + | @case2 |
|
| 8 | + | add w64 %2 %0 4; |
|
| 9 | + | load w32 %3 %2 0; |
|
| 10 | + | ret %3; |
|
| 11 | + | @arm3 |
|
| 12 | + | jmp @case4; |
|
| 13 | + | @case4 |
|
| 14 | + | ret 0; |
|
| 15 | + | } |