Auto merge of #59897 - tmandry:variantful-generators, r=eddyb
Multi-variant layouts for generators This allows generators to overlap fields using variants, but doesn't do any such overlapping yet. It creates one variant for every state of the generator (unresumed, returned, panicked, plus one for every yield), and puts every stored local in each of the yield-point variants. Required for optimizing generator layouts (#52924). There was quite a lot of refactoring needed for this change. I've done my best in later commits to eliminate assumptions in the code that only certain kinds of types are multi-variant, and to centralize knowledge of the inner mechanics of generators in as few places as possible. This change also emits debuginfo about the fields contained in each variant, as well as preserving debuginfo about stored locals while running in the generator. Also, fixes #59972. Future work: - Use this change for an optimization pass that actually overlaps locals within the generator struct (#52924) - In the type layout fields, don't include locals that are uninitialized for a particular variant, so miri and UB sanitizers can check our memory (see https://github.com/rust-lang/rust/issues/59972#issuecomment-483058172) - Preserve debuginfo scopes across generator yield points
This commit is contained in:
commit
e232636693
22 changed files with 804 additions and 331 deletions
|
@ -2030,6 +2030,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))
|
||||
}
|
||||
|
@ -2589,11 +2593,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()
|
||||
|
@ -3031,10 +3030,29 @@ pub struct UnsafetyCheckResult {
|
|||
pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
|
||||
}
|
||||
|
||||
newtype_index! {
|
||||
pub struct GeneratorSavedLocal {
|
||||
derive [HashStable]
|
||||
DEBUG_FORMAT = "_{}",
|
||||
}
|
||||
}
|
||||
|
||||
/// The layout of generator state
|
||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
|
||||
pub struct GeneratorLayout<'tcx> {
|
||||
pub fields: Vec<LocalDecl<'tcx>>,
|
||||
/// The type of every local stored inside the generator.
|
||||
pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>,
|
||||
|
||||
/// Which of the above fields are in each variant. Note that one field may
|
||||
/// be stored in multiple variants.
|
||||
pub variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>>,
|
||||
|
||||
/// Names and scopes of all the stored generator locals.
|
||||
/// NOTE(tmandry) This is *strictly* a temporary hack for codegen
|
||||
/// debuginfo generation, and will be removed at some point.
|
||||
/// Do **NOT** use it for anything else, local information should not be
|
||||
/// in the MIR, please rely on local crate HIR or other side-channels.
|
||||
pub __local_debuginfo_codegen_only_do_not_use: IndexVec<GeneratorSavedLocal, LocalDecl<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
|
||||
|
@ -3223,7 +3241,9 @@ BraceStructTypeFoldableImpl! {
|
|||
|
||||
BraceStructTypeFoldableImpl! {
|
||||
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
|
||||
fields
|
||||
field_tys,
|
||||
variant_fields,
|
||||
__local_debuginfo_codegen_only_do_not_use,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3598,6 +3618,15 @@ impl<'tcx> TypeFoldable<'tcx> for Field {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
|
||||
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
|
||||
*self
|
||||
}
|
||||
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
|
||||
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
|
||||
Constant {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -604,12 +604,63 @@ 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<_>, _>>()?,
|
||||
// FIXME(tmandry): For fields that are repeated in multiple
|
||||
// variants in the GeneratorLayout, we need code to ensure that
|
||||
// the offset of these fields never change. Right now this is
|
||||
// not an issue since every variant has every field, but once we
|
||||
// optimize this we have to be more careful.
|
||||
|
||||
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) => {
|
||||
|
@ -1647,6 +1698,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 discr_layout = |discr: &Scalar| -> C::TyLayout {
|
||||
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 |
|
||||
|
@ -1721,7 +1780,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 discr_layout(discr);
|
||||
}
|
||||
substs.prefix_tys(def_id, tcx).nth(i).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => tys[i].expect_ty(),
|
||||
|
@ -1741,11 +1812,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 discr_layout(discr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,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;
|
||||
|
@ -2416,11 +2417,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)
|
||||
|
|
|
@ -9,15 +9,17 @@ 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::borrow::Cow;
|
||||
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};
|
||||
|
@ -298,14 +300,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> {
|
||||
|
@ -391,6 +389,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> {
|
||||
|
@ -470,33 +469,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 = tcx.generator_layout(def_id).variant_fields.len();
|
||||
(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 variant_name(&self, v: VariantIdx) -> Cow<'static, str> {
|
||||
match v.as_usize() {
|
||||
Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME),
|
||||
Self::RETURNED => Cow::from(Self::RETURNED_NAME),
|
||||
Self::POISONED => Cow::from(Self::POISONED_NAME),
|
||||
_ => Cow::from(format!("Suspend{}", v.as_usize() - 3))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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))
|
||||
///
|
||||
/// 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>
|
||||
{
|
||||
let layout = tcx.generator_layout(def_id);
|
||||
layout.variant_fields.iter().map(move |variant| {
|
||||
variant.iter().map(move |field| {
|
||||
layout.field_tys[*field].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>) ->
|
||||
/// 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.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
|
||||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
|
||||
self.upvar_tys(def_id, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1996,6 +2053,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.
|
||||
|
|
|
@ -23,12 +23,13 @@ use rustc::hir::def::CtorKind;
|
|||
use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
|
||||
use rustc::ich::NodeIdHashingMode;
|
||||
use rustc::mir::Field;
|
||||
use rustc::mir::GeneratorLayout;
|
||||
use rustc::mir::interpret::truncate;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc::ty::Instance;
|
||||
use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
|
||||
use rustc::ty::layout::{self, Align, Integer, IntegerExt, LayoutOf,
|
||||
PrimitiveExt, Size, TyLayout};
|
||||
PrimitiveExt, Size, TyLayout, VariantIdx};
|
||||
use rustc::ty::subst::UnpackedKind;
|
||||
use rustc::session::config;
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
|
@ -691,14 +692,15 @@ pub fn type_metadata(
|
|||
usage_site_span).finalize(cx)
|
||||
}
|
||||
ty::Generator(def_id, substs, _) => {
|
||||
let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| {
|
||||
let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| {
|
||||
cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)
|
||||
}).collect();
|
||||
prepare_tuple_metadata(cx,
|
||||
t,
|
||||
&upvar_tys,
|
||||
unique_type_id,
|
||||
usage_site_span).finalize(cx)
|
||||
prepare_enum_metadata(cx,
|
||||
t,
|
||||
def_id,
|
||||
unique_type_id,
|
||||
usage_site_span,
|
||||
upvar_tys).finalize(cx)
|
||||
}
|
||||
ty::Adt(def, ..) => match def.adt_kind() {
|
||||
AdtKind::Struct => {
|
||||
|
@ -718,7 +720,8 @@ pub fn type_metadata(
|
|||
t,
|
||||
def.did,
|
||||
unique_type_id,
|
||||
usage_site_span).finalize(cx)
|
||||
usage_site_span,
|
||||
vec![]).finalize(cx)
|
||||
}
|
||||
},
|
||||
ty::Tuple(ref elements) => {
|
||||
|
@ -1000,6 +1003,31 @@ struct MemberDescription<'ll> {
|
|||
discriminant: Option<u64>,
|
||||
}
|
||||
|
||||
impl<'ll> MemberDescription<'ll> {
|
||||
fn into_metadata(self,
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
composite_type_metadata: &'ll DIScope) -> &'ll DIType {
|
||||
let member_name = CString::new(self.name).unwrap();
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateVariantMemberType(
|
||||
DIB(cx),
|
||||
composite_type_metadata,
|
||||
member_name.as_ptr(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
self.size.bits(),
|
||||
self.align.bits() as u32,
|
||||
self.offset.bits(),
|
||||
match self.discriminant {
|
||||
None => None,
|
||||
Some(value) => Some(cx.const_u64(value)),
|
||||
},
|
||||
self.flags,
|
||||
self.type_metadata)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A factory for MemberDescriptions. It produces a list of member descriptions
|
||||
// for some record-like type. MemberDescriptionFactories are used to defer the
|
||||
// creation of type member descriptions in order to break cycles arising from
|
||||
|
@ -1266,7 +1294,16 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
||||
fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>)
|
||||
-> Vec<MemberDescription<'ll>> {
|
||||
let adt = &self.enum_type.ty_adt_def().unwrap();
|
||||
let variant_info_for = |index: VariantIdx| {
|
||||
match &self.enum_type.sty {
|
||||
ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]),
|
||||
ty::Generator(def_id, substs, _) => {
|
||||
let generator_layout = cx.tcx.generator_layout(*def_id);
|
||||
VariantInfo::Generator(*substs, generator_layout, index)
|
||||
}
|
||||
_ => bug!(),
|
||||
}
|
||||
};
|
||||
|
||||
// This will always find the metadata in the type map.
|
||||
let fallback = use_enum_fallback(cx);
|
||||
|
@ -1277,12 +1314,18 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
};
|
||||
|
||||
match self.layout.variants {
|
||||
layout::Variants::Single { .. } if adt.variants.is_empty() => vec![],
|
||||
layout::Variants::Single { index } => {
|
||||
if let ty::Adt(adt, _) = &self.enum_type.sty {
|
||||
if adt.variants.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
|
||||
let variant_info = variant_info_for(index);
|
||||
let (variant_type_metadata, member_description_factory) =
|
||||
describe_enum_variant(cx,
|
||||
self.layout,
|
||||
&adt.variants[index],
|
||||
variant_info,
|
||||
NoDiscriminant,
|
||||
self_metadata,
|
||||
self.span);
|
||||
|
@ -1299,7 +1342,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
name: if fallback {
|
||||
String::new()
|
||||
} else {
|
||||
adt.variants[index].ident.as_str().to_string()
|
||||
variant_info.variant_name()
|
||||
},
|
||||
type_metadata: variant_type_metadata,
|
||||
offset: Size::ZERO,
|
||||
|
@ -1327,10 +1370,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
};
|
||||
variants.iter_enumerated().map(|(i, _)| {
|
||||
let variant = self.layout.for_variant(cx, i);
|
||||
let variant_info = variant_info_for(i);
|
||||
let (variant_type_metadata, member_desc_factory) =
|
||||
describe_enum_variant(cx,
|
||||
variant,
|
||||
&adt.variants[i],
|
||||
variant_info,
|
||||
discriminant_info,
|
||||
self_metadata,
|
||||
self.span);
|
||||
|
@ -1342,20 +1386,21 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
self.enum_type,
|
||||
variant_type_metadata,
|
||||
member_descriptions);
|
||||
|
||||
MemberDescription {
|
||||
name: if fallback {
|
||||
String::new()
|
||||
} else {
|
||||
adt.variants[i].ident.as_str().to_string()
|
||||
variant_info.variant_name()
|
||||
},
|
||||
type_metadata: variant_type_metadata,
|
||||
offset: Size::ZERO,
|
||||
size: self.layout.size,
|
||||
align: self.layout.align.abi,
|
||||
flags: DIFlags::FlagZero,
|
||||
discriminant: Some(self.layout.ty.ty_adt_def().unwrap()
|
||||
.discriminant_for_variant(cx.tcx, i)
|
||||
.val as u64),
|
||||
discriminant: Some(
|
||||
self.layout.ty.discriminant_for_variant(cx.tcx, i).unwrap().val as u64
|
||||
),
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
@ -1375,7 +1420,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
let (variant_type_metadata, member_description_factory) =
|
||||
describe_enum_variant(cx,
|
||||
variant,
|
||||
&adt.variants[dataful_variant],
|
||||
variant_info_for(dataful_variant),
|
||||
OptimizedDiscriminant,
|
||||
self.containing_scope,
|
||||
self.span);
|
||||
|
@ -1415,7 +1460,9 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
self.layout,
|
||||
self.layout.fields.offset(discr_index),
|
||||
self.layout.field(cx, discr_index).size);
|
||||
name.push_str(&adt.variants[*niche_variants.start()].ident.as_str());
|
||||
variant_info_for(*niche_variants.start()).map_struct_name(|variant_name| {
|
||||
name.push_str(variant_name);
|
||||
});
|
||||
|
||||
// Create the (singleton) list of descriptions of union members.
|
||||
vec![
|
||||
|
@ -1432,10 +1479,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
} else {
|
||||
variants.iter_enumerated().map(|(i, _)| {
|
||||
let variant = self.layout.for_variant(cx, i);
|
||||
let variant_info = variant_info_for(i);
|
||||
let (variant_type_metadata, member_desc_factory) =
|
||||
describe_enum_variant(cx,
|
||||
variant,
|
||||
&adt.variants[i],
|
||||
variant_info,
|
||||
OptimizedDiscriminant,
|
||||
self_metadata,
|
||||
self.span);
|
||||
|
@ -1463,7 +1511,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
|
|||
};
|
||||
|
||||
MemberDescription {
|
||||
name: adt.variants[i].ident.as_str().to_string(),
|
||||
name: variant_info.variant_name(),
|
||||
type_metadata: variant_type_metadata,
|
||||
offset: Size::ZERO,
|
||||
size: self.layout.size,
|
||||
|
@ -1521,6 +1569,49 @@ enum EnumDiscriminantInfo<'ll> {
|
|||
NoDiscriminant
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum VariantInfo<'tcx> {
|
||||
Adt(&'tcx ty::VariantDef),
|
||||
Generator(ty::GeneratorSubsts<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx),
|
||||
}
|
||||
|
||||
impl<'tcx> VariantInfo<'tcx> {
|
||||
fn map_struct_name<R>(&self, f: impl FnOnce(&str) -> R) -> R {
|
||||
match self {
|
||||
VariantInfo::Adt(variant) => f(&variant.ident.as_str()),
|
||||
VariantInfo::Generator(substs, _, variant_index) =>
|
||||
f(&substs.variant_name(*variant_index)),
|
||||
}
|
||||
}
|
||||
|
||||
fn variant_name(&self) -> String {
|
||||
match self {
|
||||
VariantInfo::Adt(variant) => variant.ident.to_string(),
|
||||
VariantInfo::Generator(_, _, variant_index) => {
|
||||
// Since GDB currently prints out the raw discriminant along
|
||||
// with every variant, make each variant name be just the value
|
||||
// of the discriminant. The struct name for the variant includes
|
||||
// the actual variant description.
|
||||
format!("{}", variant_index.as_usize()).to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn field_name(&self, i: usize) -> String {
|
||||
let field_name = match self {
|
||||
VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn =>
|
||||
Some(variant.fields[i].ident.to_string()),
|
||||
VariantInfo::Generator(_, generator_layout, variant_index) => {
|
||||
let field = generator_layout.variant_fields[*variant_index][i.into()];
|
||||
let decl = &generator_layout.__local_debuginfo_codegen_only_do_not_use[field];
|
||||
decl.name.map(|name| name.to_string())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
field_name.unwrap_or_else(|| format!("__{}", i))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a tuple of (1) type_metadata_stub of the variant, (2) a
|
||||
// MemberDescriptionFactory for producing the descriptions of the
|
||||
// fields of the variant. This is a rudimentary version of a full
|
||||
|
@ -1528,32 +1619,24 @@ enum EnumDiscriminantInfo<'ll> {
|
|||
fn describe_enum_variant(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
layout: layout::TyLayout<'tcx>,
|
||||
variant: &'tcx ty::VariantDef,
|
||||
variant: VariantInfo<'tcx>,
|
||||
discriminant_info: EnumDiscriminantInfo<'ll>,
|
||||
containing_scope: &'ll DIScope,
|
||||
span: Span,
|
||||
) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) {
|
||||
let variant_name = variant.ident.as_str();
|
||||
let unique_type_id = debug_context(cx).type_map
|
||||
.borrow_mut()
|
||||
.get_unique_type_id_of_enum_variant(
|
||||
cx,
|
||||
layout.ty,
|
||||
&variant_name);
|
||||
|
||||
let metadata_stub = create_struct_stub(cx,
|
||||
layout.ty,
|
||||
&variant_name,
|
||||
unique_type_id,
|
||||
Some(containing_scope));
|
||||
|
||||
let arg_name = |i: usize| {
|
||||
if variant.ctor_kind == CtorKind::Fn {
|
||||
format!("__{}", i)
|
||||
} else {
|
||||
variant.fields[i].ident.to_string()
|
||||
}
|
||||
};
|
||||
let metadata_stub = variant.map_struct_name(|variant_name| {
|
||||
let unique_type_id = debug_context(cx).type_map
|
||||
.borrow_mut()
|
||||
.get_unique_type_id_of_enum_variant(
|
||||
cx,
|
||||
layout.ty,
|
||||
&variant_name);
|
||||
create_struct_stub(cx,
|
||||
layout.ty,
|
||||
&variant_name,
|
||||
unique_type_id,
|
||||
Some(containing_scope))
|
||||
});
|
||||
|
||||
// Build an array of (field name, field type) pairs to be captured in the factory closure.
|
||||
let (offsets, args) = if use_enum_fallback(cx) {
|
||||
|
@ -1575,7 +1658,7 @@ fn describe_enum_variant(
|
|||
layout.fields.offset(i)
|
||||
})).collect(),
|
||||
discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| {
|
||||
(arg_name(i), layout.field(cx, i).ty)
|
||||
(variant.field_name(i), layout.field(cx, i).ty)
|
||||
})).collect()
|
||||
)
|
||||
} else {
|
||||
|
@ -1584,7 +1667,7 @@ fn describe_enum_variant(
|
|||
layout.fields.offset(i)
|
||||
}).collect(),
|
||||
(0..layout.fields.count()).map(|i| {
|
||||
(arg_name(i), layout.field(cx, i).ty)
|
||||
(variant.field_name(i), layout.field(cx, i).ty)
|
||||
}).collect()
|
||||
)
|
||||
};
|
||||
|
@ -1611,6 +1694,7 @@ fn prepare_enum_metadata(
|
|||
enum_def_id: DefId,
|
||||
unique_type_id: UniqueTypeId,
|
||||
span: Span,
|
||||
outer_field_tys: Vec<Ty<'tcx>>,
|
||||
) -> RecursiveTypeDescription<'ll, 'tcx> {
|
||||
let enum_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
|
||||
|
||||
|
@ -1624,20 +1708,36 @@ fn prepare_enum_metadata(
|
|||
let file_metadata = unknown_file_metadata(cx);
|
||||
|
||||
let discriminant_type_metadata = |discr: layout::Primitive| {
|
||||
let def = enum_type.ty_adt_def().unwrap();
|
||||
let enumerators_metadata: Vec<_> = def.discriminants(cx.tcx)
|
||||
.zip(&def.variants)
|
||||
.map(|((_, discr), v)| {
|
||||
let name = SmallCStr::new(&v.ident.as_str());
|
||||
unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
|
||||
DIB(cx),
|
||||
name.as_ptr(),
|
||||
// FIXME: what if enumeration has i128 discriminant?
|
||||
discr.val as u64))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let enumerators_metadata: Vec<_> = match enum_type.sty {
|
||||
ty::Adt(def, _) => def
|
||||
.discriminants(cx.tcx)
|
||||
.zip(&def.variants)
|
||||
.map(|((_, discr), v)| {
|
||||
let name = SmallCStr::new(&v.ident.as_str());
|
||||
unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
|
||||
DIB(cx),
|
||||
name.as_ptr(),
|
||||
// FIXME: what if enumeration has i128 discriminant?
|
||||
discr.val as u64))
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
ty::Generator(_, substs, _) => substs
|
||||
.variant_range(enum_def_id, cx.tcx)
|
||||
.map(|variant_index| {
|
||||
let name = SmallCStr::new(&substs.variant_name(variant_index));
|
||||
unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
|
||||
DIB(cx),
|
||||
name.as_ptr(),
|
||||
// FIXME: what if enumeration has i128 discriminant?
|
||||
variant_index.as_usize() as u64))
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
_ => bug!(),
|
||||
};
|
||||
|
||||
let disr_type_key = (enum_def_id, discr);
|
||||
let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types
|
||||
|
@ -1650,14 +1750,18 @@ fn prepare_enum_metadata(
|
|||
(discr.size(cx), discr.align(cx));
|
||||
let discriminant_base_type_metadata =
|
||||
type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP);
|
||||
let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str();
|
||||
|
||||
let name = SmallCStr::new(&discriminant_name);
|
||||
let discriminant_name = match enum_type.sty {
|
||||
ty::Adt(..) => SmallCStr::new(&cx.tcx.item_name(enum_def_id).as_str()),
|
||||
ty::Generator(..) => SmallCStr::new(&enum_name),
|
||||
_ => bug!(),
|
||||
};
|
||||
|
||||
let discriminant_type_metadata = unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateEnumerationType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
name.as_ptr(),
|
||||
discriminant_name.as_ptr(),
|
||||
file_metadata,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
discriminant_size.bits(),
|
||||
|
@ -1738,6 +1842,11 @@ fn prepare_enum_metadata(
|
|||
);
|
||||
}
|
||||
|
||||
let discriminator_name = match &enum_type.sty {
|
||||
ty::Generator(..) => Some(SmallCStr::new(&"__state")),
|
||||
_ => None,
|
||||
};
|
||||
let discriminator_name = discriminator_name.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
let discriminator_metadata = match layout.variants {
|
||||
// A single-variant enum has no discriminant.
|
||||
layout::Variants::Single { .. } => None,
|
||||
|
@ -1764,7 +1873,7 @@ fn prepare_enum_metadata(
|
|||
Some(llvm::LLVMRustDIBuilderCreateMemberType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
ptr::null_mut(),
|
||||
discriminator_name,
|
||||
file_metadata,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
size.bits(),
|
||||
|
@ -1789,7 +1898,7 @@ fn prepare_enum_metadata(
|
|||
Some(llvm::LLVMRustDIBuilderCreateMemberType(
|
||||
DIB(cx),
|
||||
containing_scope,
|
||||
ptr::null_mut(),
|
||||
discriminator_name,
|
||||
file_metadata,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
size.bits(),
|
||||
|
@ -1801,6 +1910,22 @@ fn prepare_enum_metadata(
|
|||
},
|
||||
};
|
||||
|
||||
let mut outer_fields = match layout.variants {
|
||||
layout::Variants::Single { .. } => vec![],
|
||||
layout::Variants::Multiple { .. } => {
|
||||
let tuple_mdf = TupleMemberDescriptionFactory {
|
||||
ty: enum_type,
|
||||
component_types: outer_field_tys,
|
||||
span
|
||||
};
|
||||
tuple_mdf
|
||||
.create_member_descriptions(cx)
|
||||
.into_iter()
|
||||
.map(|desc| Some(desc.into_metadata(cx, containing_scope)))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let variant_part_unique_type_id_str = SmallCStr::new(
|
||||
debug_context(cx).type_map
|
||||
.borrow_mut()
|
||||
|
@ -1821,9 +1946,10 @@ fn prepare_enum_metadata(
|
|||
empty_array,
|
||||
variant_part_unique_type_id_str.as_ptr())
|
||||
};
|
||||
outer_fields.push(Some(variant_part));
|
||||
|
||||
// The variant part must be wrapped in a struct according to DWARF.
|
||||
let type_array = create_DIArray(DIB(cx), &[Some(variant_part)]);
|
||||
let type_array = create_DIArray(DIB(cx), &outer_fields);
|
||||
let struct_wrapper = unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateStructType(
|
||||
DIB(cx),
|
||||
|
@ -1855,12 +1981,6 @@ fn prepare_enum_metadata(
|
|||
span,
|
||||
}),
|
||||
);
|
||||
|
||||
fn get_enum_discriminant_name(cx: &CodegenCx<'_, '_>,
|
||||
def_id: DefId)
|
||||
-> InternedString {
|
||||
cx.tcx.item_name(def_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates debug information for a composite type, that is, anything that
|
||||
|
@ -1918,26 +2038,7 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, 'tcx>,
|
|||
|
||||
let member_metadata: Vec<_> = member_descriptions
|
||||
.into_iter()
|
||||
.map(|member_description| {
|
||||
let member_name = CString::new(member_description.name).unwrap();
|
||||
unsafe {
|
||||
Some(llvm::LLVMRustDIBuilderCreateVariantMemberType(
|
||||
DIB(cx),
|
||||
composite_type_metadata,
|
||||
member_name.as_ptr(),
|
||||
unknown_file_metadata(cx),
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
member_description.size.bits(),
|
||||
member_description.align.bits() as u32,
|
||||
member_description.offset.bits(),
|
||||
match member_description.discriminant {
|
||||
None => None,
|
||||
Some(value) => Some(cx.const_u64(value)),
|
||||
},
|
||||
member_description.flags,
|
||||
member_description.type_metadata))
|
||||
}
|
||||
})
|
||||
.map(|desc| Some(desc.into_metadata(cx, composite_type_metadata)))
|
||||
.collect();
|
||||
|
||||
let type_params = compute_type_parameters(cx, composite_type);
|
||||
|
|
|
@ -63,6 +63,11 @@ fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
|||
write!(&mut name, "::{}", def.variants[index].ident).unwrap();
|
||||
}
|
||||
}
|
||||
if let (&ty::Generator(_, substs, _), &layout::Variants::Single { index })
|
||||
= (&layout.ty.sty, &layout.variants)
|
||||
{
|
||||
write!(&mut name, "::{}", substs.variant_name(index)).unwrap();
|
||||
}
|
||||
Some(name)
|
||||
}
|
||||
_ => None
|
||||
|
|
|
@ -4,6 +4,7 @@ use rustc::mir::{self, Mir};
|
|||
use rustc::session::config::DebugInfo;
|
||||
use rustc_mir::monomorphize::Instance;
|
||||
use rustc_target::abi::call::{FnType, PassMode, IgnoreMode};
|
||||
use rustc_target::abi::{Variants, VariantIdx};
|
||||
use crate::base;
|
||||
use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext};
|
||||
use crate::traits::*;
|
||||
|
@ -648,7 +649,9 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
.iter()
|
||||
.zip(upvar_tys)
|
||||
.enumerate()
|
||||
.map(|(i, (upvar, ty))| (i, upvar.debug_name, upvar.by_ref, ty));
|
||||
.map(|(i, (upvar, ty))| {
|
||||
(None, i, upvar.debug_name, upvar.by_ref, ty, scope, DUMMY_SP)
|
||||
});
|
||||
|
||||
let generator_fields = mir.generator_layout.as_ref().map(|generator_layout| {
|
||||
let (def_id, gen_substs) = match closure_layout.ty.sty {
|
||||
|
@ -657,22 +660,48 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
};
|
||||
let state_tys = gen_substs.state_tys(def_id, tcx);
|
||||
|
||||
let upvar_count = upvar_debuginfo.len();
|
||||
generator_layout.fields
|
||||
.iter()
|
||||
generator_layout.variant_fields.iter()
|
||||
.zip(state_tys)
|
||||
.enumerate()
|
||||
.filter_map(move |(i, (decl, ty))| {
|
||||
let ty = fx.monomorphize(&ty);
|
||||
decl.name.map(|name| (i + upvar_count + 1, name, false, ty))
|
||||
.flat_map(move |(variant_idx, (fields, tys))| {
|
||||
let variant_idx = Some(VariantIdx::from(variant_idx));
|
||||
fields.iter()
|
||||
.zip(tys)
|
||||
.enumerate()
|
||||
.filter_map(move |(i, (field, ty))| {
|
||||
let decl = &generator_layout.
|
||||
__local_debuginfo_codegen_only_do_not_use[*field];
|
||||
if let Some(name) = decl.name {
|
||||
let ty = fx.monomorphize(&ty);
|
||||
let (var_scope, var_span) = fx.debug_loc(mir::SourceInfo {
|
||||
span: decl.source_info.span,
|
||||
scope: decl.visibility_scope,
|
||||
});
|
||||
let var_scope = var_scope.unwrap_or(scope);
|
||||
Some((variant_idx, i, name, false, ty, var_scope, var_span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}).into_iter().flatten();
|
||||
|
||||
upvars.chain(generator_fields)
|
||||
};
|
||||
|
||||
for (field, name, by_ref, ty) in extra_locals {
|
||||
let byte_offset_of_var_in_env = closure_layout.fields.offset(field).bytes();
|
||||
for (variant_idx, field, name, by_ref, ty, var_scope, var_span) in extra_locals {
|
||||
let fields = match variant_idx {
|
||||
Some(variant_idx) => {
|
||||
match &closure_layout.variants {
|
||||
Variants::Multiple { variants, .. } => {
|
||||
&variants[variant_idx].fields
|
||||
},
|
||||
_ => bug!("variant index on univariant layout"),
|
||||
}
|
||||
}
|
||||
None => &closure_layout.fields,
|
||||
};
|
||||
let byte_offset_of_var_in_env = fields.offset(field).bytes();
|
||||
|
||||
let ops = bx.debuginfo_upvar_ops_sequence(byte_offset_of_var_in_env);
|
||||
|
||||
|
@ -694,10 +723,10 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
&fx.debug_context,
|
||||
name,
|
||||
ty,
|
||||
scope,
|
||||
var_scope,
|
||||
variable_access,
|
||||
VariableKind::LocalVariable,
|
||||
DUMMY_SP
|
||||
var_span
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -218,9 +218,8 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
}
|
||||
let (discr_scalar, discr_kind, discr_index) = match self.layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
let discr_val = self.layout.ty.ty_adt_def().map_or(
|
||||
index.as_u32() as u128,
|
||||
|def| def.discriminant_for_variant(bx.cx().tcx(), index).val);
|
||||
let discr_val = self.layout.ty.discriminant_for_variant(bx.cx().tcx(), index)
|
||||
.map_or(index.as_u32() as u128, |discr| discr.val);
|
||||
return bx.cx().const_uint_big(cast_to, discr_val);
|
||||
}
|
||||
layout::Variants::Multiple { ref discr, ref discr_kind, discr_index, .. } => {
|
||||
|
@ -296,9 +295,8 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
..
|
||||
} => {
|
||||
let ptr = self.project_field(bx, discr_index);
|
||||
let to = self.layout.ty.ty_adt_def().unwrap()
|
||||
.discriminant_for_variant(bx.tcx(), variant_index)
|
||||
.val;
|
||||
let to =
|
||||
self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val;
|
||||
bx.store(
|
||||
bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to),
|
||||
ptr.llval,
|
||||
|
|
|
@ -271,13 +271,12 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let ll_t_in = bx.cx().immediate_backend_type(operand.layout);
|
||||
match operand.layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
if let Some(def) = operand.layout.ty.ty_adt_def() {
|
||||
let discr_val = def
|
||||
.discriminant_for_variant(bx.cx().tcx(), index)
|
||||
.val;
|
||||
let discr = bx.cx().const_uint_big(ll_t_out, discr_val);
|
||||
if let Some(discr) =
|
||||
operand.layout.ty.discriminant_for_variant(bx.tcx(), index)
|
||||
{
|
||||
let discr_val = bx.cx().const_uint_big(ll_t_out, discr.val);
|
||||
return (bx, OperandRef {
|
||||
val: OperandValue::Immediate(discr),
|
||||
val: OperandValue::Immediate(discr_val),
|
||||
layout: cast,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -684,6 +684,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// We do not need to handle generators here, because this runs
|
||||
// before the generator transform stage.
|
||||
_ => {
|
||||
let ty = if let Some(name) = maybe_name {
|
||||
span_mirbug_and_err!(
|
||||
|
@ -745,11 +747,26 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
let tcx = self.tcx();
|
||||
|
||||
let (variant, substs) = match base_ty {
|
||||
PlaceTy { ty, variant_index: Some(variant_index) } => {
|
||||
match ty.sty {
|
||||
ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs),
|
||||
_ => bug!("can't have downcast of non-adt type"),
|
||||
PlaceTy { ty, variant_index: Some(variant_index) } => match ty.sty {
|
||||
ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs),
|
||||
ty::Generator(def_id, substs, _) => {
|
||||
let mut variants = substs.state_tys(def_id, tcx);
|
||||
let mut variant = match variants.nth(variant_index.into()) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
bug!("variant_index of generator out of range: {:?}/{:?}",
|
||||
variant_index,
|
||||
substs.state_tys(def_id, tcx).count())
|
||||
}
|
||||
};
|
||||
return match variant.nth(field.index()) {
|
||||
Some(ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: variant.count(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => bug!("can't have downcast of non-adt non-generator type"),
|
||||
}
|
||||
PlaceTy { ty, variant_index: None } => match ty.sty {
|
||||
ty::Adt(adt_def, substs) if !adt_def.is_enum() =>
|
||||
|
@ -763,19 +780,14 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
ty::Generator(def_id, substs, _) => {
|
||||
// Try pre-transform fields first (upvars and current state)
|
||||
if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field.index()) {
|
||||
return Ok(ty);
|
||||
}
|
||||
|
||||
// Then try `field_tys` which contains all the fields, but it
|
||||
// requires the final optimized MIR.
|
||||
return match substs.field_tys(def_id, tcx).nth(field.index()) {
|
||||
// Only prefix fields (upvars and current state) are
|
||||
// accessible without a variant index.
|
||||
return match substs.prefix_tys(def_id, tcx).nth(field.index()) {
|
||||
Some(ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: substs.field_tys(def_id, tcx).count(),
|
||||
field_count: substs.prefix_tys(def_id, tcx).count(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
ty::Tuple(tys) => {
|
||||
return match tys.get(field.index()) {
|
||||
|
@ -1908,18 +1920,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
AggregateKind::Generator(def_id, substs, _) => {
|
||||
// Try pre-transform fields first (upvars and current state)
|
||||
if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field_index) {
|
||||
Ok(ty)
|
||||
} else {
|
||||
// Then try `field_tys` which contains all the fields, but it
|
||||
// requires the final optimized MIR.
|
||||
match substs.field_tys(def_id, tcx).nth(field_index) {
|
||||
Some(ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: substs.field_tys(def_id, tcx).count(),
|
||||
}),
|
||||
}
|
||||
// It doesn't make sense to look at a field beyond the prefix;
|
||||
// these require a variant index, and are not initialized in
|
||||
// aggregate rvalues.
|
||||
match substs.prefix_tys(def_id, tcx).nth(field_index) {
|
||||
Some(ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: substs.prefix_tys(def_id, tcx).count(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
AggregateKind::Array(ty) => Ok(ty),
|
||||
|
|
|
@ -207,7 +207,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
movability,
|
||||
} => {
|
||||
// see (*) above
|
||||
let mut operands: Vec<_> = upvars
|
||||
let operands: Vec<_> = upvars
|
||||
.into_iter()
|
||||
.map(|upvar| {
|
||||
let upvar = this.hir.mirror(upvar);
|
||||
|
@ -248,21 +248,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
}).collect();
|
||||
let result = match substs {
|
||||
UpvarSubsts::Generator(substs) => {
|
||||
// We implicitly set the discriminant to 0. See
|
||||
// librustc_mir/transform/deaggregator.rs for details.
|
||||
let movability = movability.unwrap();
|
||||
// Add the state operand since it follows the upvars in the generator
|
||||
// struct. See librustc_mir/transform/generator.rs for more details.
|
||||
operands.push(Operand::Constant(box Constant {
|
||||
span: expr_span,
|
||||
ty: this.hir.tcx().types.u32,
|
||||
user_ty: None,
|
||||
literal: this.hir.tcx().mk_const(
|
||||
ty::Const::from_bits(
|
||||
this.hir.tcx(),
|
||||
0,
|
||||
ty::ParamEnv::empty().and(this.hir.tcx().types.u32),
|
||||
),
|
||||
),
|
||||
}));
|
||||
box AggregateKind::Generator(closure_id, substs, movability)
|
||||
}
|
||||
UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs),
|
||||
|
|
|
@ -54,14 +54,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
|
|||
} else {
|
||||
match src.layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
if let Some(def) = src.layout.ty.ty_adt_def() {
|
||||
if let Some(discr) =
|
||||
src.layout.ty.discriminant_for_variant(*self.tcx, index)
|
||||
{
|
||||
// Cast from a univariant enum
|
||||
assert!(src.layout.is_zst());
|
||||
let discr_val = def
|
||||
.discriminant_for_variant(*self.tcx, index)
|
||||
.val;
|
||||
return self.write_scalar(
|
||||
Scalar::from_uint(discr_val, dest.layout.size),
|
||||
Scalar::from_uint(discr.val, dest.layout.size),
|
||||
dest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -567,9 +567,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
|
|||
|
||||
let (discr_kind, discr_index) = match rval.layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
let discr_val = rval.layout.ty.ty_adt_def().map_or(
|
||||
let discr_val = rval.layout.ty.discriminant_for_variant(*self.tcx, index).map_or(
|
||||
index.as_u32() as u128,
|
||||
|def| def.discriminant_for_variant(*self.tcx, index).val);
|
||||
|discr| discr.val);
|
||||
return Ok((discr_val, index));
|
||||
}
|
||||
layout::Variants::Multiple { ref discr_kind, discr_index, .. } =>
|
||||
|
@ -604,12 +604,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
|
|||
bits_discr
|
||||
};
|
||||
// Make sure we catch invalid discriminants
|
||||
let index = rval.layout.ty
|
||||
.ty_adt_def()
|
||||
.expect("tagged layout for non adt")
|
||||
.discriminants(self.tcx.tcx)
|
||||
.find(|(_, var)| var.val == real_discr)
|
||||
.ok_or_else(|| InterpError::InvalidDiscriminant(raw_discr.erase_tag()))?;
|
||||
let index = match &rval.layout.ty.sty {
|
||||
ty::Adt(adt, _) => adt
|
||||
.discriminants(self.tcx.tcx)
|
||||
.find(|(_, var)| var.val == real_discr),
|
||||
ty::Generator(def_id, substs, _) => substs
|
||||
.discriminants(*def_id, self.tcx.tcx)
|
||||
.find(|(_, var)| var.val == real_discr),
|
||||
_ => bug!("tagged layout for non-adt non-generator"),
|
||||
}.ok_or_else(|| InterpError::InvalidDiscriminant(raw_discr.erase_tag()))?;
|
||||
(real_discr, index.0)
|
||||
},
|
||||
layout::DiscriminantKind::Niche {
|
||||
|
|
|
@ -984,11 +984,9 @@ where
|
|||
discr_index,
|
||||
..
|
||||
} => {
|
||||
let adt_def = dest.layout.ty.ty_adt_def().unwrap();
|
||||
assert!(variant_index.as_usize() < adt_def.variants.len());
|
||||
let discr_val = adt_def
|
||||
.discriminant_for_variant(*self.tcx, variant_index)
|
||||
.val;
|
||||
assert!(dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index));
|
||||
let discr_val =
|
||||
dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
|
||||
|
||||
// raw discriminants for enums are isize or bigger during
|
||||
// their computation, but the in-memory tag is the smallest possible
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use rustc::ty::TyCtxt;
|
||||
use rustc::mir::*;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::ty::layout::VariantIdx;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use crate::transform::{MirPass, MirSource};
|
||||
|
||||
|
@ -55,6 +56,23 @@ impl MirPass for Deaggregator {
|
|||
}
|
||||
active_field_index
|
||||
}
|
||||
AggregateKind::Generator(..) => {
|
||||
// Right now we only support initializing generators to
|
||||
// variant 0 (Unresumed).
|
||||
let variant_index = VariantIdx::new(0);
|
||||
set_discriminant = Some(Statement {
|
||||
kind: StatementKind::SetDiscriminant {
|
||||
place: lhs.clone(),
|
||||
variant_index,
|
||||
},
|
||||
source_info,
|
||||
});
|
||||
|
||||
// Operands are upvars stored on the base place, so no
|
||||
// downcast is necessary.
|
||||
|
||||
None
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
|
|
|
@ -54,13 +54,14 @@ use rustc::hir::def_id::DefId;
|
|||
use rustc::mir::*;
|
||||
use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor};
|
||||
use rustc::ty::{self, TyCtxt, AdtDef, Ty};
|
||||
use rustc::ty::GeneratorSubsts;
|
||||
use rustc::ty::layout::VariantIdx;
|
||||
use rustc::ty::subst::SubstsRef;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||
use rustc_data_structures::bit_set::BitSet;
|
||||
use std::borrow::Cow;
|
||||
use std::iter::once;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use crate::transform::{MirPass, MirSource};
|
||||
use crate::transform::simplify;
|
||||
|
@ -145,14 +146,14 @@ fn self_arg() -> Local {
|
|||
}
|
||||
|
||||
/// Generator have not been resumed yet
|
||||
const UNRESUMED: u32 = 0;
|
||||
const UNRESUMED: usize = GeneratorSubsts::UNRESUMED;
|
||||
/// Generator has returned / is completed
|
||||
const RETURNED: u32 = 1;
|
||||
const RETURNED: usize = GeneratorSubsts::RETURNED;
|
||||
/// Generator has been poisoned
|
||||
const POISONED: u32 = 2;
|
||||
const POISONED: usize = GeneratorSubsts::POISONED;
|
||||
|
||||
struct SuspensionPoint {
|
||||
state: u32,
|
||||
state: usize,
|
||||
resume: BasicBlock,
|
||||
drop: Option<BasicBlock>,
|
||||
storage_liveness: liveness::LiveVarSet,
|
||||
|
@ -163,12 +164,12 @@ struct TransformVisitor<'a, 'tcx: 'a> {
|
|||
state_adt_ref: &'tcx AdtDef,
|
||||
state_substs: SubstsRef<'tcx>,
|
||||
|
||||
// The index of the generator state in the generator struct
|
||||
state_field: usize,
|
||||
// The type of the discriminant in the generator struct
|
||||
discr_ty: Ty<'tcx>,
|
||||
|
||||
// Mapping from Local to (type of local, generator struct index)
|
||||
// FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`.
|
||||
remap: FxHashMap<Local, (Ty<'tcx>, usize)>,
|
||||
remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
|
||||
|
||||
// A map from a suspension point in a block to the locals which have live storage at that point
|
||||
// FIXME(eddyb) This should use `IndexVec<BasicBlock, Option<_>>`.
|
||||
|
@ -189,8 +190,9 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Create a Place referencing a generator struct field
|
||||
fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
|
||||
let base = Place::Base(PlaceBase::Local(self_arg()));
|
||||
fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
|
||||
let self_place = Place::Base(PlaceBase::Local(self_arg()));
|
||||
let base = self_place.downcast_unnamed(variant_index);
|
||||
let field = Projection {
|
||||
base: base,
|
||||
elem: ProjectionElem::Field(Field::new(idx), ty),
|
||||
|
@ -198,24 +200,28 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
|
|||
Place::Projection(Box::new(field))
|
||||
}
|
||||
|
||||
// Create a statement which changes the generator state
|
||||
fn set_state(&self, state_disc: u32, source_info: SourceInfo) -> Statement<'tcx> {
|
||||
let state = self.make_field(self.state_field, self.tcx.types.u32);
|
||||
let val = Operand::Constant(box Constant {
|
||||
span: source_info.span,
|
||||
ty: self.tcx.types.u32,
|
||||
user_ty: None,
|
||||
literal: self.tcx.mk_const(ty::Const::from_bits(
|
||||
self.tcx,
|
||||
state_disc.into(),
|
||||
ty::ParamEnv::empty().and(self.tcx.types.u32)
|
||||
)),
|
||||
});
|
||||
// Create a statement which changes the discriminant
|
||||
fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> {
|
||||
let self_place = Place::Base(PlaceBase::Local(self_arg()));
|
||||
Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(state, box Rvalue::Use(val)),
|
||||
kind: StatementKind::SetDiscriminant { place: self_place, variant_index: state_disc },
|
||||
}
|
||||
}
|
||||
|
||||
// Create a statement which reads the discriminant into a temporary
|
||||
fn get_discr(&self, mir: &mut Mir<'tcx>) -> (Statement<'tcx>, Place<'tcx>) {
|
||||
let temp_decl = LocalDecl::new_internal(self.tcx.types.isize, mir.span);
|
||||
let temp = Place::Base(PlaceBase::Local(Local::new(mir.local_decls.len())));
|
||||
mir.local_decls.push(temp_decl);
|
||||
|
||||
let self_place = Place::Base(PlaceBase::Local(self_arg()));
|
||||
let assign = Statement {
|
||||
source_info: source_info(mir),
|
||||
kind: StatementKind::Assign(temp.clone(), box Rvalue::Discriminant(self_place)),
|
||||
};
|
||||
(assign, temp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
|
||||
|
@ -232,8 +238,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
|
|||
location: Location) {
|
||||
if let Place::Base(PlaceBase::Local(l)) = *place {
|
||||
// Replace an Local in the remap with a generator struct access
|
||||
if let Some(&(ty, idx)) = self.remap.get(&l) {
|
||||
*place = self.make_field(idx, ty);
|
||||
if let Some(&(ty, variant_index, idx)) = self.remap.get(&l) {
|
||||
*place = self.make_field(variant_index, idx, ty);
|
||||
}
|
||||
} else {
|
||||
self.super_place(place, context, location);
|
||||
|
@ -274,7 +280,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
|
|||
box self.make_state(state_idx, v)),
|
||||
});
|
||||
let state = if let Some(resume) = resume { // Yield
|
||||
let state = 3 + self.suspension_points.len() as u32;
|
||||
let state = 3 + self.suspension_points.len();
|
||||
|
||||
self.suspension_points.push(SuspensionPoint {
|
||||
state,
|
||||
|
@ -283,11 +289,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
|
|||
storage_liveness: self.storage_liveness.get(&block).unwrap().clone(),
|
||||
});
|
||||
|
||||
state
|
||||
VariantIdx::new(state)
|
||||
} else { // Return
|
||||
RETURNED // state for returned
|
||||
VariantIdx::new(RETURNED) // state for returned
|
||||
};
|
||||
data.statements.push(self.set_state(state, source_info));
|
||||
data.statements.push(self.set_discr(state, source_info));
|
||||
data.terminator.as_mut().unwrap().kind = TerminatorKind::Return;
|
||||
}
|
||||
|
||||
|
@ -387,6 +393,7 @@ fn locals_live_across_suspend_points(
|
|||
) -> (
|
||||
liveness::LiveVarSet,
|
||||
FxHashMap<BasicBlock, liveness::LiveVarSet>,
|
||||
BitSet<BasicBlock>,
|
||||
) {
|
||||
let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len());
|
||||
let def_id = source.def_id();
|
||||
|
@ -431,8 +438,12 @@ fn locals_live_across_suspend_points(
|
|||
|
||||
let mut storage_liveness_map = FxHashMap::default();
|
||||
|
||||
let mut suspending_blocks = BitSet::new_empty(mir.basic_blocks().len());
|
||||
|
||||
for (block, data) in mir.basic_blocks().iter_enumerated() {
|
||||
if let TerminatorKind::Yield { .. } = data.terminator().kind {
|
||||
suspending_blocks.insert(block);
|
||||
|
||||
let loc = Location {
|
||||
block: block,
|
||||
statement_index: data.statements.len(),
|
||||
|
@ -484,7 +495,7 @@ fn locals_live_across_suspend_points(
|
|||
// The generator argument is ignored
|
||||
set.remove(self_arg());
|
||||
|
||||
(set, storage_liveness_map)
|
||||
(set, storage_liveness_map, suspending_blocks)
|
||||
}
|
||||
|
||||
fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
|
@ -493,15 +504,14 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
interior: Ty<'tcx>,
|
||||
movable: bool,
|
||||
mir: &mut Mir<'tcx>)
|
||||
-> (FxHashMap<Local, (Ty<'tcx>, usize)>,
|
||||
-> (FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
|
||||
GeneratorLayout<'tcx>,
|
||||
FxHashMap<BasicBlock, liveness::LiveVarSet>)
|
||||
{
|
||||
// Use a liveness analysis to compute locals which are live across a suspension point
|
||||
let (live_locals, storage_liveness) = locals_live_across_suspend_points(tcx,
|
||||
mir,
|
||||
source,
|
||||
movable);
|
||||
let (live_locals, storage_liveness, suspending_blocks) =
|
||||
locals_live_across_suspend_points(tcx, mir, source, movable);
|
||||
|
||||
// Erase regions from the types passed in from typeck so we can compare them with
|
||||
// MIR types
|
||||
let allowed_upvars = tcx.erase_regions(upvars);
|
||||
|
@ -527,7 +537,6 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
}
|
||||
}
|
||||
|
||||
let upvar_len = upvars.len();
|
||||
let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), mir.span);
|
||||
|
||||
// Gather live locals and their indices replacing values in mir.local_decls with a dummy
|
||||
|
@ -537,37 +546,52 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
(local, var)
|
||||
});
|
||||
|
||||
// For now we will access everything via variant #3, leaving empty variants
|
||||
// for the UNRESUMED, RETURNED, and POISONED states.
|
||||
// If there were a yield-less generator without a variant #3, it would not
|
||||
// have any vars to remap, so we would never use this.
|
||||
let variant_index = VariantIdx::new(3);
|
||||
|
||||
// Create a map from local indices to generator struct indices.
|
||||
// These are offset by (upvar_len + 1) because of fields which comes before locals.
|
||||
// We also create a vector of the LocalDecls of these locals.
|
||||
let (remap, vars) = live_decls.enumerate().map(|(idx, (local, var))| {
|
||||
((local, (var.ty, upvar_len + 1 + idx)), var)
|
||||
}).unzip();
|
||||
let mut remap = FxHashMap::default();
|
||||
let mut decls = IndexVec::new();
|
||||
for (idx, (local, var)) in live_decls.enumerate() {
|
||||
remap.insert(local, (var.ty, variant_index, idx));
|
||||
decls.push(var);
|
||||
}
|
||||
let field_tys = decls.iter().map(|field| field.ty).collect::<IndexVec<_, _>>();
|
||||
|
||||
// Put every var in each variant, for now.
|
||||
let all_vars = (0..field_tys.len()).map(GeneratorSavedLocal::from).collect();
|
||||
let empty_variants = iter::repeat(IndexVec::new()).take(3);
|
||||
let state_variants = iter::repeat(all_vars).take(suspending_blocks.count());
|
||||
|
||||
let layout = GeneratorLayout {
|
||||
fields: vars
|
||||
field_tys,
|
||||
variant_fields: empty_variants.chain(state_variants).collect(),
|
||||
__local_debuginfo_codegen_only_do_not_use: decls,
|
||||
};
|
||||
|
||||
(remap, layout, storage_liveness)
|
||||
}
|
||||
|
||||
fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir: &mut Mir<'tcx>,
|
||||
cases: Vec<(u32, BasicBlock)>,
|
||||
fn insert_switch<'a, 'tcx>(mir: &mut Mir<'tcx>,
|
||||
cases: Vec<(usize, BasicBlock)>,
|
||||
transform: &TransformVisitor<'a, 'tcx>,
|
||||
default: TerminatorKind<'tcx>) {
|
||||
let default_block = insert_term_block(mir, default);
|
||||
|
||||
let (assign, discr) = transform.get_discr(mir);
|
||||
let switch = TerminatorKind::SwitchInt {
|
||||
discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)),
|
||||
switch_ty: tcx.types.u32,
|
||||
values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::<Vec<_>>()),
|
||||
targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(),
|
||||
discr: Operand::Move(discr),
|
||||
switch_ty: transform.discr_ty,
|
||||
values: Cow::from(cases.iter().map(|&(i, _)| i as u128).collect::<Vec<_>>()),
|
||||
targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(),
|
||||
};
|
||||
|
||||
let source_info = source_info(mir);
|
||||
mir.basic_blocks_mut().raw.insert(0, BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
statements: vec![assign],
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: switch,
|
||||
|
@ -652,7 +676,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
|
|||
// The returned state and the poisoned state fall through to the default
|
||||
// case which is just to return
|
||||
|
||||
insert_switch(tcx, &mut mir, cases, &transform, TerminatorKind::Return);
|
||||
insert_switch(&mut mir, cases, &transform, TerminatorKind::Return);
|
||||
|
||||
for block in mir.basic_blocks_mut() {
|
||||
let kind = &mut block.terminator_mut().kind;
|
||||
|
@ -766,7 +790,8 @@ fn create_generator_resume_function<'a, 'tcx>(
|
|||
for block in mir.basic_blocks_mut() {
|
||||
let source_info = block.terminator().source_info;
|
||||
if let &TerminatorKind::Resume = &block.terminator().kind {
|
||||
block.statements.push(transform.set_state(POISONED, source_info));
|
||||
block.statements.push(
|
||||
transform.set_discr(VariantIdx::new(POISONED), source_info));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,7 +809,7 @@ fn create_generator_resume_function<'a, 'tcx>(
|
|||
// Panic when resumed on the poisoned state
|
||||
cases.insert(2, (POISONED, insert_panic_block(tcx, mir, GeneratorResumedAfterPanic)));
|
||||
|
||||
insert_switch(tcx, mir, cases, &transform, TerminatorKind::Unreachable);
|
||||
insert_switch(mir, cases, &transform, TerminatorKind::Unreachable);
|
||||
|
||||
make_generator_state_argument_indirect(tcx, def_id, mir);
|
||||
make_generator_state_argument_pinned(tcx, mir);
|
||||
|
@ -830,7 +855,7 @@ fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock {
|
|||
|
||||
fn create_cases<'a, 'tcx, F>(mir: &mut Mir<'tcx>,
|
||||
transform: &TransformVisitor<'a, 'tcx>,
|
||||
target: F) -> Vec<(u32, BasicBlock)>
|
||||
target: F) -> Vec<(usize, BasicBlock)>
|
||||
where F: Fn(&SuspensionPoint) -> Option<BasicBlock> {
|
||||
let source_info = source_info(mir);
|
||||
|
||||
|
@ -888,10 +913,11 @@ impl MirPass for StateTransform {
|
|||
let gen_ty = mir.local_decls.raw[1].ty;
|
||||
|
||||
// Get the interior types and substs which typeck computed
|
||||
let (upvars, interior, movable) = match gen_ty.sty {
|
||||
let (upvars, interior, discr_ty, movable) = match gen_ty.sty {
|
||||
ty::Generator(_, substs, movability) => {
|
||||
(substs.upvar_tys(def_id, tcx).collect(),
|
||||
substs.witness(def_id, tcx),
|
||||
substs.discr_ty(tcx),
|
||||
movability == hir::GeneratorMovability::Movable)
|
||||
}
|
||||
_ => bug!(),
|
||||
|
@ -921,8 +947,6 @@ impl MirPass for StateTransform {
|
|||
movable,
|
||||
mir);
|
||||
|
||||
let state_field = upvars.len();
|
||||
|
||||
// Run the transformation which converts Places from Local to generator struct
|
||||
// accesses for locals in `remap`.
|
||||
// It also rewrites `return x` and `yield y` as writing a new generator state and returning
|
||||
|
@ -935,7 +959,7 @@ impl MirPass for StateTransform {
|
|||
storage_liveness,
|
||||
suspension_points: Vec::new(),
|
||||
new_ret_local,
|
||||
state_field,
|
||||
discr_ty,
|
||||
};
|
||||
transform.visit_mir(mir);
|
||||
|
||||
|
|
|
@ -55,8 +55,7 @@ pub struct LivenessResult {
|
|||
}
|
||||
|
||||
/// Computes which local variables are live within the given function
|
||||
/// `mir`. The liveness mode `mode` determines what sorts of uses are
|
||||
/// considered to make a variable live (e.g., do drops count?).
|
||||
/// `mir`, including drops.
|
||||
pub fn liveness_of_locals<'tcx>(
|
||||
mir: &Mir<'tcx>,
|
||||
) -> LivenessResult {
|
||||
|
|
87
src/test/debuginfo/generator-locals.rs
Normal file
87
src/test/debuginfo/generator-locals.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
// min-lldb-version: 310
|
||||
|
||||
// compile-flags:-g
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
||||
// gdb-command:run
|
||||
// gdb-command:print a
|
||||
// gdb-check:$1 = 5
|
||||
// gdb-command:print c
|
||||
// gdb-check:$2 = 6
|
||||
// gdb-command:print d
|
||||
// gdb-check:$3 = 7
|
||||
// gdb-command:continue
|
||||
// gdb-command:print a
|
||||
// gdb-check:$4 = 7
|
||||
// gdb-command:print c
|
||||
// gdb-check:$5 = 6
|
||||
// gdb-command:print e
|
||||
// gdb-check:$6 = 8
|
||||
// gdb-command:continue
|
||||
// gdb-command:print a
|
||||
// gdb-check:$7 = 8
|
||||
// gdb-command:print c
|
||||
// gdb-check:$8 = 6
|
||||
|
||||
// === LLDB TESTS ==================================================================================
|
||||
|
||||
// lldb-command:run
|
||||
// lldb-command:print a
|
||||
// lldbg-check:(int) $0 = 5
|
||||
// lldbr-check:(int) a = 5
|
||||
// lldb-command:print c
|
||||
// lldbg-check:(int) $1 = 6
|
||||
// lldbr-check:(int) c = 6
|
||||
// lldb-command:print d
|
||||
// lldbg-check:(int) $2 = 7
|
||||
// lldbr-check:(int) d = 7
|
||||
// lldb-command:continue
|
||||
// lldb-command:print a
|
||||
// lldbg-check:(int) $3 = 7
|
||||
// lldbr-check:(int) a = 7
|
||||
// lldb-command:print c
|
||||
// lldbg-check:(int) $4 = 6
|
||||
// lldbr-check:(int) c = 6
|
||||
// lldb-command:print e
|
||||
// lldbg-check:(int) $5 = 8
|
||||
// lldbr-check:(int) e = 8
|
||||
// lldb-command:continue
|
||||
// lldb-command:print a
|
||||
// lldbg-check:(int) $6 = 8
|
||||
// lldbr-check:(int) a = 8
|
||||
// lldb-command:print c
|
||||
// lldbg-check:(int) $7 = 6
|
||||
// lldbr-check:(int) c = 6
|
||||
|
||||
#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
|
||||
#![omit_gdb_pretty_printer_section]
|
||||
|
||||
use std::ops::Generator;
|
||||
use std::pin::Pin;
|
||||
|
||||
fn main() {
|
||||
let mut a = 5;
|
||||
let mut b = || {
|
||||
let c = 6; // Live across multiple yield points
|
||||
|
||||
let d = 7; // Live across only one yield point
|
||||
yield;
|
||||
_zzz(); // #break
|
||||
a = d;
|
||||
|
||||
let e = 8; // Live across zero yield points
|
||||
_zzz(); // #break
|
||||
a = e;
|
||||
|
||||
yield;
|
||||
_zzz(); // #break
|
||||
a = c;
|
||||
};
|
||||
Pin::new(&mut b).resume();
|
||||
Pin::new(&mut b).resume();
|
||||
Pin::new(&mut b).resume();
|
||||
_zzz(); // #break
|
||||
}
|
||||
|
||||
fn _zzz() {()}
|
68
src/test/debuginfo/generator-objects.rs
Normal file
68
src/test/debuginfo/generator-objects.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
// ignore-tidy-linelength
|
||||
|
||||
// Require LLVM with DW_TAG_variant_part and a gdb that can read it.
|
||||
// min-system-llvm-version: 8.0
|
||||
// min-gdb-version: 8.2
|
||||
|
||||
// compile-flags:-g
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
||||
// gdb-command:run
|
||||
// gdb-command:print b
|
||||
// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 0, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {[...]}}}
|
||||
// gdb-command:continue
|
||||
// gdb-command:print b
|
||||
// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 3, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {c: 6, d: 7}, 4: generator_objects::main::generator::Suspend1 {[...]}}}
|
||||
// gdb-command:continue
|
||||
// gdb-command:print b
|
||||
// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 4, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {c: 7, d: 8}}}
|
||||
// gdb-command:continue
|
||||
// gdb-command:print b
|
||||
// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 1, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {[...]}}}
|
||||
|
||||
// === LLDB TESTS ==================================================================================
|
||||
|
||||
// lldb-command:run
|
||||
// lldb-command:print b
|
||||
// lldbg-check:(generator_objects::main::generator) $0 = generator(&0x[...])
|
||||
// lldb-command:continue
|
||||
// lldb-command:print b
|
||||
// lldbg-check:(generator_objects::main::generator) $1 = generator(&0x[...])
|
||||
// lldb-command:continue
|
||||
// lldb-command:print b
|
||||
// lldbg-check:(generator_objects::main::generator) $2 = generator(&0x[...])
|
||||
// lldb-command:continue
|
||||
// lldb-command:print b
|
||||
// lldbg-check:(generator_objects::main::generator) $3 = generator(&0x[...])
|
||||
|
||||
#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
|
||||
#![omit_gdb_pretty_printer_section]
|
||||
|
||||
use std::ops::Generator;
|
||||
use std::pin::Pin;
|
||||
|
||||
fn main() {
|
||||
let mut a = 5;
|
||||
let mut b = || {
|
||||
let mut c = 6;
|
||||
let mut d = 7;
|
||||
|
||||
yield;
|
||||
a += 1;
|
||||
c += 1;
|
||||
d += 1;
|
||||
|
||||
yield;
|
||||
println!("{} {} {}", a, c, d);
|
||||
};
|
||||
_zzz(); // #break
|
||||
Pin::new(&mut b).resume();
|
||||
_zzz(); // #break
|
||||
Pin::new(&mut b).resume();
|
||||
_zzz(); // #break
|
||||
Pin::new(&mut b).resume();
|
||||
_zzz(); // #break
|
||||
}
|
||||
|
||||
fn _zzz() {()}
|
|
@ -1,42 +0,0 @@
|
|||
// min-lldb-version: 310
|
||||
|
||||
// compile-flags:-g
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
||||
// gdb-command:run
|
||||
// gdb-command:print a
|
||||
// gdb-check:$1 = 5
|
||||
// gdb-command:print d
|
||||
// gdb-check:$2 = 6
|
||||
|
||||
// === LLDB TESTS ==================================================================================
|
||||
|
||||
// lldb-command:run
|
||||
// lldb-command:print a
|
||||
// lldbg-check:(int) $0 = 5
|
||||
// lldbr-check:(int) a = 5
|
||||
// lldb-command:print d
|
||||
// lldbg-check:(int) $1 = 6
|
||||
// lldbr-check:(int) d = 6
|
||||
|
||||
#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
|
||||
#![omit_gdb_pretty_printer_section]
|
||||
|
||||
use std::ops::Generator;
|
||||
use std::pin::Pin;
|
||||
|
||||
fn main() {
|
||||
let mut a = 5;
|
||||
let mut b = || {
|
||||
let d = 6;
|
||||
yield;
|
||||
_zzz(); // #break
|
||||
a = d;
|
||||
};
|
||||
Pin::new(&mut b).resume();
|
||||
Pin::new(&mut b).resume();
|
||||
_zzz(); // #break
|
||||
}
|
||||
|
||||
fn _zzz() {()}
|
|
@ -13,7 +13,8 @@ fn main() {
|
|||
|
||||
// START rustc.main-{{closure}}.generator_drop.0.mir
|
||||
// bb0: {
|
||||
// switchInt(((*_1).0: u32)) -> [0u32: bb4, 3u32: bb7, otherwise: bb8];
|
||||
// _5 = discriminant((*_1));
|
||||
// switchInt(move _5) -> [0u32: bb4, 3u32: bb7, otherwise: bb8];
|
||||
// }
|
||||
// bb1: {
|
||||
// goto -> bb5;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue