Skip to content

Uninhabited types are ZSTs despite partial initialization being allowed in safe code. #49298

Closed
@eddyb

Description

@eddyb
Member

When we optimized product types with uninhabited fields to be ZST, partial initialization was overlooked, i.e. writes to the inhabited sibling fields are allowed despite missing in the layout.
Thankfully, it's impossible to read or borrow such fields, because the whole value must be initialized (although this may be relaxed in the future). However , the initialized fields are still dropped.

One way we could work around this is track uninhabitedness but only take advantage of it in enums.
(I am not aware of a solution that works with variant types, which I think we should keep in mind.)
EDIT: we can disallow creating the enum from an incomplete variant. This is also required by niche optimizations because the niche slot being uninitialized would result in UB.
EDIT2: incomplete variants might be possible today already (#49298 (comment)).

As an example, in debug mode, this snippet prints 0, because x.1 and y overlap:

enum Void {}

fn main() {
    let mut x: (Void, usize);
    let y = 1;
    x.1 = 0;
    println!("{}", y)
}

cc @nikomatsakis @nagisa

Activity

added
A-codegenArea: Code generation
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Mar 23, 2018
LunaBorowska

LunaBorowska commented on Mar 23, 2018

@LunaBorowska
Contributor

Seems I-unsound.

enum Void {}

fn main() {
    let mut x: (Void, [u8; 20]);
    x.1 = [0x12; 20];
}
added
I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
on Mar 23, 2018
LunaBorowska

LunaBorowska commented on Mar 23, 2018

@LunaBorowska
Contributor

Also, this regression was introduced in Rust 1.24.0 from what I see, this correctly works in 1.23.0.

ExpHP

ExpHP commented on Mar 24, 2018

@ExpHP
Contributor

To pile on the scary labels, that also makes this a regression from stable to stable!

eddyb

eddyb commented on Mar 28, 2018

@eddyb
MemberAuthor

#48493 looks like an instance of this - cc @alexcrichton

hanna-kruppe

hanna-kruppe commented on Mar 28, 2018

@hanna-kruppe
Contributor

@eddyb Weak creates a value of the uninhabited type via mem::uninitialized, it does not perform partial initalization. It's UB anyway. This issue (edit: resolving this issue) would at best make the code working-but-still-UB.

bstrie

bstrie commented on Mar 28, 2018

@bstrie
Contributor

@rkruppe is there a separate bug for that?

hanna-kruppe

hanna-kruppe commented on Mar 28, 2018

@hanna-kruppe
Contributor

@eddyb Uh, I guess #48493 is that bug? Although if we land a workaround before the proper fix (MaybeUninitialized with unsizing support) becomes available, we should make sure we open a new bug for tracking removal of the workaround and introduction of the proper fix.

eddyb

eddyb commented on Apr 11, 2018

@eddyb
MemberAuthor

There's potentially an even more problematic implication here: initializing an uninhabited (because of its fields) variant and panicking while doing so could also result in writing over data following the whole enum, assuming the data is larger than the enum itself, e.g.:

let x: Option<(String, !)>;
let y = vec!["foo".to_string()];
x = Some((String::new("bar"), panic!("{:?}", y)));

This might be hard to demonstrate today, because of some extra temporaries, but optimizations would likely prefer to write to x directly, which would spell disaster.
I'm not sure what a proper solution would be here, we certainly need to discuss it.

nikomatsakis

nikomatsakis commented on Apr 12, 2018

@nikomatsakis
Contributor

We discussed in the meeting. It seems like the rules want to be:

  • Ignore uninhabitedness when laying out product types (! is treated like any ZST)
  • Ignore variants for enums if they are both uninhabited and ZST

This ensures there is space for non-ZST data... we need to do this if we want to be able to optimize temporaries away pre-monomorphization.

31 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-codegenArea: Code generationC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-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.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @cuviper@eddyb@nikomatsakis@vi@bstrie

      Issue actions

        Uninhabited types are ZSTs despite partial initialization being allowed in safe code. · Issue #49298 · rust-lang/rust