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