Skip to content

Tracking issue for RFC 2514, "Union initialization and Drop" #55149

Description

@Centril

This is a tracking issue for the RFC "Union initialization and Drop" (rust-lang/rfcs#2514).

Successor of #32836.

Steps:

Unresolved questions:

  • There should be more tests in particular for all move-related behaviors (1, 2, 3, 4). Done in unions: test move behavior of non-Copy fields #75559.

  • Should we try to avoid the DerefMut-related pitfall? And if yes, should we maybe try harder, e.g. lint against using * below a union type when describing a place? That would make people write let v = &mut u.f; *v = Vec::new();. It is not clear that this helps in terms of pointing out that an automatic drop may be happening. (Implementation at do not apply DerefMut on union field #75584.)

  • We could allow moving out of a union field even if the union implements Drop. That would have the effect of making the union considered uninitialized, i.e., it would not be dropped implicitly when it goes out of scope. However, it might be useful to not let people do this accidentally. The same effect can always be achieved by having a dropless union wrapped in a newtype struct with the desired Drop.

  • Should we allow impl Copy for Union even when the union has non-Copy fields? (Proposed here.)

Implementation history

Activity

  1. added
    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
    T-langRelevant to the language team
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on Oct 17, 2018
  2. RalfJung commented on Oct 17, 2018

    @RalfJung
  3. SimonSapin commented on Oct 17, 2018

    @SimonSapin
    Contributor

    Is there anything else tracked in #32836 that is not covered here? Does implementing this RFC mean fully stabilizing the untagged_unions feature?

  4. Centril commented on Oct 17, 2018

    @Centril
    ContributorAuthor

    @SimonSapin I just make the issues, so I'll leave the question up to @RalfJung :)

  5. RalfJung commented on Oct 17, 2018

    @RalfJung
    Member

    Stabilization is separate, right? It has its own checkmark above. :) Implementing the RFC means restricting unions with non-Copy types to the point where I think they can be stabilized, yes.

    I am not fully aware of everything that exists behind the untagged_union feature gate though... if e.g. there is such a thing as union patterns, they are not affected by this directly.

    Quoting from the other tracking issue:

    When moving out of one field of a union, are the others considered invalidated?

    This is answered by this RFC.

    Under what conditions can you implement Copy for a union? For example, what if some variants are of non-Copy type? All variants?

    The RFC doesn't say anything about that, but I think indeed the answer should be "all variants".

    What interaction is there between unions and enum layout optimizations?

    This is topic of a future unsafe code guidelines discussion, and unrelated to this RFC.

  6. Aaron1011 commented on Oct 29, 2018

    @Aaron1011
  7. Gankra commented on Nov 1, 2018

    @Gankra
  8. Centril commented on Nov 1, 2018

    @Centril
    Author
  9. eddyb commented on Nov 3, 2018

    @eddyb
    Contributor

    This is the part that needs to be changed:

    let param_env = self.tcx.param_env(def_id);
    if !param_env.can_type_implement_copy(self.tcx, ty).is_ok() {
    emit_feature_err(&self.tcx.sess.parse_sess,
    "untagged_unions", item.span, GateIssue::Language,
    "unions with non-`Copy` fields are unstable");
    }

    It should be possible to change it to ty.needs_drop(self.tcx, param_env).

    (note that this will remain valid only as long as nobody changes the implementation of needs_drop to return false for unions without checking! but if we add tests we should be fine)

    Turns out... I was wrong from the start, needs_drop always returns false for unions.
    Which means we need this:

    adt_def.non_enum_variant().fields.iter().any(|field| {
        self.tcx.type_of(field.did).needs_drop(self.tcx, param_env)
    })

    Also, this shouldn't be nested in the else for the has_dtor check.

  10. RalfJung commented on Nov 3, 2018

    @RalfJung
    Member

    Behavior if the check failed should then also change from "require feature flag" to "hard error".

  11. added
    E-help-wantedCall for participation: Help is requested to fix this issue.
    E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
    on Jun 9, 2019
  12. RalfJung commented on Aug 18, 2019

    @RalfJung
  13. 40 remaining items

  14. RalfJung commented on Jun 11, 2022

    @RalfJung
    Member

    FWIW, when the untagged_unions feature is removed, the AssignToDroppingUnionField unsafety kind can also be removed -- it is impossible to trigger without that feature.

  15. RalfJung commented on Jun 11, 2022

    @RalfJung
    Member

    In #97995 I am suggesting to allow a few more types for union fields on stable (specifically: mutable references), before we entirely remove the untagged_unions feature.

  16. added
    to-announceAnnounce this issue on triage meeting
    and removed
    final-comment-periodIn the final comment period and will be merged soon unless new substantive objections are raised.
    on Jun 18, 2022
  17. rfcbot commented on Jun 18, 2022

    @rfcbot

    The final comment period, with a disposition to close, as per the review above, is now complete.

    As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

  18. added 2 commits that reference this issue on Jul 12, 2022
  19. added a commit that references this issue on Jul 13, 2022
  20. raphaelcohn commented on Aug 9, 2022

    @raphaelcohn

    @joshtriplett @RalfJung I think removing this has exposed an overlooked use case that should work, but now doesn't.

    struct StackWithoutLength<T, const N: usize>(ManuallyDrop<MaybeUninit<[T; N]>>);
    
    union StackWithoutLengthOrHeap<T, const N: usize>
    {
    	stack_without_length: StackWithoutLength<T, N>,
    
             copyable: u32
    }
    

    This now fails to compile with error[E0740]: unions cannot contain fields that may need dropping (details below), when it seems like it should; trivial analysis suggests that StackWithoutLength` is a new type that can only be manually dropped...

    error[E0740]: unions cannot contain fields that may need dropping
     --> swiss-army-knife/src/const_small_vec/StackWithoutLengthOrHeap.rs:7:2
      |
    7 |     stack_without_length: StackWithoutLength<T, N>,
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |
      = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
    help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
      |
    7 |     stack_without_length: std::mem::ManuallyDrop<StackWithoutLength<T, N>>,
      |                           +++++++++++++++++++++++                        +
    
    
  21. RalfJung commented on Aug 9, 2022

    @RalfJung
    Member

    Never inspecting the fields of a struct for this check was a deliberate design decision, since if that struct was in a different crate, it might start needing drop in future versions of the crate.

    In this case, it seems like the struct and union are in the same crate? Possibly an exception could be made for that case, though it would be an extension of the RFC. Please open a feature request issue.

  22. raphaelcohn commented on Aug 9, 2022

    @raphaelcohn

    @RalfJung thanks for the quick reply. The struct and union are in the same crate and both are private too it, as well.

    If versions are correctly used, then a future version of the struct should then cause failure.

    I won't be opening a feature request.

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

    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCF-untagged_unions`#![feature(untagged_unions)]`T-langRelevant to the language teamdisposition-closeThis PR / issue is in PFCP or FCP with a disposition to close it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions