Merge pull request #19111 from ShoyuVanilla/issue-19021

fix: Apply adjustments to proper expr when invoking `CoerceMany`
This commit is contained in:
Lukas Wirth 2025-02-12 13:42:52 +00:00 committed by GitHub
commit c9838ec62d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 9 deletions

View File

@ -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) {

View File

@ -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<'_> {

View File

@ -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,
};
}
"#,
);
}

View File

@ -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