rust-lang / rust-lang/rust-clippy

unsound trait impl on union

Open
#8,946 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

What it does

Warns if a trait is manually implemented on a union type, if it's not safe to do so.

For example

#[repr(C)]
pub union Foo {
    bar: u32,
    baz: u64
}
impl PartialEq on Foo {
    fn partial_eq(&self, &other: Self) -> bool {
        // Unsound!  The baz field may be uninitialized data, if either argument was initialized like
        // let x = Foo{bar: 42}
        self.baz == other.baz
    }
}

A common pattern for using unions is to initialize only the fields one intends to use. But there's no way for most trait impls to know which fields those are. Accessing an uninitialized field is undefined behavior. Even if the entire object was zero-initialized like let mut x: Foo = mem::zeroed(); x.bar =42, it's still not possible to correctly implement PartialEq, because the implementation doesn't know which fields "matter". For example:

let mut x = Foo{baz: 0x1111111111111111};
let mut y = Foo{baz: 0x2222222222222222};
x.bar = 42;
y.bar = 42;
assert_eq!(x, y);

In this example, logically x should equal y. But any PartialEq implementation would disagree, if it checks all of the struct's bits.
Similar arguments could be made for Hash, PartialOrd, and even Debug.

Lint Name

unsound_trait_impl_on_union

Category

correctness

Advantage

It will prevent undefined behavior, and prevent incorrect behavior of basic operations like ==. Such a lint could've caught bugs like https://github.com/rust-lang/libc/issues/2816 .

Drawbacks

Occasionally it may actually be ok to implement these traits on a union, for example when every member has the same size.

Example
#[repr(C)]
pub union Foo {
    bar: u32,
    baz: u64
}
impl PartialEq on Foo {
    fn partial_eq(&self, &other: Self) -> bool {
        // Unsound!  The baz field may be uninitialized data, if either argument was initialized like
        // let x = Foo{bar: 42}
        self.baz == other.baz
    }
}

The PartialEq implementation should be omitted. There's simply no way to do it correctly.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Use the issue's unsound_trait_impl_on_union proposal and its PartialEq, Hash, PartialOrd, and Debug examples as the behavioral scope. Determine how the lint should identify unsafe or incorrect trait implementations on unions, including the stated same-size-member exception; done means the lint prevents the described cases without rejecting valid implementations.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.