Skip to content

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

Closed

Description

@Kixunil

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

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 12, 2024
  2. tgross35 commented on Jul 12, 2024

    @tgross35
    Member

    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

  3. added
    F-derive_coerce_pointeeFeature: RFC 3621's oft-renamed implementation
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 12, 2024
  4. Darksonn commented on Jul 12, 2024

    @Darksonn
    Member

    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

  5. Kixunil commented on Jul 13, 2024

    @Kixunil
    ContributorAuthor

    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.

  6. dingxiangfei2009 commented on Jul 13, 2024

    @dingxiangfei2009
    Contributor

    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".

  7. Darksonn commented on Jul 13, 2024

    @Darksonn
    Member

    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?

  8. dingxiangfei2009 commented on Jul 13, 2024

    @dingxiangfei2009
    Contributor

    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

    • struct<#[pointee] T, U: Trait<T>>... generates an impl<T, __S: Unsize<T> + ?Sized, U: Trait<T> + Trait<__S>>
    • struct<#[pointee] T: Trait<T>>... generates an impl<T: Trait<T>, __S: Unsize<T> + ?Sized + Trait<__S>>
    • struct<#[pointee] T: Trait<T>> where SomeType<T>: Trait<T> + ... generates an impl<T: Trait<T>, __S: Unsize<T> + ?Sized> where SomeType<T>: Trait<T> + ..., SomeType<__S>: Trait<__S> + ...

    Tests are added for these cases.

  9. added a commit that references this issue on Jul 31, 2024
    a952d28
  10. added 2 commits that reference this issue on Jul 31, 2024
    e447650
    563f938
  11. added a commit that references this issue on Jul 31, 2024
    5d78d7d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.F-derive_coerce_pointeeFeature: RFC 3621's oft-renamed implementationT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions