Skip to content

Commit b85026b

Browse files
committed
Auto merge of rust-lang#18252 - ShoyuVanilla:issue-15799, r=Veykril
fix: Do not consider mutable usage of deref to `*mut T` as deref_mut Fixes rust-lang#15799 We are doing some heuristics for deciding whether the given deref is deref or deref_mut here; https://github.com/rust-lang/rust-analyzer/blob/5982d9c420d0dc90739171829f0d2e9c80d98979/crates/hir-ty/src/infer/mutability.rs#L182-L200 But this heuristic is erroneous if we are dereferencing to a mut ptr and normally those cases are filtered out here as builtin; https://github.com/rust-lang/rust-analyzer/blob/5982d9c420d0dc90739171829f0d2e9c80d98979/crates/hir-ty/src/mir/lower/as_place.rs#L165-L177 Howerver, this works not so well if the given dereferencing is double dereferencings like the case in the rust-lang#15799. ```rust struct WrapPtr(*mut u32); impl core::ops::Deref for WrapPtr { type Target = *mut u32; fn deref(&self) -> &Self::Target { &self.0 } } fn main() { let mut x = 0u32; let wrap = WrapPtr(&mut x); unsafe { **wrap = 6; } } ``` Here are two - outer and inner - dereferences here, and the outer dereference is marked as deref_mut because there is an assignment operation. And this deref_mut marking is propagated into the inner dereferencing. In the later MIR lowering, the outer dereference is filtered out as it's expr type is `*mut u32`, but the expr type in the inner dereference is an ADT, so this false-mutablility is not filtered out. This PR cuts propagation of this false mutablilty chain if the expr type is mut ptr. Since this happens before the resolve_all, it may have some limitations when the expr type is determined as mut ptr at the very end of inferencing, but I couldn't find simple fix for it 🤔
2 parents a672968 + 3eea7af commit b85026b

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,25 @@ impl InferenceContext<'_> {
180180
self.infer_mut_expr(index, Mutability::Not);
181181
}
182182
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
183+
let mut mutability = mutability;
183184
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
184185
if mutability == Mutability::Mut {
185186
if let Some(deref_trait) = self
186187
.db
187188
.lang_item(self.table.trait_env.krate, LangItem::DerefMut)
188189
.and_then(|l| l.as_trait())
189190
{
190-
if let Some(deref_fn) = self
191+
let ty = self.result.type_of_expr.get(*expr);
192+
let is_mut_ptr = ty.is_some_and(|ty| {
193+
let ty = self.table.resolve_ty_shallow(ty);
194+
matches!(
195+
ty.kind(Interner),
196+
chalk_ir::TyKind::Raw(Mutability::Mut, _)
197+
)
198+
});
199+
if is_mut_ptr {
200+
mutability = Mutability::Not;
201+
} else if let Some(deref_fn) = self
191202
.db
192203
.trait_data(deref_trait)
193204
.method_by_name(&Name::new_symbol_root(sym::deref_mut.clone()))

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,31 @@ pub unsafe fn foo(a: *mut A) {
12551255
//^^^^^ 💡 warn: variable does not need to be mutable
12561256
let _ = b();
12571257
}
1258+
"#,
1259+
);
1260+
}
1261+
1262+
#[test]
1263+
fn regression_15799() {
1264+
check_diagnostics(
1265+
r#"
1266+
//- minicore: deref_mut
1267+
struct WrapPtr(*mut u32);
1268+
1269+
impl core::ops::Deref for WrapPtr {
1270+
type Target = *mut u32;
1271+
fn deref(&self) -> &Self::Target {
1272+
&self.0
1273+
}
1274+
}
1275+
1276+
fn main() {
1277+
let mut x = 0u32;
1278+
let wrap = WrapPtr(&mut x);
1279+
unsafe {
1280+
**wrap = 6;
1281+
}
1282+
}
12581283
"#,
12591284
);
12601285
}

0 commit comments

Comments
 (0)