Skip to content

PhantomData<T> no longer dropck? #70841

Description

@matklad

Consider this code:

use std::marker::PhantomData;

struct Pending<T> {
    phantom: PhantomData<T>,
}

fn pending<T>(value: T) -> Pending<T> {
    Pending {
        phantom: PhantomData,
    }
}

struct Inspector<'a> {
    value: &'a String,
}

impl Drop for Inspector<'_> {
    fn drop(&mut self) {
        eprintln!("drop inspector {}", self.value)
    }
}

fn main() {
    let p: Pending<Inspector>;
    let s = "hello".to_string();
    p = pending(Inspector { value: &s });
}

Playground

I believe it should not compile (by failing dropcheck). It, however, compiles and runs on 1.42 and 1.31. On 1.24 it indeed fails as I expect.

The equivalent code, where PhantomData<T> is replaced with T rightfully fails to compiles: Playground. So, dropchk somehow observes the difference between T and PhantomData<T>?

Either I misunderstand how PhantomData<T> is supposed to work, or this is a stable-to-stable regression and a soundless hole.

Activity

  1. jonas-schievink commented on Apr 6, 2020

    @jonas-schievink
    Contributor

    It, however, compiles and runs on 1.42 and 1.31.

    It fails to compile with the 2015 edition on 1.31, so it's probably caused by NLL.

    I suspect the issue is something like: PhantomData never "needs drop", so your struct also doesn't, so no Drop terminator is created that could access the borrowed value.

  2. added
    A-NLLArea: Non-lexical lifetimes (NLL)
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    on Apr 6, 2020
  3. jonas-schievink commented on Apr 6, 2020

    @jonas-schievink
    Contributor

    Adding an unrelated field that has a destructor makes the error appear again

  4. tesuji commented on Apr 7, 2020

    @tesuji
    Contributor

    Regression from 1.36: https://rust.godbolt.org/z/hnEi6A

  5. jonas-schievink commented on Apr 7, 2020

    @jonas-schievink
    Contributor
  6. spastorino commented on Apr 8, 2020

    @spastorino
    Member

    Assigning P-high to this bug and leaving the nomination, this could have been P-critical too. This was discussed as part of our pre-triage meeting in Zulip.

  7. self-assigned this
    on Apr 9, 2020
  8. 21 remaining items

  9. RalfJung commented on May 1, 2020

    @RalfJung
    Member

    @danielhenrymantilla Yeah, something like that is what I was imagining... though your code still has #[may_dangle] drop on the same type RemoteDrop that also has the PhantomData. I think I was imagining putting the function pointer and drop impl down into another type, so that #[may_dangle] and PhantomData are on two different types.

    I am not sure why RawOption would be needed?

  10. TOETOE55 commented on Nov 23, 2022

    @TOETOE55

    @RalfJung I'm still confused about:

    This is unsound because of the lack of PhantomData, no matter whether the drop impl is may_dangle or not.

    From my understanding of dropck, considering MyBox<T> impl no #[may_dangle] Drop:

    If it construct

    • without PhantomData<T>, the dropck would be:

      dropck(MyBox<T>, 'scope) := T: 'scope (strictly)

    • with PhantomData<T>:

      dropck(MyBox<T>, 'scope) := T: 'scope & dropck(T, 'scope) where dropck(PhantomData<T>, 'scope) := dropck(T, 'scope)

    If you want to construct a ub from the former, it is equivalent to finding a T that satisfies T: 'scope but not dropck(T, 'scope), and causes ub when it destructs.

    But I tried to construct such a T and failed. I can't even find a counterexample for T:' scope - > dropck(T, 'scope).

    And I'm not sure how this violates dropck:

    I still have an idea for a counterexample but it's getting tricky: a type not having T in its parameters at all (some kind oft type erasure going on) would have to still drop a T. The T would be remembered by an outer wrapper type that however does not have a destructor.

  11. RalfJung commented on Nov 23, 2022

    @RalfJung
    Member
  12. RalfJung commented on Nov 23, 2022

    @RalfJung
    Member

    Re-reading the thread above, it seems to be about the same fundamental question as #102810. Curious.

    My PR fixes the documentation paragraph that @pnkfelix quoted above

    Adding a field of type PhantomData indicates that your type owns data of type T. This in turn implies that when your type is dropped, it may drop one or more instances of the type T. This has bearing on the Rust compiler's drop check analysis.

    They also wrote

    I do worry a little bit about the user's mental model for this case, however. The claim "PhantomData is just like a T" doesn't quite hold up. (not that it ever did ...)

    which is still true and would require pretty fundamental changes to NLL at this point.

  13. TOETOE55 commented on Nov 23, 2022

    @TOETOE55

    I know PhatomData only works on needs_drop types dropping.

    I'm just curious how the following code causes ub, and how it violates dropck rules:

    For example:

    struct MyBox<T>(NonNull<T>);
    // Mirror `Box` API, including `Drop`.

    This is unsound because of the lack of PhantomData, no matter whether the drop impl is may_dangle or not.

    #70841 (comment)

  14. RalfJung commented on Nov 23, 2022

    @RalfJung
    Member

    Ah, you mean here. A link would have been helpful. :)
    Note that I go on saying

    Hm... okay things don't work entirely as I thought they would.

    #103413 clarifies the misunderstanding me and many others had -- PhantomData is never needed for Drop unless you use may_dangle.

  15. TOETOE55 commented on Nov 23, 2022

    @TOETOE55

    Oh, I will note that :D.

    PhantomData is never needed for Drop unless you use may_dangle

    But as the example in #103413 (comment), PhantomData still works for "normal" dropping.(without #[may_dangle])

  16. RalfJung commented on Nov 23, 2022

    @RalfJung
    Member

    Yeah I read that thread after this one.^^ The summary is "it's complicated, hopefully we'll figure it out in #103413". Let's not re-post everything in two spots. :)

  17. added a commit that references this issue on May 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-NLLArea: Non-lexical lifetimes (NLL)A-docsArea: Documentation for any part of the project, including the compiler, standard library, and toolsC-bugCategory: This is a bug.P-mediumMedium priorityT-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