Fix handling of diverging branches in match coercion

Fixes #7626.
This commit is contained in:
Florian Diebold 2021-05-15 17:01:27 +02:00
parent a09079f27a
commit 32fc944263
2 changed files with 52 additions and 4 deletions

View file

@ -45,6 +45,10 @@ impl<'a> InferenceContext<'a> {
/// - if we were concerned with lifetime subtyping, we'd need to look for a
/// least upper bound.
pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
let ty1 = self.resolve_ty_shallow(ty1);
let ty1 = ty1.as_ref();
let ty2 = self.resolve_ty_shallow(ty2);
let ty2 = ty2.as_ref();
// Special case: two function types. Try to coerce both to
// pointers to have a chance at getting a match. See
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
@ -71,12 +75,17 @@ impl<'a> InferenceContext<'a> {
}
}
if self.coerce(ty1, ty2) {
ty2.clone()
} else if self.coerce(ty2, ty1) {
// It might not seem like it, but order is important here: ty1 is our
// "previous" type, ty2 is the "new" one being added. If the previous
// type is a type variable and the new one is `!`, trying it the other
// way around first would mean we make the type variable `!`, instead of
// just marking it as possibly diverging.
if self.coerce(ty2, ty1) {
ty1.clone()
} else if self.coerce(ty1, ty2) {
ty2.clone()
} else {
// FIXME record a type mismatch
// TODO record a type mismatch
cov_mark::hit!(coerce_merge_fail_fallback);
ty1.clone()
}

View file

@ -873,3 +873,42 @@ fn foo(c: i32) {
"#,
)
}
#[test]
fn infer_match_diverging_branch_1() {
check_types(
r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }
fn test() -> i32 {
let a = match parse() {
Ok(val) => val,
Err => return 0,
};
a
//^ i32
}
"#,
)
}
#[test]
fn infer_match_diverging_branch_2() {
// same as 1 except for order of branches
check_types(
r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }
fn test() -> i32 {
let a = match parse() {
Err => return 0,
Ok(val) => val,
};
a
//^ i32
}
"#,
)
}