Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use MaybeUninit to produce more correct layouts #63035

Merged
merged 2 commits into from
Aug 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libcore/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ use crate::mem::ManuallyDrop;
/// guarantee may evolve.
#[allow(missing_debug_implementations)]
#[stable(feature = "maybe_uninit", since = "1.36.0")]
// Lang item so we can wrap other types in it. This is useful for generators.
#[cfg_attr(not(bootstrap), lang = "maybe_uninit")]
tmandry marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Copy)]
#[repr(transparent)]
pub union MaybeUninit<T> {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ language_item_table! {

ManuallyDropItem, "manually_drop", manually_drop, Target::Struct;

MaybeUninitLangItem, "maybe_uninit", maybe_uninit, Target::Union;

DebugTraitLangItem, "debug_trait", debug_trait, Target::Trait;

// Align offset for stride != 1, must not panic.
Expand Down
21 changes: 16 additions & 5 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2347,18 +2347,17 @@ impl<'tcx> TyCtxt<'tcx> {
self.mk_ty(Foreign(def_id))
}

pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem);
let adt_def = self.adt_def(def_id);
let substs = InternalSubsts::for_item(self, def_id, |param, substs| {
fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
let adt_def = self.adt_def(wrapper_def_id);
let substs = InternalSubsts::for_item(self, wrapper_def_id, |param, substs| {
match param.kind {
GenericParamDefKind::Lifetime |
GenericParamDefKind::Const => {
bug!()
}
GenericParamDefKind::Type { has_default, .. } => {
if param.index == 0 {
ty.into()
ty_param.into()
} else {
assert!(has_default);
self.type_of(param.def_id).subst(self, substs).into()
Expand All @@ -2369,6 +2368,18 @@ impl<'tcx> TyCtxt<'tcx> {
self.mk_ty(Adt(adt_def, substs))
}

#[inline]
pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem);
self.mk_generic_adt(def_id, ty)
}

#[inline]
pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> {
let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem);
self.mk_generic_adt(def_id, ty)
}

#[inline]
pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
self.mk_ty(RawPtr(tm))
Expand Down
7 changes: 2 additions & 5 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,24 +1406,21 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
Abi::Scalar(s) => s.clone(),
_ => bug!(),
};
// FIXME(eddyb) wrap each promoted type in `MaybeUninit` so that they
// don't poison the `largest_niche` or `abi` fields of `prefix`.
let promoted_layouts = ineligible_locals.iter()
.map(|local| subst_field(info.field_tys[local]))
.map(|ty| tcx.mk_maybe_uninit(ty))
.map(|ty| self.layout_of(ty));
let prefix_layouts = substs.prefix_tys(def_id, tcx)
.map(|ty| self.layout_of(ty))
.chain(iter::once(Ok(discr_layout)))
.chain(promoted_layouts)
.collect::<Result<Vec<_>, _>>()?;
let mut prefix = self.univariant_uninterned(
let prefix = self.univariant_uninterned(
ty,
&prefix_layouts,
&ReprOptions::default(),
StructKind::AlwaysSized,
)?;
// FIXME(eddyb) need `MaybeUninit` around promoted types (see above).
prefix.largest_niche = None;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add a test for some generator that captures a bool or something, that it's the same size when wrapped in Option.

In the future, the state/discriminant field would have the right validity range and would be used itself as the niche in many situations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm adding a test, but I'm afraid that if we move the upvars into a variant (#62958 (comment)) this test won't work anymore. =/


let (prefix_size, prefix_align) = (prefix.size, prefix.align);

Expand Down
17 changes: 17 additions & 0 deletions src/test/run-pass/generator/niche-in-generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Test that niche finding works with captured generator upvars.

#![feature(generators)]

use std::mem::size_of_val;

fn take<T>(_: T) {}

fn main() {
let x = false;
let gen1 = || {
yield;
take(x);
};

assert_eq!(size_of_val(&gen1), size_of_val(&Some(gen1)));
}
15 changes: 15 additions & 0 deletions src/test/ui/async-await/issues/issue-59972.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Incorrect handling of uninhabited types could cause us to mark generator
// types as entirely uninhabited, when they were in fact constructible. This
// caused us to hit "unreachable" code (illegal instruction on x86).

// run-pass

// compile-flags: --edition=2018
Expand All @@ -19,7 +23,18 @@ async fn contains_never() {
let error2 = error;
}

#[allow(unused)]
async fn overlap_never() {
let error1 = uninhabited_async();
noop().await;
let error2 = uninhabited_async();
drop(error1);
noop().await;
drop(error2);
}

#[allow(unused_must_use)]
fn main() {
contains_never();
overlap_never();
}