Skip to content
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

Be stricter about combining repr(packed) with derive. #99197

Closed

Conversation

nnethercote
Copy link
Contributor

@nnethercote nnethercote commented Jul 13, 2022

Currently there are two errors you can get when using derive on a
repr(packed) struct:

  • "#[derive] can't be used on a #[repr(packed)] struct with type
    or const parameters (error E0133)"
  • "#[derive] can't be used on a #[repr(packed)] struct that does not
    derive Copy (error E0133)"

These were introduced under #82523, because they can lead to unaligned
references, which is UB. They are in the process of being upgraded to
hard errors.

This is all fine, but the second error overstates things. It is
currently possible to use derive on a repr(packed) struct that doesn't derive
Copy in two cases.

  • If all the fields within the struct meet the required alignment: 1 for
    repr(packed), or N for repr(packed(N)).
  • Or, If Default is the only trait derived.

These exceptions don't seem particularly important or useful, and the
language would be simpler if they are removed. So this commit does that,
making these cases a hard error. The new checks are done at derive code
creation time which means that unsafe_derive_on_repr_packed is no
longer needed at MIR build time.

Test changes:

  • deriving-with-repr-packed.rs has numerous changes to the code to
    broaden coverage. Some of the errors are reported twice now,
    which is hard to avoid but harmless (and users won't see duplicates
    thanks to the normal error de-duplication). Also, if a packed struct
    is both non-Copy and has type params, both those errors are now
    reported.
  • deriving-all-codegen.rs has the PackedNonCopy case removed, because
    it's no longer allowed.
  • packed.rs needs Copy added to some packed struct that currently only
    derive Default.

r? @ghost

…est.

It's an interesting case, requiring the use of `&&` in `Debug::fmt`.
Because the generatedd code is different to a `Copy` packed struct.
Because the generated code is different to fieldless enum with multiple
variants.
It's always `ast::Mutability::Not`.
E.g. improving code like this:
```
match &*self {
    &Enum1::Single { x: ref __self_0 } => {
        ::core::hash::Hash::hash(&*__self_0, state)
    }
}
```
to this:
```
match self {
    Enum1::Single { x: __self_0 } => {
        ::core::hash::Hash::hash(&*__self_0, state)
    }
}
```
by removing the `&*`, the `&`, and the `ref`.

I suspect the current generated code predates deref-coercion.

The commit also gets rid of `use_temporaries`, instead passing around
`always_copy`, which makes things a little clearer. And it fixes up some
comments.
By producing `&T` expressions for fields instead of `T`. This matches
what the existing comments (e.g. on `FieldInfo`) claim is happening, and
it's also what most of the trait-specific code needs.

The exception is `PartialEq`, which needs `T` expressions for lots of
special case error messaging to work. So we now convert the `&T` back to
a `T` for `PartialEq`.
Use `tag` in names of things referring to tags, instead of the
mysterious `vi`.

Also change `arg_N` in output to `argN`, which has the same length as
`self` and so results in nicer vertical alignments.
To avoid computing a bunch of stuff that it doesn't need.
Currently, for the enums and comparison traits we always check the tag
for equality before doing anything else. This is a bit clumsy. This
commit changes things so that the tags are handled very much like a
zeroth field in the enum.

For `eq`/ne` this makes the code slightly cleaner.

For `partial_cmp` and `cmp` it's a more notable change: in the case
where the tags aren't equal, instead of having a tag equality check
followed by a tag comparison, it just does a single tag comparison.

The commit also improves how `Hash` works for enums: instead of having
duplicated code to hash the tag for every arm within the match, we do
it just once before the match.

All this required replacing the `EnumNonMatchingCollapsed` value with a
new `EnumTag` value.

For fieldless enums the new code is particularly improved. All the code
now produced is close to optimal, being very similar to what you'd write
by hand.
Because it's more concise than the `let` form.
Currently there are two errors you can get when using `derive` on a
`repr(packed)` struct:

- "`#[derive]` can't be used on a `#[repr(packed)]` struct with type
  or const parameters (error E0133)"
- "`#[derive]` can't be used on a `#[repr(packed)]` struct that does not
  derive Copy (error E0133)"

These were introduced under rust-lang#82523, because they can lead to unaligned
references, which is UB. They are in the process of being upgraded to
hard errors.

This is all fine, but the second error overstates things. It *is*
possible to use `derive` on a `repr(packed)` struct that doesn't derive
`Copy` in two cases.
- If all the fields within the struct meet the required alignment: 1 for
  `repr(packed)`, or N for `repr(packed(N))`.
- Or, If `Default` is the only trait derived.

These exceptions don't seem particularly important or useful, and the
language would be simpler if they are removed. So this commit does that,
making these cases a hard error. The new checks are done at derive code
creation time which means that `unsafe_derive_on_repr_packed` is no
longer needed at MIR build time.

Test changes:
- deriving-with-repr-packed.rs has numerous changes to the code to
  broaden coverage. Some of the errors are reported twice now,
  which is hard to avoid but harmless (and users won't see duplicates
  thanks to the normal error de-duplication). Also, if a packed struct
  is both non-`Copy` and has type params, both those errors are now
  reported.
- deriving-all-codegen.rs has the `PackedNonCopy` case removed, because
  it's no longer allowed.
- packed.rs needs `Copy` added to some packed struct that currently only
  derive `Default`.
@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jul 13, 2022
@rustbot
Copy link
Collaborator

rustbot commented Jul 13, 2022

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@nnethercote
Copy link
Contributor Author

All commits prior to the last commit can be ignored, because they come from #99046.

It's unclear if this change will be acceptable, so I will do a crater run.

@bors try

cc @RalfJung

@bors
Copy link
Contributor

bors commented Jul 13, 2022

⌛ Trying commit 9213a54 with merge 53184dc07942a1944769a9e15fc2cb08c9f62e9d...

@nnethercote
Copy link
Contributor Author

BTW, this kind of falls under #82523.

@bors
Copy link
Contributor

bors commented Jul 13, 2022

☀️ Try build successful - checks-actions
Build commit: 53184dc07942a1944769a9e15fc2cb08c9f62e9d (53184dc07942a1944769a9e15fc2cb08c9f62e9d)

@nnethercote
Copy link
Contributor Author

@craterbot check

@craterbot
Copy link
Collaborator

👌 Experiment pr-99197 created and queued.
🤖 Automatically detected try build 53184dc07942a1944769a9e15fc2cb08c9f62e9d
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added the S-waiting-on-crater Status: Waiting on a crater run to be completed. label Jul 13, 2022
@craterbot
Copy link
Collaborator

🚧 Experiment pr-99197 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-99197 is completed!
📊 99 regressed and 2 fixed (239149 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the blacklist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Jul 20, 2022
@nnethercote
Copy link
Contributor Author

Hmm, 99 regressing crates in the crater run, which seems too high to do this as-is. Default and Debug are the most commonly derives in the failing cases, but we see most of the builtin traits showing up in at least one place.

So, possible course of action:

  • Turn this into a lint, and go through the whole deprecation process.
  • Clarify the error message, e.g. change "#[derive] can't be used on a #[repr(packed)] struct that does not derive Copy" into "#[derive(Debug)] can't be used on a #[repr(packed)] struct with this alignment that does not derive Copy".
  • Do nothing.

@RalfJung
Copy link
Member

If we accept the same code when hand-written, I don't quite see the advantage of rejecting it for automatic derive.

@nnethercote
Copy link
Contributor Author

Yeah, I agree. The advantage of the blanket ban is simplicity. But that would ban some legitimate things that are currently accepted. In particular, derive(Default) shows up a lot in the crater regressions.

So I think I will improve the error message a little but leave everything else alone.

@nnethercote
Copy link
Contributor Author

The follow-up is #99581.

@RalfJung
Copy link
Member

Maybe something like this can be salvaged if we find a way to support some of the cases that started to error here... see #82523 (comment).

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. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants