Skip to content

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

samueltardieu
Copy link
Contributor

@samueltardieu samueltardieu commented Mar 10, 2025

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 implicit DerefMut on ManuallyDrop union fields

Fix #14386

@rustbot
Copy link
Collaborator

rustbot commented Mar 10, 2025

r? @Alexendoo

rustbot has assigned @Alexendoo.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 10, 2025
Comment on lines 100 to 113
// 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
Copy link
Member

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

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

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.

@samueltardieu samueltardieu changed the title Do not suggest to use implicit DerefMut on non-Copy union fields Do not suggest to use implicit DerefMut on ManuallyDrop union fields Mar 11, 2025
@samueltardieu
Copy link
Contributor Author

@Alexendoo Anything missing on my side?

Comment on lines 108 to 123
/// 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
}
Copy link
Member

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])

Copy link
Contributor Author

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

@rustbot

This comment has been minimized.

This is necessary in order to use type analysis in later commits.
@samueltardieu
Copy link
Contributor Author

Rebased

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label May 6, 2025
@rustbot
Copy link
Collaborator

rustbot commented May 6, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) label May 6, 2025
@samueltardieu samueltardieu changed the title Do not suggest to use implicit DerefMut on ManuallyDrop union fields Do not suggest to use implicit DerefMut on ManuallyDrop reached through unions May 7, 2025
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels May 7, 2025
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;
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clippy suggests broken code when modifying union fields behind ManuallyDrop
3 participants