diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 617ebba881..9f20117bf1 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1239,7 +1239,29 @@ impl<'a> InferenceContext<'a> { } fn write_expr_adj(&mut self, expr: ExprId, adjustments: Vec) { - self.result.expr_adjustments.insert(expr, adjustments); + if adjustments.is_empty() { + return; + } + match self.result.expr_adjustments.entry(expr) { + std::collections::hash_map::Entry::Occupied(mut entry) => { + match (&mut entry.get_mut()[..], &adjustments[..]) { + ( + [Adjustment { kind: Adjust::NeverToAny, target }], + [.., Adjustment { target: new_target, .. }], + ) => { + // NeverToAny coercion can target any type, so instead of adding a new + // adjustment on top we can change the target. + *target = new_target.clone(); + } + _ => { + *entry.get_mut() = adjustments; + } + } + } + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(adjustments); + } + } } fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) { diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index d40816ba8c..b490a31f68 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -163,10 +163,27 @@ impl CoerceMany { // 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 let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) { - self.final_ty = Some(res); - } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty, CoerceNever::Yes) { + // + // - [Comment from rustc](https://github.com/rust-lang/rust/blob/5ff18d0eaefd1bd9ab8ec33dab2404a44e7631ed/compiler/rustc_hir_typeck/src/coercion.rs#L1334-L1335) + // First try to coerce the new expression to the type of the previous ones, + // but only if the new expression has no coercion already applied to it. + if expr.is_none_or(|expr| !ctx.result.expr_adjustments.contains_key(&expr)) { + if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) { + self.final_ty = Some(res); + if let Some(expr) = expr { + self.expressions.push(expr); + } + return; + } + } + + if let Ok((adjustments, res)) = + ctx.coerce_inner(&self.merged_ty(), &expr_ty, CoerceNever::Yes) + { self.final_ty = Some(res); + for &e in &self.expressions { + ctx.write_expr_adj(e, adjustments.clone()); + } } else { match cause { CoercionCause::Expr(id) => { @@ -244,14 +261,23 @@ impl InferenceContext<'_> { // between places and values. coerce_never: CoerceNever, ) -> Result { - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); - let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty, coerce_never)?; + let (adjustments, ty) = self.coerce_inner(from_ty, to_ty, coerce_never)?; if let Some(expr) = expr { self.write_expr_adj(expr, adjustments); } Ok(ty) } + + fn coerce_inner( + &mut self, + from_ty: &Ty, + to_ty: &Ty, + coerce_never: CoerceNever, + ) -> Result<(Vec, Ty), TypeError> { + let from_ty = self.resolve_ty_shallow(from_ty); + let to_ty = self.resolve_ty_shallow(to_ty); + self.table.coerce(&from_ty, &to_ty, coerce_never) + } } impl InferenceTable<'_> { diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index f1e86daea2..9625ae5f88 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -912,3 +912,36 @@ fn main() { "", ); } + +#[test] +fn regression_19021() { + check_pass( + r#" +//- minicore: deref +use core::ops::Deref; + +#[lang = "owned_box"] +struct Box(T); + +impl Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct Foo; + +fn main() { + let x = Box(Foo); + let y = &Foo; + + || match x { + ref x => x, + _ => y, + }; +} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index 7992f1feee..7e7c1f835c 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -185,11 +185,10 @@ fn test() { let t = &mut 1; let x = match 1 { 1 => t as *mut i32, + //^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer) 2 => t as &i32, //^^^^^^^^^ expected *mut i32, got &'? i32 _ => t as *const i32, - // ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer) - }; x; //^ type: *const i32