From 52e4407d46e687c17f49ab3046a56ab172c49709 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Mon, 8 Apr 2019 16:25:46 -0700 Subject: [PATCH 01/13] Update comment for liveness_of_locals --- src/librustc_mir/util/liveness.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index cbdd50cf4052a..0cebc57e1b69a 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -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 { From 9e06f2520088a3ad77018f1f5d4c8d3b90497fb1 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 21 Mar 2019 14:35:54 -0700 Subject: [PATCH 02/13] Test debuginfo of different var liveness in generators --- src/test/debuginfo/generators.rs | 53 +++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/test/debuginfo/generators.rs b/src/test/debuginfo/generators.rs index 35a67217f1671..59dbfecc39ffc 100644 --- a/src/test/debuginfo/generators.rs +++ b/src/test/debuginfo/generators.rs @@ -7,8 +7,22 @@ // gdb-command:run // gdb-command:print a // gdb-check:$1 = 5 -// gdb-command:print d +// 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 ================================================================================== @@ -16,9 +30,29 @@ // lldb-command:print a // lldbg-check:(int) $0 = 5 // lldbr-check:(int) a = 5 -// lldb-command:print d +// lldb-command:print c // lldbg-check:(int) $1 = 6 -// lldbr-check:(int) d = 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] @@ -29,13 +63,24 @@ use std::pin::Pin; fn main() { let mut a = 5; let mut b = || { - let d = 6; + 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 } From 70c1b6c530bfe86a2b54dbd40a7628fc2105c0b9 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Mon, 8 Apr 2019 17:44:00 -0700 Subject: [PATCH 03/13] Define generator discriminant type in only one place --- src/librustc/ty/sty.rs | 24 +++++++++++++----------- src/librustc_mir/build/expr/as_rvalue.rs | 5 +++-- src/librustc_mir/transform/generator.rs | 13 +++++++++---- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 9a90ccda9140c..1ac18f6117f05 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -298,14 +298,10 @@ static_assert!(MEM_SIZE_OF_TY_KIND: ::std::mem::size_of::>() == 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 +387,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,6 +467,11 @@ impl<'tcx> GeneratorSubsts<'tcx> { } impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { + /// The type of the state "discriminant" used in the generator type. + 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. @@ -484,15 +486,15 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { /// 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. + /// It includes the upvars and the state discriminant. pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator> + 'a { - self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32)) + self.upvar_tys(def_id, tcx).chain(iter::once(self.discr_ty(tcx))) } /// 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. + /// It includes the upvars, state types and the state discriminant. pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator> + Captures<'gcx> + 'a { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index c58b570d8fc9f..0871217c524c4 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -255,15 +255,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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. + let discr_ty = substs.discr_ty(this.hir.tcx()); operands.push(Operand::Constant(box Constant { span: expr_span, - ty: this.hir.tcx().types.u32, + ty: discr_ty, 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), + ty::ParamEnv::empty().and(discr_ty), ), ), })); diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 2b909feb9603f..cf7397ca48880 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -166,6 +166,9 @@ struct TransformVisitor<'a, 'tcx: 'a> { // The index of the generator state in the generator struct state_field: usize, + // The type of the generator state in the generator struct + discr_ty: Ty<'tcx>, + // Mapping from Local to (type of local, generator struct index) // FIXME(eddyb) This should use `IndexVec>`. remap: FxHashMap, usize)>, @@ -200,15 +203,15 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> { // 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 state = self.make_field(self.state_field, self.discr_ty); let val = Operand::Constant(box Constant { span: source_info.span, - ty: self.tcx.types.u32, + ty: self.discr_ty, 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) + ty::ParamEnv::empty().and(self.discr_ty) )), }); Statement { @@ -889,10 +892,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!(), @@ -937,6 +941,7 @@ impl MirPass for StateTransform { suspension_points: Vec::new(), new_ret_local, state_field, + discr_ty, }; transform.visit_mir(mir); From 4de2d8a86909cec4279c4054790c62c66ca033d7 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 25 Apr 2019 10:23:15 -0700 Subject: [PATCH 04/13] Give GeneratorLayout a list of fields for each variant But don't really use it yet. --- src/librustc/mir/mod.rs | 4 ++-- src/librustc/ty/sty.rs | 16 ++++++++-------- src/librustc_codegen_ssa/mir/mod.rs | 6 ++++-- src/librustc_mir/transform/generator.rs | 3 ++- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index bf2a1eaafd664..5372f868aa9ab 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2998,7 +2998,7 @@ pub struct UnsafetyCheckResult { /// The layout of generator state #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GeneratorLayout<'tcx> { - pub fields: Vec>, + pub variant_fields: Vec>>, } #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] @@ -3187,7 +3187,7 @@ BraceStructTypeFoldableImpl! { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> { - fields + variant_fields } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 1ac18f6117f05..edd6014618e78 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -475,16 +475,16 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { /// 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> + Captures<'gcx> + 'a { - let state = tcx.generator_layout(def_id).fields.iter(); - state.map(move |d| d.ty.subst(tcx, self.substs)) + pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> + impl Iterator> + Captures<'gcx> + 'a + { + // TODO remove so we can handle variants properly + tcx.generator_layout(def_id) + .variant_fields[0].iter() + .map(move |d| d.ty.subst(tcx, self.substs)) } - /// This is the types of the fields of a generate which + /// This is the types of the fields of a generator which /// is available before the generator transformation. /// It includes the upvars and the state discriminant. pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 621c4e5d4488e..4387d77a925b2 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -655,10 +655,12 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( ty::Generator(def_id, substs, _) => (def_id, substs), _ => bug!("generator layout without generator substs"), }; + // TODO handle variant scopes here let state_tys = gen_substs.state_tys(def_id, tcx); - let upvar_count = upvar_debuginfo.len(); - generator_layout.fields + // TODO remove assumption of only one variant + let upvar_count = mir.upvar_decls.len(); + generator_layout.variant_fields[0] .iter() .zip(state_tys) .enumerate() diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index cf7397ca48880..253038fd03037 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -549,7 +549,8 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }).unzip(); let layout = GeneratorLayout { - fields: vars + // Put everything in one variant, for now. + variant_fields: vec![vars] }; (remap, layout, storage_liveness) From 5a7af5480c5f9a7d1b5964e3c77ef18326a3a67b Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 2 Apr 2019 16:04:51 -0700 Subject: [PATCH 05/13] Support variantful generators This allows generators to overlap fields using variants. --- src/librustc/mir/mod.rs | 9 +- src/librustc/mir/tcx.rs | 12 +- src/librustc/ty/layout.rs | 81 +++++++++-- src/librustc/ty/sty.rs | 28 ++-- .../debuginfo/metadata.rs | 6 +- src/librustc_codegen_llvm/type_of.rs | 5 + src/librustc_codegen_ssa/mir/mod.rs | 37 +++-- src/librustc_codegen_ssa/mir/place.rs | 12 +- .../borrow_check/nll/type_check/mod.rs | 77 +++++++---- src/librustc_mir/build/expr/as_rvalue.rs | 19 +-- src/librustc_mir/transform/deaggregator.rs | 20 ++- src/librustc_mir/transform/generator.rs | 127 ++++++++++-------- src/test/mir-opt/generator-drop-cleanup.rs | 3 +- 13 files changed, 281 insertions(+), 155 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 5372f868aa9ab..79e143b5c24a5 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -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)) } @@ -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() diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 04b763f773d9e..199ee3e04b32a 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -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), diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index fd1d3a91ede1f..10afaebc91a91 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -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::, _>>()?, + 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::, _>>()?, &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::, _>>()?, + &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::, _>>()?; + + 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) => { @@ -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 { + 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 | @@ -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], @@ -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); } } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index edd6014618e78..842e49dfc0865 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -15,7 +15,6 @@ 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 rustc_target::spec::abi; @@ -475,30 +474,23 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { /// 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. + /// + /// The locals are grouped by their variant number. Note that some locals may + /// be repeated in multiple variants. pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> - impl Iterator> + Captures<'gcx> + 'a + impl Iterator> + Captures<'gcx> + 'a> { - // TODO remove so we can handle variants properly tcx.generator_layout(def_id) - .variant_fields[0].iter() - .map(move |d| d.ty.subst(tcx, self.substs)) + .variant_fields.iter() + .map(move |v| v.iter().map(move |d| d.ty.subst(tcx, self.substs))) } - /// This is the types of the fields of a generator which - /// is available before the generator transformation. - /// It includes the upvars and the state discriminant. - 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. + pub fn prefix_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator> + 'a { - self.upvar_tys(def_id, tcx).chain(iter::once(self.discr_ty(tcx))) - } - - /// This is the types of all the fields stored in a generator. - /// It includes the upvars, state types and the state discriminant. - pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> - impl Iterator> + Captures<'gcx> + 'a - { - self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx)) + self.upvar_tys(def_id, tcx) } } diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 31348b99c5af0..bbcd3c220d67c 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -691,9 +691,12 @@ 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| { + // TODO handle variant fields + let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| { cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) }).collect(); + // TODO use prepare_enum_metadata and update it to handle multiple + // fields in the outer layout. prepare_tuple_metadata(cx, t, &upvar_tys, @@ -1818,6 +1821,7 @@ fn prepare_enum_metadata( }; // The variant part must be wrapped in a struct according to DWARF. + // TODO create remaining fields here, if any. let type_array = create_DIArray(DIB(cx), &[Some(variant_part)]); let struct_wrapper = unsafe { llvm::LLVMRustDIBuilderCreateStructType( diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index d42fa8291618c..080f78ff112ad 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -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(..), &layout::Variants::Single { index }) + = (&layout.ty.sty, &layout.variants) + { + write!(&mut name, "::variant#{:?}", index).unwrap(); + } Some(name) } _ => None diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 4387d77a925b2..52429294852e2 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -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,7 @@ 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)); let generator_fields = mir.generator_layout.as_ref().map(|generator_layout| { let (def_id, gen_substs) = match closure_layout.ty.sty { @@ -658,23 +659,39 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( // TODO handle variant scopes here let state_tys = gen_substs.state_tys(def_id, tcx); - // TODO remove assumption of only one variant - let upvar_count = mir.upvar_decls.len(); - generator_layout.variant_fields[0] - .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, (decls, tys))| { + let variant_idx = Some(VariantIdx::from(variant_idx)); + decls.iter() + .zip(tys) + .enumerate() + .filter_map(move |(i, (decl, ty))| { + let ty = fx.monomorphize(&ty); + decl.name.map(|name| { + (variant_idx, i, name, false, ty) + }) + }) }) }).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) 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); diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 1134707f96c92..2875468127e81 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -296,9 +296,15 @@ 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 = match self.layout.ty.sty { + ty::TyKind::Adt(adt_def, _) => adt_def + .discriminant_for_variant(bx.tcx(), variant_index) + .val, + // Generators don't support explicit discriminant values, so + // they are the same as the variant index. + ty::TyKind::Generator(..) => variant_index.as_u32() as u128, + _ => bug!(), + }; bx.store( bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to), ptr.llval, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 0dee64db72763..94900b98a52d5 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -684,6 +684,25 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } } + ty::Generator(def_id, substs, _) => { + let variants = substs.state_tys(def_id, tcx).count(); + if index.as_usize() >= variants { + PlaceTy::from_ty( + span_mirbug_and_err!( + self, + place, + "cast to variant #{:?} but generator only has {:?}", + index, + variants + ), + ) + } else { + PlaceTy { + ty: base_ty, + variant_index: Some(index), + } + } + } _ => { let ty = if let Some(name) = maybe_name { span_mirbug_and_err!( @@ -745,11 +764,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 +797,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 +1937,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), diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 0871217c524c4..201bc4a43e4a4 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -211,7 +211,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); @@ -252,22 +252,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. - let discr_ty = substs.discr_ty(this.hir.tcx()); - operands.push(Operand::Constant(box Constant { - span: expr_span, - ty: discr_ty, - user_ty: None, - literal: this.hir.tcx().mk_const( - ty::Const::from_bits( - this.hir.tcx(), - 0, - ty::ParamEnv::empty().and(discr_ty), - ), - ), - })); box AggregateKind::Generator(closure_id, substs, movability) } UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs), diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index 9061dfff76fe8..9f8d40bf4cdd5 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -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. + 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 }; diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 253038fd03037..b7c4bfd5126e8 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -60,7 +60,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; 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 +145,14 @@ fn self_arg() -> Local { } /// Generator have not been resumed yet -const UNRESUMED: u32 = 0; +const UNRESUMED: usize = 0; /// Generator has returned / is completed -const RETURNED: u32 = 1; +const RETURNED: usize = 1; /// Generator has been poisoned -const POISONED: u32 = 2; +const POISONED: usize = 2; struct SuspensionPoint { - state: u32, + state: usize, resume: BasicBlock, drop: Option, storage_liveness: liveness::LiveVarSet, @@ -163,15 +163,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 generator state in the generator struct + // 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>`. - remap: FxHashMap, usize)>, + remap: FxHashMap, 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>`. @@ -192,8 +189,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), @@ -201,24 +199,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.discr_ty); - let val = Operand::Constant(box Constant { - span: source_info.span, - ty: self.discr_ty, - user_ty: None, - literal: self.tcx.mk_const(ty::Const::from_bits( - self.tcx, - state_disc.into(), - ty::ParamEnv::empty().and(self.discr_ty) - )), - }); + // 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> { @@ -235,8 +237,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); @@ -277,7 +279,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, @@ -286,11 +288,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; } @@ -391,6 +393,7 @@ fn locals_live_across_suspend_points( ) -> ( liveness::LiveVarSet, FxHashMap, + BitSet, ) { let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len()); let def_id = source.def_id(); @@ -435,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(), @@ -488,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>, @@ -497,15 +504,14 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, interior: Ty<'tcx>, movable: bool, mir: &mut Mir<'tcx>) - -> (FxHashMap, usize)>, + -> (FxHashMap, VariantIdx, usize)>, GeneratorLayout<'tcx>, FxHashMap) { // 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); @@ -531,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 @@ -541,38 +546,44 @@ 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) + ((local, (var.ty, variant_index, idx)), var) }).unzip(); + // Put every var in each variant, for now. + let empty_variants = iter::repeat(vec![]).take(3); + let state_variants = iter::repeat(vars).take(suspending_blocks.count()); let layout = GeneratorLayout { - // Put everything in one variant, for now. - variant_fields: vec![vars] + variant_fields: empty_variants.chain(state_variants).collect() }; (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::>()), - 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::>()), + 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, @@ -657,7 +668,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; @@ -771,7 +782,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)); } } @@ -789,7 +801,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); @@ -835,7 +847,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 { let source_info = source_info(mir); @@ -927,8 +939,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 @@ -941,7 +951,6 @@ impl MirPass for StateTransform { storage_liveness, suspension_points: Vec::new(), new_ret_local, - state_field, discr_ty, }; transform.visit_mir(mir); diff --git a/src/test/mir-opt/generator-drop-cleanup.rs b/src/test/mir-opt/generator-drop-cleanup.rs index 48398691271ba..9cc4272fafabf 100644 --- a/src/test/mir-opt/generator-drop-cleanup.rs +++ b/src/test/mir-opt/generator-drop-cleanup.rs @@ -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; From 961ba95e5a89a101db38e120468be6224af3f8cc Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 12 Apr 2019 17:03:03 -0700 Subject: [PATCH 06/13] Describe generator variants in debuginfo --- src/librustc/ty/sty.rs | 40 +++ .../debuginfo/metadata.rs | 270 ++++++++++++------ src/librustc_mir/transform/generator.rs | 7 +- .../{generators.rs => generator-locals.rs} | 0 src/test/debuginfo/generator-objects.rs | 68 +++++ 5 files changed, 288 insertions(+), 97 deletions(-) rename src/test/debuginfo/{generators.rs => generator-locals.rs} (100%) create mode 100644 src/test/debuginfo/generator-objects.rs diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 842e49dfc0865..af447fc8a4896 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -11,6 +11,7 @@ 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::{List, TyS, ParamEnvAnd, ParamEnv}; +use crate::ty::layout::VariantIdx; use crate::util::captures::Captures; use crate::mir::interpret::{Scalar, Pointer}; @@ -466,7 +467,44 @@ 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 variants of this Generator. + #[inline] + pub fn variants(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> + impl Iterator + { + // FIXME requires optimized MIR + let num_variants = self.state_tys(def_id, tcx).count(); + (0..num_variants).map(VariantIdx::new) + } + + /// Calls `f` with a reference to the name of the enumerator for the given + /// variant `v`. + #[inline] + pub fn map_variant_name(&self, v: VariantIdx, f: impl FnOnce(&str) -> R) -> R { + 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())); + } + }; + 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 } @@ -477,6 +515,7 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { /// /// 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> + Captures<'gcx> + 'a> { @@ -487,6 +526,7 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'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> + 'a { diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index bbcd3c220d67c..0b1ac1ad78866 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -28,7 +28,7 @@ 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,17 +691,15 @@ pub fn type_metadata( usage_site_span).finalize(cx) } ty::Generator(def_id, substs, _) => { - // TODO handle variant fields let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| { cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) }).collect(); - // TODO use prepare_enum_metadata and update it to handle multiple - // fields in the outer layout. - 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 => { @@ -721,7 +719,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) => { @@ -998,6 +997,31 @@ struct MemberDescription<'ll> { discriminant: Option, } +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 @@ -1264,7 +1288,13 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> { impl EnumMemberDescriptionFactory<'ll, 'tcx> { fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec> { - 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(_, substs, _) => VariantInfo::Generator(*substs, index), + _ => bug!(), + } + }; // This will always find the metadata in the type map. let fallback = use_enum_fallback(cx); @@ -1275,12 +1305,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); @@ -1297,7 +1333,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { name: if fallback { String::new() } else { - adt.variants[index].ident.as_str().to_string() + variant_info.name_as_string() }, type_metadata: variant_type_metadata, offset: Size::ZERO, @@ -1325,10 +1361,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); @@ -1340,20 +1377,25 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { self.enum_type, variant_type_metadata, member_descriptions); + + // TODO make this into a helper + let discriminant = match &self.layout.ty.sty { + ty::Adt(adt, _) => adt.discriminant_for_variant(cx.tcx, i).val as u64, + ty::Generator(..) => i.as_usize() as u64, + _ => bug!(), + }.into(); MemberDescription { name: if fallback { String::new() } else { - adt.variants[i].ident.as_str().to_string() + variant_info.name_as_string() }, 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, } }).collect() } @@ -1373,7 +1415,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); @@ -1413,7 +1455,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_name(|variant_name| { + name.push_str(variant_name); + }); // Create the (singleton) list of descriptions of union members. vec![ @@ -1430,10 +1474,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); @@ -1461,7 +1506,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { }; MemberDescription { - name: adt.variants[i].ident.as_str().to_string(), + name: variant_info.name_as_string(), type_metadata: variant_type_metadata, offset: Size::ZERO, size: self.layout.size, @@ -1519,6 +1564,34 @@ enum EnumDiscriminantInfo<'ll> { NoDiscriminant } +#[derive(Copy, Clone)] +enum VariantInfo<'tcx> { + Adt(&'tcx ty::VariantDef), + Generator(ty::GeneratorSubsts<'tcx>, VariantIdx), +} + +impl<'tcx> VariantInfo<'tcx> { + fn map_name(&self, f: impl FnOnce(&str) -> R) -> R { + match self { + VariantInfo::Adt(variant) => f(&variant.ident.as_str()), + VariantInfo::Generator(substs, variant_index) => + substs.map_variant_name(*variant_index, f), + } + } + + fn name_as_string(&self) -> String { + self.map_name(|name| name.to_string()) + } + + fn field_name(&self, i: usize) -> String { + match self { + VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn => + variant.fields[i].ident.to_string(), + _ => 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 @@ -1526,32 +1599,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_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) { @@ -1573,7 +1638,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 { @@ -1582,7 +1647,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() ) }; @@ -1609,6 +1674,7 @@ fn prepare_enum_metadata( enum_def_id: DefId, unique_type_id: UniqueTypeId, span: Span, + outer_field_tys: Vec>, ) -> RecursiveTypeDescription<'ll, 'tcx> { let enum_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); @@ -1622,20 +1688,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 + .variants(enum_def_id, cx.tcx) + .map(|v| substs.map_variant_name(v, |name| { + let name = SmallCStr::new(name); + unsafe { + Some(llvm::LLVMRustDIBuilderCreateEnumerator( + DIB(cx), + name.as_ptr(), + // FIXME: what if enumeration has i128 discriminant? + v.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 @@ -1648,14 +1730,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(), @@ -1736,6 +1822,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, @@ -1762,7 +1853,7 @@ fn prepare_enum_metadata( Some(llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), containing_scope, - ptr::null_mut(), + discriminator_name, file_metadata, UNKNOWN_LINE_NUMBER, size.bits(), @@ -1787,7 +1878,7 @@ fn prepare_enum_metadata( Some(llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), containing_scope, - ptr::null_mut(), + discriminator_name, file_metadata, UNKNOWN_LINE_NUMBER, size.bits(), @@ -1799,6 +1890,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() @@ -1819,10 +1926,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. - // TODO create remaining fields here, if any. - 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), @@ -1854,12 +1961,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 @@ -1917,26 +2018,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); diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index b7c4bfd5126e8..36db8c0b7ef79 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -54,6 +54,7 @@ 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; @@ -145,11 +146,11 @@ fn self_arg() -> Local { } /// Generator have not been resumed yet -const UNRESUMED: usize = 0; +const UNRESUMED: usize = GeneratorSubsts::UNRESUMED; /// Generator has returned / is completed -const RETURNED: usize = 1; +const RETURNED: usize = GeneratorSubsts::RETURNED; /// Generator has been poisoned -const POISONED: usize = 2; +const POISONED: usize = GeneratorSubsts::POISONED; struct SuspensionPoint { state: usize, diff --git a/src/test/debuginfo/generators.rs b/src/test/debuginfo/generator-locals.rs similarity index 100% rename from src/test/debuginfo/generators.rs rename to src/test/debuginfo/generator-locals.rs diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs new file mode 100644 index 0000000000000..a7c1ac1e902bd --- /dev/null +++ b/src/test/debuginfo/generator-objects.rs @@ -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[...], <>: {__state: 0, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 ([...]), variant#4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-command:continue +// gdb-command:print b +// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 3, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 (6, 7), variant#4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-command:continue +// gdb-command:print b +// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 4, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 ([...]), variant#4: generator_objects::main::generator::variant#4 (7, 8)}} +// gdb-command:continue +// gdb-command:print b +// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 1, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 ([...]), variant#4: generator_objects::main::generator::variant#4 ([...])}} + +// === 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() {()} From 6e2e17d1152d46314b332ecaf11b80786386b919 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Wed, 17 Apr 2019 17:46:40 -0700 Subject: [PATCH 07/13] Make generator object debuginfo easier to read --- .../debuginfo/metadata.rs | 25 +++++++++++++------ src/test/debuginfo/generator-objects.rs | 8 +++--- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 0b1ac1ad78866..85f38912d3d76 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1333,7 +1333,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { name: if fallback { String::new() } else { - variant_info.name_as_string() + variant_info.variant_name() }, type_metadata: variant_type_metadata, offset: Size::ZERO, @@ -1388,7 +1388,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { name: if fallback { String::new() } else { - variant_info.name_as_string() + variant_info.variant_name() }, type_metadata: variant_type_metadata, offset: Size::ZERO, @@ -1455,7 +1455,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { self.layout, self.layout.fields.offset(discr_index), self.layout.field(cx, discr_index).size); - variant_info_for(*niche_variants.start()).map_name(|variant_name| { + variant_info_for(*niche_variants.start()).map_struct_name(|variant_name| { name.push_str(variant_name); }); @@ -1506,7 +1506,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { }; MemberDescription { - name: variant_info.name_as_string(), + name: variant_info.variant_name(), type_metadata: variant_type_metadata, offset: Size::ZERO, size: self.layout.size, @@ -1571,7 +1571,7 @@ enum VariantInfo<'tcx> { } impl<'tcx> VariantInfo<'tcx> { - fn map_name(&self, f: impl FnOnce(&str) -> R) -> R { + fn map_struct_name(&self, f: impl FnOnce(&str) -> R) -> R { match self { VariantInfo::Adt(variant) => f(&variant.ident.as_str()), VariantInfo::Generator(substs, variant_index) => @@ -1579,8 +1579,17 @@ impl<'tcx> VariantInfo<'tcx> { } } - fn name_as_string(&self) -> String { - self.map_name(|name| name.to_string()) + 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 { @@ -1604,7 +1613,7 @@ fn describe_enum_variant( containing_scope: &'ll DIScope, span: Span, ) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) { - let metadata_stub = variant.map_name(|variant_name| { + 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( diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index a7c1ac1e902bd..b2837f42f94c7 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -10,16 +10,16 @@ // gdb-command:run // gdb-command:print b -// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 0, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 ([...]), variant#4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 ([...]), 4: generator_objects::main::generator::variant#4 ([...])}} // gdb-command:continue // gdb-command:print b -// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 3, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 (6, 7), variant#4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 (6, 7), 4: generator_objects::main::generator::variant#4 ([...])}} // gdb-command:continue // gdb-command:print b -// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 4, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 ([...]), variant#4: generator_objects::main::generator::variant#4 (7, 8)}} +// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 ([...]), 4: generator_objects::main::generator::variant#4 (7, 8)}} // gdb-command:continue // gdb-command:print b -// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <>: {__state: 1, Unresumed: generator_objects::main::generator::Unresumed, Returned: generator_objects::main::generator::Returned, Panicked: generator_objects::main::generator::Panicked, variant#3: generator_objects::main::generator::variant#3 ([...]), variant#4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 ([...]), 4: generator_objects::main::generator::variant#4 ([...])}} // === LLDB TESTS ================================================================================== From f772c39bf9fb3af982bb9a4728d4fdc7308a8bbd Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 18 Apr 2019 15:36:16 -0700 Subject: [PATCH 08/13] Include generator locals as field names in debuginfo --- src/librustc/mir/mod.rs | 2 +- .../debuginfo/metadata.rs | 25 +++++++++++++------ src/test/debuginfo/generator-objects.rs | 8 +++--- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 79e143b5c24a5..2f05a37986d2c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2997,7 +2997,7 @@ pub struct UnsafetyCheckResult { /// The layout of generator state #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GeneratorLayout<'tcx> { - pub variant_fields: Vec>>, + pub variant_fields: IndexVec>>, } #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 85f38912d3d76..54d15e527c9c6 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -23,6 +23,7 @@ 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; @@ -1291,7 +1292,10 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { let variant_info_for = |index: VariantIdx| { match &self.enum_type.sty { ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]), - ty::Generator(_, substs, _) => VariantInfo::Generator(*substs, index), + ty::Generator(def_id, substs, _) => { + let generator_layout = cx.tcx.generator_layout(*def_id); + VariantInfo::Generator(*substs, generator_layout, index) + } _ => bug!(), } }; @@ -1567,14 +1571,14 @@ enum EnumDiscriminantInfo<'ll> { #[derive(Copy, Clone)] enum VariantInfo<'tcx> { Adt(&'tcx ty::VariantDef), - Generator(ty::GeneratorSubsts<'tcx>, VariantIdx), + Generator(ty::GeneratorSubsts<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx), } impl<'tcx> VariantInfo<'tcx> { fn map_struct_name(&self, f: impl FnOnce(&str) -> R) -> R { match self { VariantInfo::Adt(variant) => f(&variant.ident.as_str()), - VariantInfo::Generator(substs, variant_index) => + VariantInfo::Generator(substs, _, variant_index) => substs.map_variant_name(*variant_index, f), } } @@ -1582,7 +1586,7 @@ impl<'tcx> VariantInfo<'tcx> { fn variant_name(&self) -> String { match self { VariantInfo::Adt(variant) => variant.ident.to_string(), - VariantInfo::Generator(_, variant_index) => { + 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 @@ -1593,11 +1597,16 @@ impl<'tcx> VariantInfo<'tcx> { } fn field_name(&self, i: usize) -> String { - match self { + let field_name = match self { VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn => - variant.fields[i].ident.to_string(), - _ => format!("__{}", i), - } + Some(variant.fields[i].ident.to_string()), + VariantInfo::Generator(_, generator_layout, variant_index) => { + let variant_decls = &generator_layout.variant_fields[*variant_index]; + variant_decls[i].name.map(|name| name.to_string()) + } + _ => None, + }; + field_name.unwrap_or_else(|| format!("__{}", i)) } } diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index b2837f42f94c7..682c213f33244 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -10,16 +10,16 @@ // gdb-command:run // gdb-command:print b -// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 ([...]), 4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 {[...]}, 4: generator_objects::main::generator::variant#4 {[...]}}} // gdb-command:continue // gdb-command:print b -// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 (6, 7), 4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 {c: 6, d: 7}, 4: generator_objects::main::generator::variant#4 {[...]}}} // gdb-command:continue // gdb-command:print b -// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 ([...]), 4: generator_objects::main::generator::variant#4 (7, 8)}} +// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 {[...]}, 4: generator_objects::main::generator::variant#4 {c: 7, d: 8}}} // gdb-command:continue // gdb-command:print b -// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 ([...]), 4: generator_objects::main::generator::variant#4 ([...])}} +// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 {[...]}, 4: generator_objects::main::generator::variant#4 {[...]}}} // === LLDB TESTS ================================================================================== From b8f6de4aca47ddd1daa5217813848155a2a825fa Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 18 Apr 2019 14:39:03 -0700 Subject: [PATCH 09/13] Generalize discriminant info calls for generators and ADTs --- src/librustc/ty/mod.rs | 7 ++ src/librustc/ty/sty.rs | 64 +++++++++++++++++-- .../debuginfo/metadata.rs | 12 ++-- src/librustc_codegen_ssa/mir/place.rs | 16 ++--- src/librustc_codegen_ssa/mir/rvalue.rs | 11 ++-- src/librustc_mir/interpret/cast.rs | 9 ++- src/librustc_mir/interpret/operand.rs | 19 +++--- src/librustc_mir/interpret/place.rs | 8 +-- 8 files changed, 95 insertions(+), 51 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 67be228d232e1..82696c22a22be 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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; @@ -2418,11 +2419,17 @@ impl<'a, 'gcx, 'tcx> AdtDef { }) } + #[inline] + pub fn variant_range(&self) -> Range { + (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) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index af447fc8a4896..331c54d1d4681 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -9,7 +9,7 @@ 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; @@ -18,6 +18,7 @@ use crate::mir::interpret::{Scalar, Pointer}; use smallvec::SmallVec; 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}; @@ -478,14 +479,35 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { const RETURNED_NAME: &'static str = "Returned"; const POISONED_NAME: &'static str = "Panicked"; - /// The variants of this Generator. + /// The valid variant indices of this Generator. #[inline] - pub fn variants(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> - impl Iterator - { + pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Range { // FIXME requires optimized MIR let num_variants = self.state_tys(def_id, tcx).count(); - (0..num_variants).map(VariantIdx::new) + (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)> + 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 @@ -503,7 +525,7 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { f(name) } - /// The type of the state "discriminant" used in the generator type. + /// 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 @@ -2028,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> { + 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> { + 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. diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 54d15e527c9c6..6f427f29fcc93 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1382,12 +1382,6 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { variant_type_metadata, member_descriptions); - // TODO make this into a helper - let discriminant = match &self.layout.ty.sty { - ty::Adt(adt, _) => adt.discriminant_for_variant(cx.tcx, i).val as u64, - ty::Generator(..) => i.as_usize() as u64, - _ => bug!(), - }.into(); MemberDescription { name: if fallback { String::new() @@ -1399,7 +1393,9 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { size: self.layout.size, align: self.layout.align.abi, flags: DIFlags::FlagZero, - discriminant, + discriminant: Some( + self.layout.ty.discriminant_for_variant(cx.tcx, i).unwrap().val as u64 + ), } }).collect() } @@ -1722,7 +1718,7 @@ fn prepare_enum_metadata( }) .collect(), ty::Generator(_, substs, _) => substs - .variants(enum_def_id, cx.tcx) + .variant_range(enum_def_id, cx.tcx) .map(|v| substs.map_variant_name(v, |name| { let name = SmallCStr::new(name); unsafe { diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 2875468127e81..670b6c472698d 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -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,15 +295,8 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> { .. } => { let ptr = self.project_field(bx, discr_index); - let to = match self.layout.ty.sty { - ty::TyKind::Adt(adt_def, _) => adt_def - .discriminant_for_variant(bx.tcx(), variant_index) - .val, - // Generators don't support explicit discriminant values, so - // they are the same as the variant index. - ty::TyKind::Generator(..) => variant_index.as_u32() as u128, - _ => bug!(), - }; + 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, diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 35e9d918aa6f9..8573021bef386 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -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, }); } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 32f218d49cea2..58e474658ead5 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -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); } } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 55c1bfb17dec3..334b22112fb8e 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -566,9 +566,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, .. } => @@ -603,12 +603,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 { diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 8239337796e9a..0cc480028161f 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -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 From 9ef2c30a1ae8dc6379e6c3a2e95815be5b192755 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 18 Apr 2019 14:43:44 -0700 Subject: [PATCH 10/13] Preserve visibility scopes in stored generator locals Unfortunately, this didn't have quite the effect I was hoping for. Locals still appear visible at every point in the function, regardless of scopes. I suspect all the rewriting of the MIR we do for the generator transform makes these scopes less useful. I didn't observe any regressions in behavior, but it's possible that this change is wrong without additional changes to the MIR. --- src/librustc_codegen_ssa/mir/mod.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 52429294852e2..d524ec0546297 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -649,14 +649,15 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( .iter() .zip(upvar_tys) .enumerate() - .map(|(i, (upvar, ty))| (None, 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 { ty::Generator(def_id, substs, _) => (def_id, substs), _ => bug!("generator layout without generator substs"), }; - // TODO handle variant scopes here let state_tys = gen_substs.state_tys(def_id, tcx); generator_layout.variant_fields.iter() @@ -668,10 +669,17 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( .zip(tys) .enumerate() .filter_map(move |(i, (decl, ty))| { - let ty = fx.monomorphize(&ty); - decl.name.map(|name| { - (variant_idx, i, name, false, ty) - }) + 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(); @@ -679,7 +687,7 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( upvars.chain(generator_fields) }; - for (variant_idx, field, name, by_ref, ty) in extra_locals { + 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 { @@ -713,10 +721,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 ); } }); From f7c2f2475a0adc2bf025a7a845fee61ff1468350 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 3 May 2019 14:25:22 -0700 Subject: [PATCH 11/13] Make variant_fields inner an IndexVec --- src/librustc/mir/mod.rs | 2 +- src/librustc_codegen_llvm/debuginfo/metadata.rs | 2 +- src/librustc_mir/transform/generator.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 2f05a37986d2c..ca1c30def7afc 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2997,7 +2997,7 @@ pub struct UnsafetyCheckResult { /// The layout of generator state #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GeneratorLayout<'tcx> { - pub variant_fields: IndexVec>>, + pub variant_fields: IndexVec>>, } #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 6f427f29fcc93..b09fe827b24bd 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1598,7 +1598,7 @@ impl<'tcx> VariantInfo<'tcx> { Some(variant.fields[i].ident.to_string()), VariantInfo::Generator(_, generator_layout, variant_index) => { let variant_decls = &generator_layout.variant_fields[*variant_index]; - variant_decls[i].name.map(|name| name.to_string()) + variant_decls[i.into()].name.map(|name| name.to_string()) } _ => None, }; diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 36db8c0b7ef79..d9496c8c20a40 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -58,7 +58,7 @@ 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; @@ -560,7 +560,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }).unzip(); // Put every var in each variant, for now. - let empty_variants = iter::repeat(vec![]).take(3); + let empty_variants = iter::repeat(IndexVec::new()).take(3); let state_variants = iter::repeat(vars).take(suspending_blocks.count()); let layout = GeneratorLayout { variant_fields: empty_variants.chain(state_variants).collect() From 15dbe652ffa260fda162be3fa8e7aa075884c412 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 3 May 2019 16:03:05 -0700 Subject: [PATCH 12/13] Split out debuginfo from type info in MIR GeneratorLayout --- src/librustc/mir/mod.rs | 34 +++++++++++++++++-- src/librustc/ty/sty.rs | 11 +++--- .../debuginfo/metadata.rs | 5 +-- src/librustc_codegen_ssa/mir/mod.rs | 8 +++-- src/librustc_mir/transform/generator.rs | 17 +++++++--- 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index ca1c30def7afc..9c71e694f4c79 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2994,10 +2994,29 @@ pub struct UnsafetyCheckResult { pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>, } +newtype_index! { + pub struct GeneratorField { + derive [HashStable] + DEBUG_FORMAT = "_{}", + } +} + /// The layout of generator state #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GeneratorLayout<'tcx> { - pub variant_fields: IndexVec>>, + /// The type of every local stored inside the generator. + pub field_tys: IndexVec>, + + /// Which of the above fields are in each variant. Note that one field may + /// be stored in multiple variants. + pub variant_fields: IndexVec>, + + /// 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>, } #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] @@ -3186,7 +3205,9 @@ BraceStructTypeFoldableImpl! { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> { - variant_fields + field_tys, + variant_fields, + __local_debuginfo_codegen_only_do_not_use, } } @@ -3561,6 +3582,15 @@ impl<'tcx> TypeFoldable<'tcx> for Field { } } +impl<'tcx> TypeFoldable<'tcx> for GeneratorField { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&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 { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 331c54d1d4681..a41209496bd27 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -483,7 +483,7 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { #[inline] pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Range { // FIXME requires optimized MIR - let num_variants = self.state_tys(def_id, tcx).count(); + let num_variants = tcx.generator_layout(def_id).variant_fields.len(); (VariantIdx::new(0)..VariantIdx::new(num_variants)) } @@ -541,9 +541,12 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator> + Captures<'gcx> + 'a> { - tcx.generator_layout(def_id) - .variant_fields.iter() - .map(move |v| v.iter().map(move |d| d.ty.subst(tcx, self.substs))) + 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 generator which are not stored in a diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index b09fe827b24bd..5b18e821982bd 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1597,8 +1597,9 @@ impl<'tcx> VariantInfo<'tcx> { VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn => Some(variant.fields[i].ident.to_string()), VariantInfo::Generator(_, generator_layout, variant_index) => { - let variant_decls = &generator_layout.variant_fields[*variant_index]; - variant_decls[i.into()].name.map(|name| name.to_string()) + 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, }; diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index d524ec0546297..fcf099235aa14 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -663,12 +663,14 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( generator_layout.variant_fields.iter() .zip(state_tys) .enumerate() - .flat_map(move |(variant_idx, (decls, tys))| { + .flat_map(move |(variant_idx, (fields, tys))| { let variant_idx = Some(VariantIdx::from(variant_idx)); - decls.iter() + fields.iter() .zip(tys) .enumerate() - .filter_map(move |(i, (decl, ty))| { + .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 { diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index d9496c8c20a40..77bf789d2dd16 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -555,15 +555,22 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Create a map from local indices to generator struct indices. // We also create a vector of the LocalDecls of these locals. - let (remap, vars) = live_decls.enumerate().map(|(idx, (local, var))| { - ((local, (var.ty, variant_index, 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::>(); // Put every var in each variant, for now. + let all_vars = (0..field_tys.len()).map(GeneratorField::from).collect(); let empty_variants = iter::repeat(IndexVec::new()).take(3); - let state_variants = iter::repeat(vars).take(suspending_blocks.count()); + let state_variants = iter::repeat(all_vars).take(suspending_blocks.count()); let layout = GeneratorLayout { - variant_fields: empty_variants.chain(state_variants).collect() + field_tys, + variant_fields: empty_variants.chain(state_variants).collect(), + __local_debuginfo_codegen_only_do_not_use: decls, }; (remap, layout, storage_liveness) From 77a6d29f483c6a1925a90f7e54b0d5721806d8fa Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 3 May 2019 16:11:39 -0700 Subject: [PATCH 13/13] Address review comments --- src/librustc/mir/mod.rs | 10 ++++----- src/librustc/ty/layout.rs | 12 ++++++++--- src/librustc/ty/sty.rs | 18 +++++++--------- .../debuginfo/metadata.rs | 10 ++++----- src/librustc_codegen_llvm/type_of.rs | 4 ++-- .../borrow_check/nll/type_check/mod.rs | 21 ++----------------- src/librustc_mir/transform/deaggregator.rs | 2 +- src/librustc_mir/transform/generator.rs | 5 +++-- src/test/debuginfo/generator-objects.rs | 8 +++---- 9 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9c71e694f4c79..e77315f06a952 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2995,7 +2995,7 @@ pub struct UnsafetyCheckResult { } newtype_index! { - pub struct GeneratorField { + pub struct GeneratorSavedLocal { derive [HashStable] DEBUG_FORMAT = "_{}", } @@ -3005,18 +3005,18 @@ newtype_index! { #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct GeneratorLayout<'tcx> { /// The type of every local stored inside the generator. - pub field_tys: IndexVec>, + pub field_tys: IndexVec>, /// Which of the above fields are in each variant. Note that one field may /// be stored in multiple variants. - pub variant_fields: IndexVec>, + pub variant_fields: IndexVec>, /// 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>, + pub __local_debuginfo_codegen_only_do_not_use: IndexVec>, } #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] @@ -3582,7 +3582,7 @@ impl<'tcx> TypeFoldable<'tcx> for Field { } } -impl<'tcx> TypeFoldable<'tcx> for GeneratorField { +impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self { *self } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 10afaebc91a91..1dbb5a8f0bff6 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -605,6 +605,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } ty::Generator(def_id, ref substs, _) => { + // 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))); @@ -1691,7 +1697,7 @@ 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 { + let discr_layout = |discr: &Scalar| -> C::TyLayout { let layout = LayoutDetails::scalar(cx, discr.clone()); MaybeResult::from_ok(TyLayout { details: tcx.intern_layout(layout), @@ -1781,7 +1787,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx> } Variants::Multiple { ref discr, discr_index, .. } => { if i == discr_index { - return handle_discriminant(discr); + return discr_layout(discr); } substs.prefix_tys(def_id, tcx).nth(i).unwrap() } @@ -1805,7 +1811,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); - return handle_discriminant(discr); + return discr_layout(discr); } } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index a41209496bd27..44e452d8373e3 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -16,6 +16,7 @@ use crate::util::captures::Captures; use crate::mir::interpret::{Scalar, Pointer}; use smallvec::SmallVec; +use std::borrow::Cow; use std::cmp::Ordering; use std::marker::PhantomData; use std::ops::Range; @@ -513,16 +514,13 @@ impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> { /// Calls `f` with a reference to the name of the enumerator for the given /// variant `v`. #[inline] - pub fn map_variant_name(&self, v: VariantIdx, f: impl FnOnce(&str) -> R) -> R { - 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())); - } - }; - f(name) + 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. diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 5b18e821982bd..4ab197c64604f 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1575,7 +1575,7 @@ impl<'tcx> VariantInfo<'tcx> { match self { VariantInfo::Adt(variant) => f(&variant.ident.as_str()), VariantInfo::Generator(substs, _, variant_index) => - substs.map_variant_name(*variant_index, f), + f(&substs.variant_name(*variant_index)), } } @@ -1720,16 +1720,16 @@ fn prepare_enum_metadata( .collect(), ty::Generator(_, substs, _) => substs .variant_range(enum_def_id, cx.tcx) - .map(|v| substs.map_variant_name(v, |name| { - let name = SmallCStr::new(name); + .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? - v.as_usize() as u64)) + variant_index.as_usize() as u64)) } - })) + }) .collect(), _ => bug!(), }; diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs index 080f78ff112ad..cbcc457fda9a2 100644 --- a/src/librustc_codegen_llvm/type_of.rs +++ b/src/librustc_codegen_llvm/type_of.rs @@ -63,10 +63,10 @@ fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, write!(&mut name, "::{}", def.variants[index].ident).unwrap(); } } - if let (&ty::Generator(..), &layout::Variants::Single { index }) + if let (&ty::Generator(_, substs, _), &layout::Variants::Single { index }) = (&layout.ty.sty, &layout.variants) { - write!(&mut name, "::variant#{:?}", index).unwrap(); + write!(&mut name, "::{}", substs.variant_name(index)).unwrap(); } Some(name) } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 94900b98a52d5..a076f4aab9de1 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -684,25 +684,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } } - ty::Generator(def_id, substs, _) => { - let variants = substs.state_tys(def_id, tcx).count(); - if index.as_usize() >= variants { - PlaceTy::from_ty( - span_mirbug_and_err!( - self, - place, - "cast to variant #{:?} but generator only has {:?}", - index, - variants - ), - ) - } else { - PlaceTy { - ty: base_ty, - variant_index: Some(index), - } - } - } + // 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!( diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index 9f8d40bf4cdd5..9215356daa484 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -58,7 +58,7 @@ impl MirPass for Deaggregator { } AggregateKind::Generator(..) => { // Right now we only support initializing generators to - // variant#0. + // variant 0 (Unresumed). let variant_index = VariantIdx::new(0); set_discriminant = Some(Statement { kind: StatementKind::SetDiscriminant { diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 77bf789d2dd16..e35787a98719c 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -561,12 +561,13 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, remap.insert(local, (var.ty, variant_index, idx)); decls.push(var); } - let field_tys = decls.iter().map(|field| field.ty).collect::>(); + let field_tys = decls.iter().map(|field| field.ty).collect::>(); // Put every var in each variant, for now. - let all_vars = (0..field_tys.len()).map(GeneratorField::from).collect(); + 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 { field_tys, variant_fields: empty_variants.chain(state_variants).collect(), diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index 682c213f33244..c6f98e5782b1f 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -10,16 +10,16 @@ // gdb-command:run // gdb-command:print b -// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <>: {__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::variant#3 {[...]}, 4: generator_objects::main::generator::variant#4 {[...]}}} +// gdb-check:$1 = generator_objects::main::generator {__0: 0x[...], <>: {__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[...], <>: {__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::variant#3 {c: 6, d: 7}, 4: generator_objects::main::generator::variant#4 {[...]}}} +// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <>: {__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[...], <>: {__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::variant#3 {[...]}, 4: generator_objects::main::generator::variant#4 {c: 7, d: 8}}} +// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <>: {__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[...], <>: {__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::variant#3 {[...]}, 4: generator_objects::main::generator::variant#4 {[...]}}} +// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <>: {__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 ==================================================================================