Skip to content

Commit

Permalink
Wrap promoted generator fields in MaybeUninit
Browse files Browse the repository at this point in the history
This prevents uninhabited fields from "infecting" the abi and
largest_niche of the generator layout.

This fixes a latent bug, where an uninhabited field could be promoted to
the generator prefix and cause the entire generator to become
uninhabited.
  • Loading branch information
tmandry committed Jul 29, 2019
1 parent c43753f commit 6fae7f8
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/libcore/mem/maybe_uninit.rs
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")]
#[derive(Copy)]
#[repr(transparent)]
pub union MaybeUninit<T> {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/lang_items.rs
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
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
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;

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

Expand Down
15 changes: 15 additions & 0 deletions src/test/ui/async-await/issues/issue-59972.rs
@@ -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();
}

0 comments on commit 6fae7f8

Please sign in to comment.