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

Multi-variant layouts for generators #59897

Merged
merged 13 commits into from
May 4, 2019
Merged
13 changes: 6 additions & 7 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,10 @@ impl<'tcx> Place<'tcx> {
variant_index))
}

pub fn downcast_unnamed(self, variant_index: VariantIdx) -> Place<'tcx> {
self.elem(ProjectionElem::Downcast(None, variant_index))
}

pub fn index(self, index: Local) -> Place<'tcx> {
self.elem(ProjectionElem::Index(index))
}
Expand Down Expand Up @@ -2553,11 +2557,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
let var_name = tcx.hir().name_by_hir_id(freevar.var_id());
struct_fmt.field(&var_name.as_str(), place);
}
struct_fmt.field("$state", &places[freevars.len()]);
for i in (freevars.len() + 1)..places.len() {
struct_fmt
.field(&format!("${}", i - freevars.len() - 1), &places[i]);
}
});

struct_fmt.finish()
Expand Down Expand Up @@ -2998,7 +2997,7 @@ pub struct UnsafetyCheckResult {
/// The layout of generator state
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
pub struct GeneratorLayout<'tcx> {
pub fields: Vec<LocalDecl<'tcx>>,
pub variant_fields: IndexVec<VariantIdx, Vec<LocalDecl<'tcx>>>,
Copy link
Member

Choose a reason for hiding this comment

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

I think the inner Vec could be IndexVec<Field.
Also, surprised this is LocalDecl, is that only for debuginfo?
If so, can you add something similar to __upvar_debuginfo_codegen_only_do_not_use, to ensure there is no cross-talk between debuginfo and non-debuginfo usecases?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, no, we also use it to get the type of the fields in GeneratorSubsts::state_tys(). I suppose I can turn it into a struct with something like __upvar_debuginfo_codegen_only_do_not_use and whatever struct we should be using here. (What should we be using? Right now we only need the type, I think.)

Copy link
Member

Choose a reason for hiding this comment

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

I meant that GeneratorLayout should only have Ty<'tcx>, and that debuginfo should be separate.

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 separated these out and made a GeneratorField newtype. That way our layout code will also know when the same field is in two variants so it can lay them out correctly (see the FIXME I added).

}

#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
Expand Down Expand Up @@ -3187,7 +3186,7 @@ BraceStructTypeFoldableImpl! {

BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
fields
variant_fields
}
}

Expand Down
12 changes: 7 additions & 5 deletions src/librustc/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,13 @@ impl<'tcx> Rvalue<'tcx> {
}
Rvalue::Discriminant(ref place) => {
let ty = place.ty(local_decls, tcx).ty;
if let ty::Adt(adt_def, _) = ty.sty {
adt_def.repr.discr_type().to_ty(tcx)
} else {
// This can only be `0`, for now, so `u8` will suffice.
tcx.types.u8
match ty.sty {
ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx),
ty::Generator(_, substs, _) => substs.discr_ty(tcx),
_ => {
// This can only be `0`, for now, so `u8` will suffice.
tcx.types.u8
}
}
}
Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t),
Expand Down
81 changes: 71 additions & 10 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,12 +604,57 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
tcx.intern_layout(unit)
}

// Tuples, generators and closures.
ty::Generator(def_id, ref substs, _) => {
let tys = substs.field_tys(def_id, tcx);
univariant(&tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
let discr_index = substs.prefix_tys(def_id, tcx).count();
let prefix_tys = substs.prefix_tys(def_id, tcx)
.chain(iter::once(substs.discr_ty(tcx)));
let prefix = univariant_uninterned(
&prefix_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::AlwaysSized)?
StructKind::AlwaysSized)?;

let mut size = prefix.size;
let mut align = prefix.align;
let variants_tys = substs.state_tys(def_id, tcx);
let variants = variants_tys.enumerate().map(|(i, variant_tys)| {
let mut variant = univariant_uninterned(
&variant_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::Prefixed(prefix.size, prefix.align.abi))?;

variant.variants = Variants::Single { index: VariantIdx::new(i) };

size = size.max(variant.size);
align = align.max(variant.align);

Ok(variant)
}).collect::<Result<IndexVec<VariantIdx, _>, _>>()?;

let abi = if prefix.abi.is_uninhabited() ||
variants.iter().all(|v| v.abi.is_uninhabited()) {
Abi::Uninhabited
} else {
Abi::Aggregate { sized: true }
};
let discr = match &self.layout_of(substs.discr_ty(tcx))?.abi {
Abi::Scalar(s) => s.clone(),
_ => bug!(),
};

let layout = tcx.intern_layout(LayoutDetails {
variants: Variants::Multiple {
discr,
discr_kind: DiscriminantKind::Tag,
discr_index,
variants,
},
fields: prefix.fields,
abi,
size,
align,
});
debug!("generator layout: {:#?}", layout);
layout
}

ty::Closure(def_id, ref substs) => {
Expand Down Expand Up @@ -1646,6 +1691,14 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>

fn field(this: TyLayout<'tcx>, cx: &C, i: usize) -> C::TyLayout {
let tcx = cx.tcx();
let handle_discriminant = |discr: &Scalar| -> C::TyLayout {
Copy link
Member

@eddyb eddyb Apr 30, 2019

Choose a reason for hiding this comment

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

I'd call this discr_layout (which we should maybe pre-intern, this could be unnecessarily expensive).

Copy link
Member Author

Choose a reason for hiding this comment

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

Renamed. Pre-interning feels out of scope, since we are handling all fields more or less the same here?

let layout = LayoutDetails::scalar(cx, discr.clone());
MaybeResult::from_ok(TyLayout {
details: tcx.intern_layout(layout),
ty: discr.value.to_ty(tcx)
})
};

cx.layout_of(match this.ty.sty {
ty::Bool |
ty::Char |
Expand Down Expand Up @@ -1720,7 +1773,19 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
}

ty::Generator(def_id, ref substs, _) => {
substs.field_tys(def_id, tcx).nth(i).unwrap()
match this.variants {
Variants::Single { index } => {
substs.state_tys(def_id, tcx)
.nth(index.as_usize()).unwrap()
.nth(i).unwrap()
}
Variants::Multiple { ref discr, discr_index, .. } => {
if i == discr_index {
return handle_discriminant(discr);
}
substs.prefix_tys(def_id, tcx).nth(i).unwrap()
}
}
}

ty::Tuple(tys) => tys[i],
Expand All @@ -1740,11 +1805,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
// Discriminant field for enums (where applicable).
Variants::Multiple { ref discr, .. } => {
assert_eq!(i, 0);
let layout = LayoutDetails::scalar(cx, discr.clone());
return MaybeResult::from_ok(TyLayout {
details: tcx.intern_layout(layout),
ty: discr.value.to_ty(tcx)
});
return handle_discriminant(discr);
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use std::ops::Deref;
use rustc_data_structures::sync::{self, Lrc, ParallelIterator, par_iter};
use std::slice;
use std::{mem, ptr};
use std::ops::Range;
use syntax::ast::{self, Name, Ident, NodeId};
use syntax::attr;
use syntax::ext::hygiene::Mark;
Expand Down Expand Up @@ -2418,11 +2419,17 @@ impl<'a, 'gcx, 'tcx> AdtDef {
})
}

#[inline]
pub fn variant_range(&self) -> Range<VariantIdx> {
(VariantIdx::new(0)..VariantIdx::new(self.variants.len()))
}

/// Computes the discriminant value used by a specific variant.
/// Unlike `discriminants`, this is (amortized) constant-time,
/// only doing at most one query for evaluating an explicit
/// discriminant (the last one before the requested variant),
/// assuming there are no constant-evaluation errors there.
#[inline]
pub fn discriminant_for_variant(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: VariantIdx)
Expand Down
144 changes: 114 additions & 30 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ use polonius_engine::Atom;
use rustc_data_structures::indexed_vec::Idx;
use rustc_macros::HashStable;
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, Kind, UnpackedKind};
use crate::ty::{self, AdtDef, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv};
use crate::ty::layout::VariantIdx;
use crate::util::captures::Captures;
use crate::mir::interpret::{Scalar, Pointer};

use smallvec::SmallVec;
use std::iter;
use std::cmp::Ordering;
use std::marker::PhantomData;
use std::ops::Range;
use rustc_target::spec::abi;
use syntax::ast::{self, Ident};
use syntax::symbol::{keywords, InternedString};
Expand Down Expand Up @@ -298,14 +299,10 @@ static_assert!(MEM_SIZE_OF_TY_KIND: ::std::mem::size_of::<TyKind<'_>>() == 24);
///
/// ## Generators
///
/// Perhaps surprisingly, `ClosureSubsts` are also used for
/// generators. In that case, what is written above is only half-true
/// -- the set of type parameters is similar, but the role of CK and
/// CS are different. CK represents the "yield type" and CS
/// represents the "return type" of the generator.
///
/// It'd be nice to split this struct into ClosureSubsts and
/// GeneratorSubsts, I believe. -nmatsakis
/// Generators are handled similarly in `GeneratorSubsts`. The set of
/// type parameters is similar, but the role of CK and CS are
/// different. CK represents the "yield type" and CS represents the
/// "return type" of the generator.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
Debug, RustcEncodable, RustcDecodable, HashStable)]
pub struct ClosureSubsts<'tcx> {
Expand Down Expand Up @@ -391,6 +388,7 @@ impl<'tcx> ClosureSubsts<'tcx> {
}
}

/// Similar to `ClosureSubsts`; see the above documentation for more.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug,
RustcEncodable, RustcDecodable, HashStable)]
pub struct GeneratorSubsts<'tcx> {
Expand Down Expand Up @@ -470,33 +468,91 @@ impl<'tcx> GeneratorSubsts<'tcx> {
}

impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> {
/// Generator have not been resumed yet
pub const UNRESUMED: usize = 0;
/// Generator has returned / is completed
pub const RETURNED: usize = 1;
/// Generator has been poisoned
pub const POISONED: usize = 2;

const UNRESUMED_NAME: &'static str = "Unresumed";
const RETURNED_NAME: &'static str = "Returned";
const POISONED_NAME: &'static str = "Panicked";

/// The valid variant indices of this Generator.
#[inline]
pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Range<VariantIdx> {
// FIXME requires optimized MIR
let num_variants = self.state_tys(def_id, tcx).count();
(VariantIdx::new(0)..VariantIdx::new(num_variants))
}

/// The discriminant for the given variant. Panics if the variant_index is
/// out of range.
#[inline]
pub fn discriminant_for_variant(
&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: VariantIdx
) -> Discr<'tcx> {
// Generators don't support explicit discriminant values, so they are
// the same as the variant index.
assert!(self.variant_range(def_id, tcx).contains(&variant_index));
Discr { val: variant_index.as_usize() as u128, ty: self.discr_ty(tcx) }
}

/// The set of all discriminants for the Generator, enumerated with their
/// variant indices.
#[inline]
pub fn discriminants(
&'a self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>
) -> impl Iterator<Item=(VariantIdx, Discr<'tcx>)> + Captures<'gcx> + 'a {
self.variant_range(def_id, tcx).map(move |index| {
(index, Discr { val: index.as_usize() as u128, ty: self.discr_ty(tcx) })
})
}

/// Calls `f` with a reference to the name of the enumerator for the given
/// variant `v`.
#[inline]
pub fn map_variant_name<R>(&self, v: VariantIdx, f: impl FnOnce(&str) -> R) -> R {
tmandry marked this conversation as resolved.
Show resolved Hide resolved
let name = match v.as_usize() {
Self::UNRESUMED => Self::UNRESUMED_NAME,
Self::RETURNED => Self::RETURNED_NAME,
Self::POISONED => Self::POISONED_NAME,
_ => {
return f(&format!("variant#{}", v.as_usize()));
tmandry marked this conversation as resolved.
Show resolved Hide resolved
}
};
f(name)
}

/// The type of the state discriminant used in the generator type.
#[inline]
pub fn discr_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
tcx.types.u32
}

/// This returns the types of the MIR locals which had to be stored across suspension points.
/// It is calculated in rustc_mir::transform::generator::StateTransform.
/// All the types here must be in the tuple in GeneratorInterior.
pub fn state_tys(
self,
def_id: DefId,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
) -> impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a {
let state = tcx.generator_layout(def_id).fields.iter();
state.map(move |d| d.ty.subst(tcx, self.substs))
}

/// This is the types of the fields of a generate which
/// is available before the generator transformation.
/// It includes the upvars and the state discriminant which is u32.
pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
///
/// The locals are grouped by their variant number. Note that some locals may
/// be repeated in multiple variants.
#[inline]
pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a>
{
self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
tcx.generator_layout(def_id)
.variant_fields.iter()
.map(move |v| v.iter().map(move |d| d.ty.subst(tcx, self.substs)))
}

/// This is the types of all the fields stored in a generator.
/// It includes the upvars, state types and the state discriminant which is u32.
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a
/// This is the types of the fields of a generator which are not stored in a
/// variant.
#[inline]
pub fn prefix_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
{
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
self.upvar_tys(def_id, tcx)
}
}

Expand Down Expand Up @@ -1994,6 +2050,34 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}

/// If the type contains variants, returns the valid range of variant indices.
/// FIXME This requires the optimized MIR in the case of generators.
#[inline]
pub fn variant_range(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Range<VariantIdx>> {
match self.sty {
TyKind::Adt(adt, _) => Some(adt.variant_range()),
TyKind::Generator(def_id, substs, _) => Some(substs.variant_range(def_id, tcx)),
_ => None,
}
}

/// If the type contains variants, returns the variant for `variant_index`.
/// Panics if `variant_index` is out of range.
/// FIXME This requires the optimized MIR in the case of generators.
#[inline]
pub fn discriminant_for_variant(
&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: VariantIdx
) -> Option<Discr<'tcx>> {
match self.sty {
TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)),
TyKind::Generator(def_id, substs, _) =>
Some(substs.discriminant_for_variant(def_id, tcx, variant_index)),
_ => None,
}
}

/// Push onto `out` the regions directly referenced from this type (but not
/// types reachable from this type via `walk_tys`). This ignores late-bound
/// regions binders.
Expand Down
Loading