about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-02-12 13:42:52 +0000
committerGitHub <noreply@github.com>2025-02-12 13:42:52 +0000
commit4a1559344f0413b9f52bb512ec5fd8e40f668d2d (patch)
tree200f5d55c7f4adae49159d9f72027b2c5f6cf0fb
parent53e687058c78b737accabc736cf469d63c7ff7cf (diff)
parent720101002876ad6043735004b9ae20600a989fe8 (diff)
downloadrust-4a1559344f0413b9f52bb512ec5fd8e40f668d2d.tar.gz
rust-4a1559344f0413b9f52bb512ec5fd8e40f668d2d.zip
Merge pull request #19111 from ShoyuVanilla/issue-19021
fix: Apply adjustments to proper expr when invoking `CoerceMany`
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs38
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs3
4 files changed, 89 insertions, 9 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 617ebba8811..9f20117bf14 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1239,7 +1239,29 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn write_expr_adj(&mut self, expr: ExprId, adjustments: Vec<Adjustment>) {
-        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/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index d40816ba8ce..b490a31f68f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/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<Ty, TypeError> {
-        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<Adjustment>, 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/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index f1e86daea23..9625ae5f88e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/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>(T);
+
+impl<T> Deref for Box<T> {
+    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/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index 7992f1feeeb..7e7c1f835c7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/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