diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index ced2cf2b57b14..497ea92d71fa8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1164,10 +1164,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { exprs.len() ); - // The following check fixes #88097, where the compiler erroneously - // attempted to coerce a closure type to itself via a function pointer. - if prev_ty == new_ty { - return Ok(prev_ty); + // If the prev and new ty can be lub'd then just do that instead of + // trying to coerce them. This avoids coercing closures and FnDefs + // to function pointers when we have two closures or FnDefs of the + // same DefId. E.g. `FooFnDef coerce-lub FooFnDef` will + // result in relating `?x` and `?y` rather than coercing both to + // some shared `fn(?z)` type. This check fixed #88097. + match self.commit_if_ok(|_| { + // We need to eagerly handle nested obligations due to lazy norm. + if self.next_trait_solver() { + let ocx = ObligationCtxt::new(self); + let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; + if ocx.select_where_possible().is_empty() { + Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) + } + } else { + self.at(cause, self.param_env).lub(prev_ty, new_ty) + } + }) { + // We have a LUB of prev_ty and new_ty, just return it. + Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), + Err(_) => {} } let is_force_inline = |ty: Ty<'tcx>| { @@ -1196,31 +1215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { match (prev_ty.kind(), new_ty.kind()) { (ty::FnDef(..), ty::FnDef(..)) => { - // Don't reify if the function types have a LUB, i.e., they - // are the same function and their parameters have a LUB. - match self.commit_if_ok(|_| { - // We need to eagerly handle nested obligations due to lazy norm. - if self.next_trait_solver() { - let ocx = ObligationCtxt::new(self); - let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; - if ocx.select_where_possible().is_empty() { - Ok(InferOk { - value, - obligations: ocx.into_pending_obligations(), - }) - } else { - Err(TypeError::Mismatch) - } - } else { - self.at(cause, self.param_env).lub(prev_ty, new_ty) - } - }) { - // We have a LUB of prev_ty and new_ty, just return it. - Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), - Err(_) => { - (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx))) - } - } + (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx))) } (ty::Closure(_, args), ty::FnDef(..)) => { let b_sig = new_ty.fn_sig(self.tcx); diff --git a/tests/ui/coercion/issue-88097.rs b/tests/ui/coercion/issue-88097.rs index f636323d62367..a5804e3b789cb 100644 --- a/tests/ui/coercion/issue-88097.rs +++ b/tests/ui/coercion/issue-88097.rs @@ -3,6 +3,9 @@ // behavior has been fixed. //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver fn peculiar() -> impl Fn(u8) -> u8 { return |x| x + 1