rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.54k stars 12.47k forks source link

Trait bounds not properly propagated in `#[derive(SmartPointer)]` #127647

Closed Kixunil closed 1 month ago

Kixunil commented 1 month ago

When deriving SmartPointer on a struct with additional bounds the resulting impl is missing a bound resulting in a compile error. This behavior was not explicitly mentioned in SmartPointer RFC, so I assume it's a bug. See #123430

I tried this code: (playground)

#![feature(derive_smart_pointer)]

#[derive(core::marker::SmartPointer)]
#[repr(transparent)]
// Remove the OnDrop bound to make it compile but then the functionality is lost
pub struct Ptr<'a, #[pointee] T: OnDrop + ?Sized, X> {
    data: &'a mut T,
    x: core::marker::PhantomData<X>,
}

pub trait OnDrop {
    fn on_drop(&mut self);
}

I expected to see this happen: the code compiles and provides a smart pointer that executes on_drop when the smart pointer is dropped.

Instead, this happened: compile error:

error[E0277]: the trait bound `__S: OnDrop` is not satisfied
 --> src/lib.rs:3:10
  |
3 | #[derive(core::marker::SmartPointer)]
  |          ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `OnDrop` is not implemented for `__S`
  |
note: required by a bound in `Ptr`
 --> src/lib.rs:6:34
  |
6 | pub struct Ptr<'a, #[pointee] T: OnDrop + ?Sized, X> {
  |                                  ^^^^^^ required by this bound in `Ptr`
  = note: this error originates in the derive macro `core::marker::SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (lib) due to 1 previous error

Meta

rustc --version --verbose:

1.81.0-nightly
2024-07-11 5315cbe15b79533f380b
tgross35 commented 1 month ago

Note this is a pretty incomplete feature so there may be some things that just haven't gotten updated yet, but it is good to have the bug report.

@rustbot label +F-derive_smart_pointer +T-compiler -needs-triage

Darksonn commented 1 month ago

Thank you for catching this. It looks like we will need this to be fixed before we can use the macro for its intended purpose with linked lists, as the relevant smart pointer is defined with a trait bound:

#[repr(transparent)]
pub struct ListArc<T, const ID: u64 = 0>
where
    T: ListArcSafe<ID> + ?Sized,
{
    arc: Arc<T>,
}

It would make sense to add a test that ensures that the relevant parts of the linked list example compiles. (link 1, link 2).

cc @dingxiangfei2009

Kixunil commented 1 month ago

Oh, I've just noticed that when you expand the macro the bounds are not applied to the __S parameter which needs them too because it's used as an argument into Ptr generics. It looks like #125575 doesn't copy the bounds (but I have no compiler knowledge so this is just an educated guess). Edit: I've linked it below.

dingxiangfei2009 commented 1 month ago

cc @Darksonn I have one open question. There could be more convoluted bounds that involves the pointee type like the following.

#[derive(SmartPointer)]
#[repr(transparent)]
pub struct Ptr<#[pointee] T>
where
    T: Trait<T>,
{ .. }

pub trait Trait<U> { .. }

For now, #127681 does not rewrite T into __S. Should the macro apply this substitution? I feel that the answer is probably "depends".

Darksonn commented 1 month ago

This is what I have in the RFC:

For every trait bound declared on the trait, add it twice to the trait implementation. Once exactly as written, and once with every instance of the #[pointee] parameter replaced with U.

Does that work?

dingxiangfei2009 commented 1 month ago

Okay #127681 is updated, so that given a #[pointee] T generic parameter a U: Trait<T> bound is commuted to a U: Trait<__S> bound at both the generic and where-clause position, regardless of whether U is bound or free.

So now

Tests are added for these cases.