mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Merge pull request #19111 from ShoyuVanilla/issue-19021
fix: Apply adjustments to proper expr when invoking `CoerceMany`
This commit is contained in:
commit
c9838ec62d
@ -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) {
|
||||
|
@ -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<'_> {
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user