-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Do not suggest to use implicit DerefMut
on ManuallyDrop
reached through unions
#14387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
r? @Alexendoo rustbot has assigned @Alexendoo. Use |
cd1337d
to
263a25e
Compare
tests/ui/deref_addrof.rs
Outdated
// Lint, as `Copy` fields in unions are ok. | ||
(*(&raw mut a.with_padding)) = [1; size_of::<DataWithPadding>()]; | ||
//~^ deref_addrof | ||
*(*(&raw mut a.x)) = 0; | ||
//~^ deref_addrof | ||
(*(&raw mut a.y)).0 = 0; | ||
//~^ deref_addrof |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error looks to be specific to ManuallyDrop
rather than Copy
, e.g. this is an error even though everything is Copy
#[derive(Copy, Clone)]
struct Data {
num: u64,
}
union U {
data: ManuallyDrop<Data>,
}
fn x(mut a: U) {
unsafe {
a.data.num = 3;
}
}
*a.x = 0;
doesn't hit it because it doesn't use DerefMut
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error looks to be specific to
ManuallyDrop
rather thanCopy
, e.g. this is an error even though everything isCopy
Indeed. And it requires going through the parent exprs to check if a ManuallyDrop
is mutably dereferenced. I have reused some code from reference.rs
and shared it in clippy_utils::ty
.
263a25e
to
18d0707
Compare
DerefMut
on non-Copy
union fieldsDerefMut
on ManuallyDrop
union fields
@Alexendoo Anything missing on my side? |
clippy_lints/src/reference.rs
Outdated
/// Check if `expr` is an access to an union field which contains a dereferenced | ||
/// `ManuallyDrop<_>` entity. | ||
fn is_manually_drop_union_field(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ||
let typeck = cx.typeck_results(); | ||
if let ExprKind::Field(parent, _) = expr.kind | ||
&& typeck.expr_ty_adjusted(parent).is_union() | ||
{ | ||
for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { | ||
if let Node::Expr(expr) = node { | ||
if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) { | ||
return true; | ||
} | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently this lints
use std::mem::ManuallyDrop;
#[derive(Clone, Copy)]
struct B {
md: ManuallyDrop<[u8; 4]>
}
union U {
b: B,
}
fn x(mut u: U) {
unsafe {
(*&mut u.b.md)[3] = 1;
// ~~~~~~ `expr`
}
}
Currently we only check if u.b
is a union
, but we need to keep going while we still see field/index expressions since it doesn't have to directly in the union
The parent iter I believe can be replaced with is_manually_drop([type of expr])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll check this, thanks for the review. @rustbot author
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, the cases you mention, and some similar ones, are no longer linted.
However, I am not sure that
The parent iter I believe can be replaced with
is_manually_drop([type of expr])
holds, as not all ManuallyDrop
are equal here: **&mut a.prim = 0
is linted and fixed as *a.prim = 0
, as there is an explicit deref left. However, (*&mut a.data).num = 42
is currently no longer linted, as a.data.num = 42
would be an implicit deref, but we could suggest to keep the explicit dereference instead, as in (*a.data).num = 42
.
I'll think more about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not as simple as keeping the reference and removing the dereference in some case where the implicit dereference happens later in the expression:
(*&mut a.tup).0.num = 42;
should be rewritten as
(*a.tup.0).num = 42;
if we want to remove the &num
. That would require moving the explicit dereference to the point where the implicit DerefMut
would take place, so this case should not be linted.
I've added a new commit which reinstates those cases and suggests the proper fix. I may squash the two commits after the review.
One thing I have not checked is if it is really wise to keep linting in macros, as the lint is currently doing. If a macro is used with a ManuallyDrop
field reached through a union, the lint may incorrectly suggest fixing the macro in cases where it works fine, which would generate an error in other cases. I would be in favor of disabling the lint in macros, even if we have false negatives, this would be better than false positives. Thoughts?
@rustbot ready
This comment has been minimized.
This comment has been minimized.
This is necessary in order to use type analysis in later commits.
18d0707
to
89124a0
Compare
Rebased |
Reminder, once the PR becomes ready for a review, use |
89124a0
to
dde75ef
Compare
DerefMut
on ManuallyDrop
union fieldsDerefMut
on ManuallyDrop
reached through unions
dde75ef
to
ca9e429
Compare
Suggesting to remove `*&` or `*&mut` in a macro may be incorrect, as it would require tracking all possible macro usages. In some cases, it is not possible to remove the dereference or the reference. For example, in the following code, the `drmut!()` macro will be linted against while called as `drmut!(d)`, and the suggestion would be to remove the `*&mut`. That would make `drmut!(u.data).num = 1` invalid, as a `ManuallyDrop` object coming through a union cannot be implicitely dereferenced through `DerefMut`. ```rust use std::mem::ManuallyDrop; #[derive(Copy, Clone)] struct Data { num: u64, } union Union { data: ManuallyDrop<Data>, } macro_rules! drmut { ($e:expr) => { *&mut $e }; } fn f(mut u: Union, mut d: Data) { unsafe { drmut!(u.data).num = 1; } drmut!(d).num = 1; } ```
e5aab3a
to
24dd8a0
Compare
This requires making the
deref_addrof
lint a late lint instead of an early lint to check for types.changelog: [
deref_addrof
]: do not suggest implicitDerefMut
onManuallyDrop
union fieldsFix #14386