about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-05-04 03:18:14 +0000
committerbors <bors@rust-lang.org>2019-05-04 03:18:14 +0000
commite2326366935613816927e679d3b2dc04db44678c (patch)
tree9d9b3fb6ed6d688cf60a74b6072b325e8abb5872 /src
parent13fde05b12c28e1ed66bd13fdf1ea392f166b811 (diff)
parent77a6d29f483c6a1925a90f7e54b0d5721806d8fa (diff)
Auto merge of #59897 - tmandry:variantful-generators, r=eddyb
Multi-variant layouts for generators

This allows generators to overlap fields using variants, but doesn't do any such overlapping yet. It creates one variant for every state of the generator (unresumed, returned, panicked, plus one for every yield), and puts every stored local in each of the yield-point variants.

Required for optimizing generator layouts (#52924).

There was quite a lot of refactoring needed for this change. I've done my best in later commits to eliminate assumptions in the code that only certain kinds of types are multi-variant, and to centralize knowledge of the inner mechanics of generators in as few places as possible.

This change also emits debuginfo about the fields contained in each variant, as well as preserving debuginfo about stored locals while running in the generator.

Also, fixes #59972.

Future work:
- Use this change for an optimization pass that actually overlaps locals within the generator struct (#52924)
- In the type layout fields, don't include locals that are uninitialized for a particular variant, so miri and UB sanitizers can check our memory (see https://github.com/rust-lang/rust/issues/59972#issuecomment-483058172)
- Preserve debuginfo scopes across generator yield points
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/mod.rs43
-rw-r--r--src/librustc/mir/tcx.rs12
-rw-r--r--src/librustc/ty/layout.rs87
-rw-r--r--src/librustc/ty/mod.rs7
-rw-r--r--src/librustc/ty/sty.rs145
-rw-r--r--src/librustc_codegen_llvm/debuginfo/metadata.rs283
-rw-r--r--src/librustc_codegen_llvm/type_of.rs5
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs51
-rw-r--r--src/librustc_codegen_ssa/mir/place.rs10
-rw-r--r--src/librustc_codegen_ssa/mir/rvalue.rs11
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs60
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs18
-rw-r--r--src/librustc_mir/interpret/cast.rs9
-rw-r--r--src/librustc_mir/interpret/operand.rs19
-rw-r--r--src/librustc_mir/interpret/place.rs8
-rw-r--r--src/librustc_mir/transform/deaggregator.rs20
-rw-r--r--src/librustc_mir/transform/generator.rs144
-rw-r--r--src/librustc_mir/util/liveness.rs3
-rw-r--r--src/test/debuginfo/generator-locals.rs87
-rw-r--r--src/test/debuginfo/generator-objects.rs68
-rw-r--r--src/test/debuginfo/generators.rs42
-rw-r--r--src/test/mir-opt/generator-drop-cleanup.rs3
22 files changed, 804 insertions, 331 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 0dc23f5ce47..82a28716f5c 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2030,6 +2030,10 @@ impl<'tcx> Place<'tcx> {
             variant_index))
     }
 
+    pub fn downcast_unnamed(self, variant_index: VariantIdx) -> Place<'tcx> {
+        self.elem(ProjectionElem::Downcast(None, variant_index))
+    }
+
     pub fn index(self, index: Local) -> Place<'tcx> {
         self.elem(ProjectionElem::Index(index))
     }
@@ -2589,11 +2593,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                                     let var_name = tcx.hir().name_by_hir_id(freevar.var_id());
                                     struct_fmt.field(&var_name.as_str(), place);
                                 }
-                                struct_fmt.field("$state", &places[freevars.len()]);
-                                for i in (freevars.len() + 1)..places.len() {
-                                    struct_fmt
-                                        .field(&format!("${}", i - freevars.len() - 1), &places[i]);
-                                }
                             });
 
                             struct_fmt.finish()
@@ -3031,10 +3030,29 @@ pub struct UnsafetyCheckResult {
     pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
 }
 
+newtype_index! {
+    pub struct GeneratorSavedLocal {
+        derive [HashStable]
+        DEBUG_FORMAT = "_{}",
+    }
+}
+
 /// The layout of generator state
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
 pub struct GeneratorLayout<'tcx> {
-    pub fields: Vec<LocalDecl<'tcx>>,
+    /// The type of every local stored inside the generator.
+    pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>,
+
+    /// Which of the above fields are in each variant. Note that one field may
+    /// be stored in multiple variants.
+    pub variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>>,
+
+    /// Names and scopes of all the stored generator locals.
+    /// NOTE(tmandry) This is *strictly* a temporary hack for codegen
+    /// debuginfo generation, and will be removed at some point.
+    /// Do **NOT** use it for anything else, local information should not be
+    /// in the MIR, please rely on local crate HIR or other side-channels.
+    pub __local_debuginfo_codegen_only_do_not_use: IndexVec<GeneratorSavedLocal, LocalDecl<'tcx>>,
 }
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
@@ -3223,7 +3241,9 @@ BraceStructTypeFoldableImpl! {
 
 BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
-        fields
+        field_tys,
+        variant_fields,
+        __local_debuginfo_codegen_only_do_not_use,
     }
 }
 
@@ -3598,6 +3618,15 @@ impl<'tcx> TypeFoldable<'tcx> for Field {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
+        *self
+    }
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
+        false
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         Constant {
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 5d1dca5a1f4..d3fa5e84b6a 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 c67a57182cb..21e4d8b07a1 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -604,12 +604,63 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                 tcx.intern_layout(unit)
             }
 
-            // Tuples, generators and closures.
             ty::Generator(def_id, ref substs, _) => {
-                let tys = substs.field_tys(def_id, tcx);
-                univariant(&tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
+                // FIXME(tmandry): For fields that are repeated in multiple
+                // variants in the GeneratorLayout, we need code to ensure that
+                // the offset of these fields never change. Right now this is
+                // not an issue since every variant has every field, but once we
+                // optimize this we have to be more careful.
+
+                let discr_index = substs.prefix_tys(def_id, tcx).count();
+                let prefix_tys = substs.prefix_tys(def_id, tcx)
+                    .chain(iter::once(substs.discr_ty(tcx)));
+                let prefix = univariant_uninterned(
+                    &prefix_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
                     &ReprOptions::default(),
-                    StructKind::AlwaysSized)?
+                    StructKind::AlwaysSized)?;
+
+                let mut size = prefix.size;
+                let mut align = prefix.align;
+                let variants_tys = substs.state_tys(def_id, tcx);
+                let variants = variants_tys.enumerate().map(|(i, variant_tys)| {
+                    let mut variant = univariant_uninterned(
+                        &variant_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
+                        &ReprOptions::default(),
+                        StructKind::Prefixed(prefix.size, prefix.align.abi))?;
+
+                    variant.variants = Variants::Single { index: VariantIdx::new(i) };
+
+                    size = size.max(variant.size);
+                    align = align.max(variant.align);
+
+                    Ok(variant)
+                }).collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
+
+                let abi = if prefix.abi.is_uninhabited() ||
+                             variants.iter().all(|v| v.abi.is_uninhabited()) {
+                    Abi::Uninhabited
+                } else {
+                    Abi::Aggregate { sized: true }
+                };
+                let discr = match &self.layout_of(substs.discr_ty(tcx))?.abi {
+                    Abi::Scalar(s) => s.clone(),
+                    _ => bug!(),
+                };
+
+                let layout = tcx.intern_layout(LayoutDetails {
+                    variants: Variants::Multiple {
+                        discr,
+                        discr_kind: DiscriminantKind::Tag,
+                        discr_index,
+                        variants,
+                    },
+                    fields: prefix.fields,
+                    abi,
+                    size,
+                    align,
+                });
+                debug!("generator layout: {:#?}", layout);
+                layout
             }
 
             ty::Closure(def_id, ref substs) => {
@@ -1647,6 +1698,14 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
 
     fn field(this: TyLayout<'tcx>, cx: &C, i: usize) -> C::TyLayout {
         let tcx = cx.tcx();
+        let discr_layout = |discr: &Scalar| -> C::TyLayout {
+            let layout = LayoutDetails::scalar(cx, discr.clone());
+            MaybeResult::from_ok(TyLayout {
+                details: tcx.intern_layout(layout),
+                ty: discr.value.to_ty(tcx)
+            })
+        };
+
         cx.layout_of(match this.ty.sty {
             ty::Bool |
             ty::Char |
@@ -1721,7 +1780,19 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
             }
 
             ty::Generator(def_id, ref substs, _) => {
-                substs.field_tys(def_id, tcx).nth(i).unwrap()
+                match this.variants {
+                    Variants::Single { index } => {
+                        substs.state_tys(def_id, tcx)
+                            .nth(index.as_usize()).unwrap()
+                            .nth(i).unwrap()
+                    }
+                    Variants::Multiple { ref discr, discr_index, .. } => {
+                        if i == discr_index {
+                            return discr_layout(discr);
+                        }
+                        substs.prefix_tys(def_id, tcx).nth(i).unwrap()
+                    }
+                }
             }
 
             ty::Tuple(tys) => tys[i].expect_ty(),
@@ -1741,11 +1812,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
                     // Discriminant field for enums (where applicable).
                     Variants::Multiple { ref discr, .. } => {
                         assert_eq!(i, 0);
-                        let layout = LayoutDetails::scalar(cx, discr.clone());
-                        return MaybeResult::from_ok(TyLayout {
-                            details: tcx.intern_layout(layout),
-                            ty: discr.value.to_ty(tcx)
-                        });
+                        return discr_layout(discr);
                     }
                 }
             }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index ffd34c7cdc3..feedf5741f6 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -43,6 +43,7 @@ use std::ops::Deref;
 use rustc_data_structures::sync::{self, Lrc, ParallelIterator, par_iter};
 use std::slice;
 use std::{mem, ptr};
+use std::ops::Range;
 use syntax::ast::{self, Name, Ident, NodeId};
 use syntax::attr;
 use syntax::ext::hygiene::Mark;
@@ -2416,11 +2417,17 @@ impl<'a, 'gcx, 'tcx> AdtDef {
         })
     }
 
+    #[inline]
+    pub fn variant_range(&self) -> Range<VariantIdx> {
+        (VariantIdx::new(0)..VariantIdx::new(self.variants.len()))
+    }
+
     /// Computes the discriminant value used by a specific variant.
     /// Unlike `discriminants`, this is (amortized) constant-time,
     /// only doing at most one query for evaluating an explicit
     /// discriminant (the last one before the requested variant),
     /// assuming there are no constant-evaluation errors there.
+    #[inline]
     pub fn discriminant_for_variant(&self,
                                     tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                     variant_index: VariantIdx)
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 1e8f3ebd0dd..cca40c379fc 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -9,15 +9,17 @@ use polonius_engine::Atom;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_macros::HashStable;
 use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, Kind, UnpackedKind};
-use crate::ty::{self, AdtDef, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
 use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv};
+use crate::ty::layout::VariantIdx;
 use crate::util::captures::Captures;
 use crate::mir::interpret::{Scalar, Pointer};
 
 use smallvec::SmallVec;
-use std::iter;
+use std::borrow::Cow;
 use std::cmp::Ordering;
 use std::marker::PhantomData;
+use std::ops::Range;
 use rustc_target::spec::abi;
 use syntax::ast::{self, Ident};
 use syntax::symbol::{keywords, InternedString};
@@ -298,14 +300,10 @@ static_assert!(MEM_SIZE_OF_TY_KIND: ::std::mem::size_of::<TyKind<'_>>() == 24);
 ///
 /// ## Generators
 ///
-/// Perhaps surprisingly, `ClosureSubsts` are also used for
-/// generators. In that case, what is written above is only half-true
-/// -- the set of type parameters is similar, but the role of CK and
-/// CS are different. CK represents the "yield type" and CS
-/// represents the "return type" of the generator.
-///
-/// It'd be nice to split this struct into ClosureSubsts and
-/// GeneratorSubsts, I believe. -nmatsakis
+/// Generators are handled similarly in `GeneratorSubsts`.  The set of
+/// type parameters is similar, but the role of CK and CS are
+/// different. CK represents the "yield type" and CS represents the
+/// "return type" of the generator.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
          Debug, RustcEncodable, RustcDecodable, HashStable)]
 pub struct ClosureSubsts<'tcx> {
@@ -391,6 +389,7 @@ impl<'tcx> ClosureSubsts<'tcx> {
     }
 }
 
+/// Similar to `ClosureSubsts`; see the above documentation for more.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug,
          RustcEncodable, RustcDecodable, HashStable)]
 pub struct GeneratorSubsts<'tcx> {
@@ -470,33 +469,91 @@ impl<'tcx> GeneratorSubsts<'tcx> {
 }
 
 impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> {
+    /// Generator have not been resumed yet
+    pub const UNRESUMED: usize = 0;
+    /// Generator has returned / is completed
+    pub const RETURNED: usize = 1;
+    /// Generator has been poisoned
+    pub const POISONED: usize = 2;
+
+    const UNRESUMED_NAME: &'static str = "Unresumed";
+    const RETURNED_NAME: &'static str = "Returned";
+    const POISONED_NAME: &'static str = "Panicked";
+
+    /// The valid variant indices of this Generator.
+    #[inline]
+    pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Range<VariantIdx> {
+        // FIXME requires optimized MIR
+        let num_variants = tcx.generator_layout(def_id).variant_fields.len();
+        (VariantIdx::new(0)..VariantIdx::new(num_variants))
+    }
+
+    /// The discriminant for the given variant. Panics if the variant_index is
+    /// out of range.
+    #[inline]
+    pub fn discriminant_for_variant(
+        &self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: VariantIdx
+    ) -> Discr<'tcx> {
+        // Generators don't support explicit discriminant values, so they are
+        // the same as the variant index.
+        assert!(self.variant_range(def_id, tcx).contains(&variant_index));
+        Discr { val: variant_index.as_usize() as u128, ty: self.discr_ty(tcx) }
+    }
+
+    /// The set of all discriminants for the Generator, enumerated with their
+    /// variant indices.
+    #[inline]
+    pub fn discriminants(
+        &'a self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>
+    ) -> impl Iterator<Item=(VariantIdx, Discr<'tcx>)> + Captures<'gcx> + 'a {
+        self.variant_range(def_id, tcx).map(move |index| {
+            (index, Discr { val: index.as_usize() as u128, ty: self.discr_ty(tcx) })
+        })
+    }
+
+    /// Calls `f` with a reference to the name of the enumerator for the given
+    /// variant `v`.
+    #[inline]
+    pub fn variant_name(&self, v: VariantIdx) -> Cow<'static, str> {
+        match v.as_usize() {
+            Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME),
+            Self::RETURNED => Cow::from(Self::RETURNED_NAME),
+            Self::POISONED => Cow::from(Self::POISONED_NAME),
+            _ => Cow::from(format!("Suspend{}", v.as_usize() - 3))
+        }
+    }
+
+    /// The type of the state discriminant used in the generator type.
+    #[inline]
+    pub fn discr_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
+        tcx.types.u32
+    }
+
     /// This returns the types of the MIR locals which had to be stored across suspension points.
     /// It is calculated in rustc_mir::transform::generator::StateTransform.
     /// All the types here must be in the tuple in GeneratorInterior.
-    pub fn state_tys(
-        self,
-        def_id: DefId,
-        tcx: TyCtxt<'a, 'gcx, 'tcx>,
-    ) -> impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a {
-        let state = tcx.generator_layout(def_id).fields.iter();
-        state.map(move |d| d.ty.subst(tcx, self.substs))
-    }
-
-    /// This is the types of the fields of a generate which
-    /// is available before the generator transformation.
-    /// It includes the upvars and the state discriminant which is u32.
-    pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
-        impl Iterator<Item=Ty<'tcx>> + 'a
+    ///
+    /// The locals are grouped by their variant number. Note that some locals may
+    /// be repeated in multiple variants.
+    #[inline]
+    pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
+        impl Iterator<Item=impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a>
     {
-        self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
+        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 all the fields stored in a generator.
-    /// It includes the upvars, state types and the state discriminant which is u32.
-    pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
-        impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a
+    /// This is the types of the fields of a generator which are not stored in a
+    /// variant.
+    #[inline]
+    pub fn prefix_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
+        impl Iterator<Item=Ty<'tcx>> + 'a
     {
-        self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
+        self.upvar_tys(def_id, tcx)
     }
 }
 
@@ -1996,6 +2053,34 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    /// If the type contains variants, returns the valid range of variant indices.
+    /// FIXME This requires the optimized MIR in the case of generators.
+    #[inline]
+    pub fn variant_range(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Range<VariantIdx>> {
+        match self.sty {
+            TyKind::Adt(adt, _) => Some(adt.variant_range()),
+            TyKind::Generator(def_id, substs, _) => Some(substs.variant_range(def_id, tcx)),
+            _ => None,
+        }
+    }
+
+    /// If the type contains variants, returns the variant for `variant_index`.
+    /// Panics if `variant_index` is out of range.
+    /// FIXME This requires the optimized MIR in the case of generators.
+    #[inline]
+    pub fn discriminant_for_variant(
+        &self,
+        tcx: TyCtxt<'a, 'gcx, 'tcx>,
+        variant_index: VariantIdx
+    ) -> Option<Discr<'tcx>> {
+        match self.sty {
+            TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)),
+            TyKind::Generator(def_id, substs, _) =>
+                Some(substs.discriminant_for_variant(def_id, tcx, variant_index)),
+            _ => None,
+        }
+    }
+
     /// Push onto `out` the regions directly referenced from this type (but not
     /// types reachable from this type via `walk_tys`). This ignores late-bound
     /// regions binders.
diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs
index 0d67865d717..9bdce37b8ba 100644
--- a/src/librustc_codegen_llvm/debuginfo/metadata.rs
+++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -23,12 +23,13 @@ use rustc::hir::def::CtorKind;
 use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
 use rustc::ich::NodeIdHashingMode;
 use rustc::mir::Field;
+use rustc::mir::GeneratorLayout;
 use rustc::mir::interpret::truncate;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc::ty::Instance;
 use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
 use rustc::ty::layout::{self, Align, Integer, IntegerExt, LayoutOf,
-                        PrimitiveExt, Size, TyLayout};
+                        PrimitiveExt, Size, TyLayout, VariantIdx};
 use rustc::ty::subst::UnpackedKind;
 use rustc::session::config;
 use rustc::util::nodemap::FxHashMap;
@@ -691,14 +692,15 @@ pub fn type_metadata(
                                    usage_site_span).finalize(cx)
         }
         ty::Generator(def_id, substs,  _) => {
-            let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| {
+            let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| {
                 cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)
             }).collect();
-            prepare_tuple_metadata(cx,
-                                   t,
-                                   &upvar_tys,
-                                   unique_type_id,
-                                   usage_site_span).finalize(cx)
+            prepare_enum_metadata(cx,
+                                  t,
+                                  def_id,
+                                  unique_type_id,
+                                  usage_site_span,
+                                  upvar_tys).finalize(cx)
         }
         ty::Adt(def, ..) => match def.adt_kind() {
             AdtKind::Struct => {
@@ -718,7 +720,8 @@ pub fn type_metadata(
                                       t,
                                       def.did,
                                       unique_type_id,
-                                      usage_site_span).finalize(cx)
+                                      usage_site_span,
+                                      vec![]).finalize(cx)
             }
         },
         ty::Tuple(ref elements) => {
@@ -1000,6 +1003,31 @@ struct MemberDescription<'ll> {
     discriminant: Option<u64>,
 }
 
+impl<'ll> MemberDescription<'ll> {
+    fn into_metadata(self,
+                     cx: &CodegenCx<'ll, '_>,
+                     composite_type_metadata: &'ll DIScope) -> &'ll DIType {
+        let member_name = CString::new(self.name).unwrap();
+        unsafe {
+            llvm::LLVMRustDIBuilderCreateVariantMemberType(
+                DIB(cx),
+                composite_type_metadata,
+                member_name.as_ptr(),
+                unknown_file_metadata(cx),
+                UNKNOWN_LINE_NUMBER,
+                self.size.bits(),
+                self.align.bits() as u32,
+                self.offset.bits(),
+                match self.discriminant {
+                    None => None,
+                    Some(value) => Some(cx.const_u64(value)),
+                },
+                self.flags,
+                self.type_metadata)
+        }
+    }
+}
+
 // A factory for MemberDescriptions. It produces a list of member descriptions
 // for some record-like type. MemberDescriptionFactories are used to defer the
 // creation of type member descriptions in order to break cycles arising from
@@ -1266,7 +1294,16 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> {
 impl EnumMemberDescriptionFactory<'ll, 'tcx> {
     fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>)
                                   -> Vec<MemberDescription<'ll>> {
-        let adt = &self.enum_type.ty_adt_def().unwrap();
+        let variant_info_for = |index: VariantIdx| {
+            match &self.enum_type.sty {
+                ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]),
+                ty::Generator(def_id, substs, _) => {
+                    let generator_layout = cx.tcx.generator_layout(*def_id);
+                    VariantInfo::Generator(*substs, generator_layout, index)
+                }
+                _ => bug!(),
+            }
+        };
 
         // This will always find the metadata in the type map.
         let fallback = use_enum_fallback(cx);
@@ -1277,12 +1314,18 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
         };
 
         match self.layout.variants {
-            layout::Variants::Single { .. } if adt.variants.is_empty() => vec![],
             layout::Variants::Single { index } => {
+                if let ty::Adt(adt, _) = &self.enum_type.sty {
+                    if adt.variants.is_empty() {
+                        return vec![];
+                    }
+                }
+
+                let variant_info = variant_info_for(index);
                 let (variant_type_metadata, member_description_factory) =
                     describe_enum_variant(cx,
                                           self.layout,
-                                          &adt.variants[index],
+                                          variant_info,
                                           NoDiscriminant,
                                           self_metadata,
                                           self.span);
@@ -1299,7 +1342,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                         name: if fallback {
                             String::new()
                         } else {
-                            adt.variants[index].ident.as_str().to_string()
+                            variant_info.variant_name()
                         },
                         type_metadata: variant_type_metadata,
                         offset: Size::ZERO,
@@ -1327,10 +1370,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                 };
                 variants.iter_enumerated().map(|(i, _)| {
                     let variant = self.layout.for_variant(cx, i);
+                    let variant_info = variant_info_for(i);
                     let (variant_type_metadata, member_desc_factory) =
                         describe_enum_variant(cx,
                                               variant,
-                                              &adt.variants[i],
+                                              variant_info,
                                               discriminant_info,
                                               self_metadata,
                                               self.span);
@@ -1342,20 +1386,21 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                                                   self.enum_type,
                                                   variant_type_metadata,
                                                   member_descriptions);
+
                     MemberDescription {
                         name: if fallback {
                             String::new()
                         } else {
-                            adt.variants[i].ident.as_str().to_string()
+                            variant_info.variant_name()
                         },
                         type_metadata: variant_type_metadata,
                         offset: Size::ZERO,
                         size: self.layout.size,
                         align: self.layout.align.abi,
                         flags: DIFlags::FlagZero,
-                        discriminant: Some(self.layout.ty.ty_adt_def().unwrap()
-                                           .discriminant_for_variant(cx.tcx, i)
-                                           .val as u64),
+                        discriminant: Some(
+                            self.layout.ty.discriminant_for_variant(cx.tcx, i).unwrap().val as u64
+                        ),
                     }
                 }).collect()
             }
@@ -1375,7 +1420,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                     let (variant_type_metadata, member_description_factory) =
                         describe_enum_variant(cx,
                                               variant,
-                                              &adt.variants[dataful_variant],
+                                              variant_info_for(dataful_variant),
                                               OptimizedDiscriminant,
                                               self.containing_scope,
                                               self.span);
@@ -1415,7 +1460,9 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                                        self.layout,
                                        self.layout.fields.offset(discr_index),
                                        self.layout.field(cx, discr_index).size);
-                    name.push_str(&adt.variants[*niche_variants.start()].ident.as_str());
+                    variant_info_for(*niche_variants.start()).map_struct_name(|variant_name| {
+                        name.push_str(variant_name);
+                    });
 
                     // Create the (singleton) list of descriptions of union members.
                     vec![
@@ -1432,10 +1479,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                 } else {
                     variants.iter_enumerated().map(|(i, _)| {
                         let variant = self.layout.for_variant(cx, i);
+                        let variant_info = variant_info_for(i);
                         let (variant_type_metadata, member_desc_factory) =
                             describe_enum_variant(cx,
                                                   variant,
-                                                  &adt.variants[i],
+                                                  variant_info,
                                                   OptimizedDiscriminant,
                                                   self_metadata,
                                                   self.span);
@@ -1463,7 +1511,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                         };
 
                         MemberDescription {
-                            name: adt.variants[i].ident.as_str().to_string(),
+                            name: variant_info.variant_name(),
                             type_metadata: variant_type_metadata,
                             offset: Size::ZERO,
                             size: self.layout.size,
@@ -1521,6 +1569,49 @@ enum EnumDiscriminantInfo<'ll> {
     NoDiscriminant
 }
 
+#[derive(Copy, Clone)]
+enum VariantInfo<'tcx> {
+    Adt(&'tcx ty::VariantDef),
+    Generator(ty::GeneratorSubsts<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx),
+}
+
+impl<'tcx> VariantInfo<'tcx> {
+    fn map_struct_name<R>(&self, f: impl FnOnce(&str) -> R) -> R {
+        match self {
+            VariantInfo::Adt(variant) => f(&variant.ident.as_str()),
+            VariantInfo::Generator(substs, _, variant_index) =>
+                f(&substs.variant_name(*variant_index)),
+        }
+    }
+
+    fn variant_name(&self) -> String {
+        match self {
+            VariantInfo::Adt(variant) => variant.ident.to_string(),
+            VariantInfo::Generator(_, _, variant_index) => {
+                // Since GDB currently prints out the raw discriminant along
+                // with every variant, make each variant name be just the value
+                // of the discriminant. The struct name for the variant includes
+                // the actual variant description.
+                format!("{}", variant_index.as_usize()).to_string()
+            }
+        }
+    }
+
+    fn field_name(&self, i: usize) -> String {
+        let field_name = match self {
+            VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn =>
+                Some(variant.fields[i].ident.to_string()),
+            VariantInfo::Generator(_, generator_layout, variant_index) => {
+                let field = generator_layout.variant_fields[*variant_index][i.into()];
+                let decl = &generator_layout.__local_debuginfo_codegen_only_do_not_use[field];
+                decl.name.map(|name| name.to_string())
+            }
+            _ => None,
+        };
+        field_name.unwrap_or_else(|| format!("__{}", i))
+    }
+}
+
 // Returns a tuple of (1) type_metadata_stub of the variant, (2) a
 // MemberDescriptionFactory for producing the descriptions of the
 // fields of the variant. This is a rudimentary version of a full
@@ -1528,32 +1619,24 @@ enum EnumDiscriminantInfo<'ll> {
 fn describe_enum_variant(
     cx: &CodegenCx<'ll, 'tcx>,
     layout: layout::TyLayout<'tcx>,
-    variant: &'tcx ty::VariantDef,
+    variant: VariantInfo<'tcx>,
     discriminant_info: EnumDiscriminantInfo<'ll>,
     containing_scope: &'ll DIScope,
     span: Span,
 ) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) {
-    let variant_name = variant.ident.as_str();
-    let unique_type_id = debug_context(cx).type_map
-                                          .borrow_mut()
-                                          .get_unique_type_id_of_enum_variant(
-                                              cx,
-                                              layout.ty,
-                                              &variant_name);
-
-    let metadata_stub = create_struct_stub(cx,
-                                           layout.ty,
-                                           &variant_name,
-                                           unique_type_id,
-                                           Some(containing_scope));
-
-    let arg_name = |i: usize| {
-        if variant.ctor_kind == CtorKind::Fn {
-            format!("__{}", i)
-        } else {
-            variant.fields[i].ident.to_string()
-        }
-    };
+    let metadata_stub = variant.map_struct_name(|variant_name| {
+        let unique_type_id = debug_context(cx).type_map
+                                              .borrow_mut()
+                                              .get_unique_type_id_of_enum_variant(
+                                                  cx,
+                                                  layout.ty,
+                                                  &variant_name);
+        create_struct_stub(cx,
+                           layout.ty,
+                           &variant_name,
+                           unique_type_id,
+                           Some(containing_scope))
+    });
 
     // Build an array of (field name, field type) pairs to be captured in the factory closure.
     let (offsets, args) = if use_enum_fallback(cx) {
@@ -1575,7 +1658,7 @@ fn describe_enum_variant(
                 layout.fields.offset(i)
             })).collect(),
             discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| {
-                (arg_name(i), layout.field(cx, i).ty)
+                (variant.field_name(i), layout.field(cx, i).ty)
             })).collect()
         )
     } else {
@@ -1584,7 +1667,7 @@ fn describe_enum_variant(
                 layout.fields.offset(i)
             }).collect(),
             (0..layout.fields.count()).map(|i| {
-                (arg_name(i), layout.field(cx, i).ty)
+                (variant.field_name(i), layout.field(cx, i).ty)
             }).collect()
         )
     };
@@ -1611,6 +1694,7 @@ fn prepare_enum_metadata(
     enum_def_id: DefId,
     unique_type_id: UniqueTypeId,
     span: Span,
+    outer_field_tys: Vec<Ty<'tcx>>,
 ) -> RecursiveTypeDescription<'ll, 'tcx> {
     let enum_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
 
@@ -1624,20 +1708,36 @@ fn prepare_enum_metadata(
     let file_metadata = unknown_file_metadata(cx);
 
     let discriminant_type_metadata = |discr: layout::Primitive| {
-        let def = enum_type.ty_adt_def().unwrap();
-        let enumerators_metadata: Vec<_> = def.discriminants(cx.tcx)
-            .zip(&def.variants)
-            .map(|((_, discr), v)| {
-                let name = SmallCStr::new(&v.ident.as_str());
-                unsafe {
-                    Some(llvm::LLVMRustDIBuilderCreateEnumerator(
-                        DIB(cx),
-                        name.as_ptr(),
-                        // FIXME: what if enumeration has i128 discriminant?
-                        discr.val as u64))
-                }
-            })
-            .collect();
+        let enumerators_metadata: Vec<_> = match enum_type.sty {
+            ty::Adt(def, _) => def
+                .discriminants(cx.tcx)
+                .zip(&def.variants)
+                .map(|((_, discr), v)| {
+                    let name = SmallCStr::new(&v.ident.as_str());
+                    unsafe {
+                        Some(llvm::LLVMRustDIBuilderCreateEnumerator(
+                            DIB(cx),
+                            name.as_ptr(),
+                            // FIXME: what if enumeration has i128 discriminant?
+                            discr.val as u64))
+                    }
+                })
+                .collect(),
+            ty::Generator(_, substs, _) => substs
+                .variant_range(enum_def_id, cx.tcx)
+                .map(|variant_index| {
+                    let name = SmallCStr::new(&substs.variant_name(variant_index));
+                    unsafe {
+                        Some(llvm::LLVMRustDIBuilderCreateEnumerator(
+                            DIB(cx),
+                            name.as_ptr(),
+                            // FIXME: what if enumeration has i128 discriminant?
+                            variant_index.as_usize() as u64))
+                    }
+                })
+                .collect(),
+            _ => bug!(),
+        };
 
         let disr_type_key = (enum_def_id, discr);
         let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types
@@ -1650,14 +1750,18 @@ fn prepare_enum_metadata(
                     (discr.size(cx), discr.align(cx));
                 let discriminant_base_type_metadata =
                     type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP);
-                let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str();
 
-                let name = SmallCStr::new(&discriminant_name);
+                let discriminant_name = match enum_type.sty {
+                    ty::Adt(..) => SmallCStr::new(&cx.tcx.item_name(enum_def_id).as_str()),
+                    ty::Generator(..) => SmallCStr::new(&enum_name),
+                    _ => bug!(),
+                };
+
                 let discriminant_type_metadata = unsafe {
                     llvm::LLVMRustDIBuilderCreateEnumerationType(
                         DIB(cx),
                         containing_scope,
-                        name.as_ptr(),
+                        discriminant_name.as_ptr(),
                         file_metadata,
                         UNKNOWN_LINE_NUMBER,
                         discriminant_size.bits(),
@@ -1738,6 +1842,11 @@ fn prepare_enum_metadata(
         );
     }
 
+    let discriminator_name = match &enum_type.sty {
+        ty::Generator(..) => Some(SmallCStr::new(&"__state")),
+        _ => None,
+    };
+    let discriminator_name = discriminator_name.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut());
     let discriminator_metadata = match layout.variants {
         // A single-variant enum has no discriminant.
         layout::Variants::Single { .. } => None,
@@ -1764,7 +1873,7 @@ fn prepare_enum_metadata(
                 Some(llvm::LLVMRustDIBuilderCreateMemberType(
                     DIB(cx),
                     containing_scope,
-                    ptr::null_mut(),
+                    discriminator_name,
                     file_metadata,
                     UNKNOWN_LINE_NUMBER,
                     size.bits(),
@@ -1789,7 +1898,7 @@ fn prepare_enum_metadata(
                 Some(llvm::LLVMRustDIBuilderCreateMemberType(
                     DIB(cx),
                     containing_scope,
-                    ptr::null_mut(),
+                    discriminator_name,
                     file_metadata,
                     UNKNOWN_LINE_NUMBER,
                     size.bits(),
@@ -1801,6 +1910,22 @@ fn prepare_enum_metadata(
         },
     };
 
+    let mut outer_fields = match layout.variants {
+        layout::Variants::Single { .. } => vec![],
+        layout::Variants::Multiple { .. } => {
+            let tuple_mdf = TupleMemberDescriptionFactory {
+                ty: enum_type,
+                component_types: outer_field_tys,
+                span
+            };
+            tuple_mdf
+                .create_member_descriptions(cx)
+                .into_iter()
+                .map(|desc| Some(desc.into_metadata(cx, containing_scope)))
+                .collect()
+        }
+    };
+
     let variant_part_unique_type_id_str = SmallCStr::new(
         debug_context(cx).type_map
             .borrow_mut()
@@ -1821,9 +1946,10 @@ fn prepare_enum_metadata(
             empty_array,
             variant_part_unique_type_id_str.as_ptr())
     };
+    outer_fields.push(Some(variant_part));
 
     // The variant part must be wrapped in a struct according to DWARF.
-    let type_array = create_DIArray(DIB(cx), &[Some(variant_part)]);
+    let type_array = create_DIArray(DIB(cx), &outer_fields);
     let struct_wrapper = unsafe {
         llvm::LLVMRustDIBuilderCreateStructType(
             DIB(cx),
@@ -1855,12 +1981,6 @@ fn prepare_enum_metadata(
             span,
         }),
     );
-
-    fn get_enum_discriminant_name(cx: &CodegenCx<'_, '_>,
-                                  def_id: DefId)
-                                  -> InternedString {
-        cx.tcx.item_name(def_id)
-    }
 }
 
 /// Creates debug information for a composite type, that is, anything that
@@ -1918,26 +2038,7 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, 'tcx>,
 
     let member_metadata: Vec<_> = member_descriptions
         .into_iter()
-        .map(|member_description| {
-            let member_name = CString::new(member_description.name).unwrap();
-            unsafe {
-                Some(llvm::LLVMRustDIBuilderCreateVariantMemberType(
-                    DIB(cx),
-                    composite_type_metadata,
-                    member_name.as_ptr(),
-                    unknown_file_metadata(cx),
-                    UNKNOWN_LINE_NUMBER,
-                    member_description.size.bits(),
-                    member_description.align.bits() as u32,
-                    member_description.offset.bits(),
-                    match member_description.discriminant {
-                        None => None,
-                        Some(value) => Some(cx.const_u64(value)),
-                    },
-                    member_description.flags,
-                    member_description.type_metadata))
-            }
-        })
+        .map(|desc| Some(desc.into_metadata(cx, composite_type_metadata)))
         .collect();
 
     let type_params = compute_type_parameters(cx, composite_type);
diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs
index d42fa829161..cbcc457fda9 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(_, substs, _), &layout::Variants::Single { index })
+                 = (&layout.ty.sty, &layout.variants)
+            {
+                write!(&mut name, "::{}", substs.variant_name(index)).unwrap();
+            }
             Some(name)
         }
         _ => None
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index 621c4e5d448..fcf099235aa 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,9 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
                     .iter()
                     .zip(upvar_tys)
                     .enumerate()
-                    .map(|(i, (upvar, ty))| (i, upvar.debug_name, upvar.by_ref, ty));
+                    .map(|(i, (upvar, ty))| {
+                        (None, i, upvar.debug_name, upvar.by_ref, ty, scope, DUMMY_SP)
+                    });
 
                 let generator_fields = mir.generator_layout.as_ref().map(|generator_layout| {
                     let (def_id, gen_substs) = match closure_layout.ty.sty {
@@ -657,22 +660,48 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
                     };
                     let state_tys = gen_substs.state_tys(def_id, tcx);
 
-                    let upvar_count = upvar_debuginfo.len();
-                    generator_layout.fields
-                        .iter()
+                    generator_layout.variant_fields.iter()
                         .zip(state_tys)
                         .enumerate()
-                        .filter_map(move |(i, (decl, ty))| {
-                            let ty = fx.monomorphize(&ty);
-                            decl.name.map(|name| (i + upvar_count + 1, name, false, ty))
+                        .flat_map(move |(variant_idx, (fields, tys))| {
+                            let variant_idx = Some(VariantIdx::from(variant_idx));
+                            fields.iter()
+                                .zip(tys)
+                                .enumerate()
+                                .filter_map(move |(i, (field, ty))| {
+                                    let decl = &generator_layout.
+                                        __local_debuginfo_codegen_only_do_not_use[*field];
+                                    if let Some(name) = decl.name {
+                                        let ty = fx.monomorphize(&ty);
+                                        let (var_scope, var_span) = fx.debug_loc(mir::SourceInfo {
+                                            span: decl.source_info.span,
+                                            scope: decl.visibility_scope,
+                                        });
+                                        let var_scope = var_scope.unwrap_or(scope);
+                                        Some((variant_idx, i, name, false, ty, var_scope, var_span))
+                                    } else {
+                                        None
+                                    }
+                            })
                         })
                 }).into_iter().flatten();
 
                 upvars.chain(generator_fields)
             };
 
-            for (field, name, by_ref, ty) in extra_locals {
-                let byte_offset_of_var_in_env = closure_layout.fields.offset(field).bytes();
+            for (variant_idx, field, name, by_ref, ty, var_scope, var_span) in extra_locals {
+                let fields = match variant_idx {
+                    Some(variant_idx) => {
+                        match &closure_layout.variants {
+                            Variants::Multiple { variants, .. } => {
+                                &variants[variant_idx].fields
+                            },
+                            _ => bug!("variant index on univariant layout"),
+                        }
+                    }
+                    None => &closure_layout.fields,
+                };
+                let byte_offset_of_var_in_env = fields.offset(field).bytes();
 
                 let ops = bx.debuginfo_upvar_ops_sequence(byte_offset_of_var_in_env);
 
@@ -694,10 +723,10 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
                     &fx.debug_context,
                     name,
                     ty,
-                    scope,
+                    var_scope,
                     variable_access,
                     VariableKind::LocalVariable,
-                    DUMMY_SP
+                    var_span
                 );
             }
         });
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 1134707f96c..670b6c47269 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,9 +295,8 @@ impl<'a, 'tcx: 'a, V: CodegenObject> PlaceRef<'tcx, V> {
                 ..
             } => {
                 let ptr = self.project_field(bx, discr_index);
-                let to = self.layout.ty.ty_adt_def().unwrap()
-                    .discriminant_for_variant(bx.tcx(), variant_index)
-                    .val;
+                let to =
+                    self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val;
                 bx.store(
                     bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to),
                     ptr.llval,
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs
index 1caafb014e0..8d3e64c1aa6 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/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index f1bbb1c8809..1d62b9f764a 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,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                         }
                     }
                 }
+                // We do not need to handle generators here, because this runs
+                // before the generator transform stage.
                 _ => {
                     let ty = if let Some(name) = maybe_name {
                         span_mirbug_and_err!(
@@ -745,11 +747,26 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
         let tcx = self.tcx();
 
         let (variant, substs) = match base_ty {
-            PlaceTy { ty, variant_index: Some(variant_index) } => {
-                match ty.sty {
-                    ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs),
-                    _ => bug!("can't have downcast of non-adt type"),
+            PlaceTy { ty, variant_index: Some(variant_index) } => match ty.sty {
+                ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs),
+                ty::Generator(def_id, substs, _) => {
+                    let mut variants = substs.state_tys(def_id, tcx);
+                    let mut variant = match variants.nth(variant_index.into()) {
+                        Some(v) => v,
+                        None => {
+                            bug!("variant_index of generator out of range: {:?}/{:?}",
+                                 variant_index,
+                                 substs.state_tys(def_id, tcx).count())
+                        }
+                    };
+                    return match variant.nth(field.index()) {
+                        Some(ty) => Ok(ty),
+                        None => Err(FieldAccessError::OutOfRange {
+                            field_count: variant.count(),
+                        }),
+                    }
                 }
+                _ => bug!("can't have downcast of non-adt non-generator type"),
             }
             PlaceTy { ty, variant_index: None } => match ty.sty {
                 ty::Adt(adt_def, substs) if !adt_def.is_enum() =>
@@ -763,19 +780,14 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                     }
                 }
                 ty::Generator(def_id, substs, _) => {
-                    // Try pre-transform fields first (upvars and current state)
-                    if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field.index()) {
-                        return Ok(ty);
-                    }
-
-                    // Then try `field_tys` which contains all the fields, but it
-                    // requires the final optimized MIR.
-                    return match substs.field_tys(def_id, tcx).nth(field.index()) {
+                    // Only prefix fields (upvars and current state) are
+                    // accessible without a variant index.
+                    return match substs.prefix_tys(def_id, tcx).nth(field.index()) {
                         Some(ty) => Ok(ty),
                         None => Err(FieldAccessError::OutOfRange {
-                            field_count: substs.field_tys(def_id, tcx).count(),
+                            field_count: substs.prefix_tys(def_id, tcx).count(),
                         }),
-                    };
+                    }
                 }
                 ty::Tuple(tys) => {
                     return match tys.get(field.index()) {
@@ -1908,18 +1920,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 }
             }
             AggregateKind::Generator(def_id, substs, _) => {
-                // Try pre-transform fields first (upvars and current state)
-                if let Some(ty) = substs.pre_transforms_tys(def_id, tcx).nth(field_index) {
-                    Ok(ty)
-                } else {
-                    // Then try `field_tys` which contains all the fields, but it
-                    // requires the final optimized MIR.
-                    match substs.field_tys(def_id, tcx).nth(field_index) {
-                        Some(ty) => Ok(ty),
-                        None => Err(FieldAccessError::OutOfRange {
-                            field_count: substs.field_tys(def_id, tcx).count(),
-                        }),
-                    }
+                // It doesn't make sense to look at a field beyond the prefix;
+                // these require a variant index, and are not initialized in
+                // aggregate rvalues.
+                match substs.prefix_tys(def_id, tcx).nth(field_index) {
+                    Some(ty) => Ok(ty),
+                    None => Err(FieldAccessError::OutOfRange {
+                        field_count: substs.prefix_tys(def_id, tcx).count(),
+                    }),
                 }
             }
             AggregateKind::Array(ty) => Ok(ty),
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 629aef56f5f..e745ee0f190 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -207,7 +207,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 movability,
             } => {
                 // see (*) above
-                let mut operands: Vec<_> = upvars
+                let operands: Vec<_> = upvars
                     .into_iter()
                     .map(|upvar| {
                         let upvar = this.hir.mirror(upvar);
@@ -248,21 +248,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     }).collect();
                 let result = match substs {
                     UpvarSubsts::Generator(substs) => {
+                        // We implicitly set the discriminant to 0. See
+                        // librustc_mir/transform/deaggregator.rs for details.
                         let movability = movability.unwrap();
-                        // Add the state operand since it follows the upvars in the generator
-                        // struct. See librustc_mir/transform/generator.rs for more details.
-                        operands.push(Operand::Constant(box Constant {
-                            span: expr_span,
-                            ty: this.hir.tcx().types.u32,
-                            user_ty: None,
-                            literal: this.hir.tcx().mk_const(
-                                ty::Const::from_bits(
-                                    this.hir.tcx(),
-                                    0,
-                                    ty::ParamEnv::empty().and(this.hir.tcx().types.u32),
-                                ),
-                            ),
-                        }));
                         box AggregateKind::Generator(closure_id, substs, movability)
                     }
                     UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs),
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 32f218d49ce..58e474658ea 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 ed7c316e3da..9c2b491925f 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -567,9 +567,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
 
         let (discr_kind, discr_index) = match rval.layout.variants {
             layout::Variants::Single { index } => {
-                let discr_val = rval.layout.ty.ty_adt_def().map_or(
+                let discr_val = rval.layout.ty.discriminant_for_variant(*self.tcx, index).map_or(
                     index.as_u32() as u128,
-                    |def| def.discriminant_for_variant(*self.tcx, index).val);
+                    |discr| discr.val);
                 return Ok((discr_val, index));
             }
             layout::Variants::Multiple { ref discr_kind, discr_index, .. } =>
@@ -604,12 +604,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
                     bits_discr
                 };
                 // Make sure we catch invalid discriminants
-                let index = rval.layout.ty
-                    .ty_adt_def()
-                    .expect("tagged layout for non adt")
-                    .discriminants(self.tcx.tcx)
-                    .find(|(_, var)| var.val == real_discr)
-                    .ok_or_else(|| InterpError::InvalidDiscriminant(raw_discr.erase_tag()))?;
+                let index = match &rval.layout.ty.sty {
+                    ty::Adt(adt, _) => adt
+                        .discriminants(self.tcx.tcx)
+                        .find(|(_, var)| var.val == real_discr),
+                    ty::Generator(def_id, substs, _) => substs
+                        .discriminants(*def_id, self.tcx.tcx)
+                        .find(|(_, var)| var.val == real_discr),
+                    _ => bug!("tagged layout for non-adt non-generator"),
+                }.ok_or_else(|| InterpError::InvalidDiscriminant(raw_discr.erase_tag()))?;
                 (real_discr, index.0)
             },
             layout::DiscriminantKind::Niche {
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 8239337796e..0cc48002816 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
diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs
index 9061dfff76f..9215356daa4 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 (Unresumed).
+                        let variant_index = VariantIdx::new(0);
+                        set_discriminant = Some(Statement {
+                            kind: StatementKind::SetDiscriminant {
+                                place: lhs.clone(),
+                                variant_index,
+                            },
+                            source_info,
+                        });
+
+                        // Operands are upvars stored on the base place, so no
+                        // downcast is necessary.
+
+                        None
+                    }
                     _ => None
                 };
 
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 68f33d9438d..d2da1e6e3ac 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -54,13 +54,14 @@ use rustc::hir::def_id::DefId;
 use rustc::mir::*;
 use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor};
 use rustc::ty::{self, TyCtxt, AdtDef, Ty};
+use rustc::ty::GeneratorSubsts;
 use rustc::ty::layout::VariantIdx;
 use rustc::ty::subst::SubstsRef;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc_data_structures::bit_set::BitSet;
 use std::borrow::Cow;
-use std::iter::once;
+use std::iter;
 use std::mem;
 use crate::transform::{MirPass, MirSource};
 use crate::transform::simplify;
@@ -145,14 +146,14 @@ fn self_arg() -> Local {
 }
 
 /// Generator have not been resumed yet
-const UNRESUMED: u32 = 0;
+const UNRESUMED: usize = GeneratorSubsts::UNRESUMED;
 /// Generator has returned / is completed
-const RETURNED: u32 = 1;
+const RETURNED: usize = GeneratorSubsts::RETURNED;
 /// Generator has been poisoned
-const POISONED: u32 = 2;
+const POISONED: usize = GeneratorSubsts::POISONED;
 
 struct SuspensionPoint {
-    state: u32,
+    state: usize,
     resume: BasicBlock,
     drop: Option<BasicBlock>,
     storage_liveness: liveness::LiveVarSet,
@@ -163,12 +164,12 @@ struct TransformVisitor<'a, 'tcx: 'a> {
     state_adt_ref: &'tcx AdtDef,
     state_substs: SubstsRef<'tcx>,
 
-    // The index of the generator state in the generator struct
-    state_field: usize,
+    // The type of the discriminant in the generator struct
+    discr_ty: Ty<'tcx>,
 
     // Mapping from Local to (type of local, generator struct index)
     // FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`.
-    remap: FxHashMap<Local, (Ty<'tcx>, usize)>,
+    remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
 
     // A map from a suspension point in a block to the locals which have live storage at that point
     // FIXME(eddyb) This should use `IndexVec<BasicBlock, Option<_>>`.
@@ -189,8 +190,9 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
     }
 
     // Create a Place referencing a generator struct field
-    fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
-        let base = Place::Base(PlaceBase::Local(self_arg()));
+    fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
+        let self_place = Place::Base(PlaceBase::Local(self_arg()));
+        let base = self_place.downcast_unnamed(variant_index);
         let field = Projection {
             base: base,
             elem: ProjectionElem::Field(Field::new(idx), ty),
@@ -198,24 +200,28 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
         Place::Projection(Box::new(field))
     }
 
-    // Create a statement which changes the generator state
-    fn set_state(&self, state_disc: u32, source_info: SourceInfo) -> Statement<'tcx> {
-        let state = self.make_field(self.state_field, self.tcx.types.u32);
-        let val = Operand::Constant(box Constant {
-            span: source_info.span,
-            ty: self.tcx.types.u32,
-            user_ty: None,
-            literal: self.tcx.mk_const(ty::Const::from_bits(
-                self.tcx,
-                state_disc.into(),
-                ty::ParamEnv::empty().and(self.tcx.types.u32)
-            )),
-        });
+    // Create a statement which changes the discriminant
+    fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> {
+        let self_place = Place::Base(PlaceBase::Local(self_arg()));
         Statement {
             source_info,
-            kind: StatementKind::Assign(state, box Rvalue::Use(val)),
+            kind: StatementKind::SetDiscriminant { place: self_place, variant_index: state_disc },
         }
     }
+
+    // Create a statement which reads the discriminant into a temporary
+    fn get_discr(&self, mir: &mut Mir<'tcx>) -> (Statement<'tcx>, Place<'tcx>) {
+        let temp_decl = LocalDecl::new_internal(self.tcx.types.isize, mir.span);
+        let temp = Place::Base(PlaceBase::Local(Local::new(mir.local_decls.len())));
+        mir.local_decls.push(temp_decl);
+
+        let self_place = Place::Base(PlaceBase::Local(self_arg()));
+        let assign = Statement {
+            source_info: source_info(mir),
+            kind: StatementKind::Assign(temp.clone(), box Rvalue::Discriminant(self_place)),
+        };
+        (assign, temp)
+    }
 }
 
 impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
@@ -232,8 +238,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
                     location: Location) {
         if let Place::Base(PlaceBase::Local(l)) = *place {
             // Replace an Local in the remap with a generator struct access
-            if let Some(&(ty, idx)) = self.remap.get(&l) {
-                *place = self.make_field(idx, ty);
+            if let Some(&(ty, variant_index, idx)) = self.remap.get(&l) {
+                *place = self.make_field(variant_index, idx, ty);
             }
         } else {
             self.super_place(place, context, location);
@@ -274,7 +280,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
                                             box self.make_state(state_idx, v)),
             });
             let state = if let Some(resume) = resume { // Yield
-                let state = 3 + self.suspension_points.len() as u32;
+                let state = 3 + self.suspension_points.len();
 
                 self.suspension_points.push(SuspensionPoint {
                     state,
@@ -283,11 +289,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
                     storage_liveness: self.storage_liveness.get(&block).unwrap().clone(),
                 });
 
-                state
+                VariantIdx::new(state)
             } else { // Return
-                RETURNED // state for returned
+                VariantIdx::new(RETURNED) // state for returned
             };
-            data.statements.push(self.set_state(state, source_info));
+            data.statements.push(self.set_discr(state, source_info));
             data.terminator.as_mut().unwrap().kind = TerminatorKind::Return;
         }
 
@@ -387,6 +393,7 @@ fn locals_live_across_suspend_points(
 ) -> (
     liveness::LiveVarSet,
     FxHashMap<BasicBlock, liveness::LiveVarSet>,
+    BitSet<BasicBlock>,
 ) {
     let dead_unwinds = BitSet::new_empty(mir.basic_blocks().len());
     let def_id = source.def_id();
@@ -431,8 +438,12 @@ fn locals_live_across_suspend_points(
 
     let mut storage_liveness_map = FxHashMap::default();
 
+    let mut suspending_blocks = BitSet::new_empty(mir.basic_blocks().len());
+
     for (block, data) in mir.basic_blocks().iter_enumerated() {
         if let TerminatorKind::Yield { .. } = data.terminator().kind {
+            suspending_blocks.insert(block);
+
             let loc = Location {
                 block: block,
                 statement_index: data.statements.len(),
@@ -484,7 +495,7 @@ fn locals_live_across_suspend_points(
     // The generator argument is ignored
     set.remove(self_arg());
 
-    (set, storage_liveness_map)
+    (set, storage_liveness_map, suspending_blocks)
 }
 
 fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -493,15 +504,14 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             interior: Ty<'tcx>,
                             movable: bool,
                             mir: &mut Mir<'tcx>)
-    -> (FxHashMap<Local, (Ty<'tcx>, usize)>,
+    -> (FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
         GeneratorLayout<'tcx>,
         FxHashMap<BasicBlock, liveness::LiveVarSet>)
 {
     // Use a liveness analysis to compute locals which are live across a suspension point
-    let (live_locals, storage_liveness) = locals_live_across_suspend_points(tcx,
-                                                                            mir,
-                                                                            source,
-                                                                            movable);
+    let (live_locals, storage_liveness, suspending_blocks) =
+        locals_live_across_suspend_points(tcx, mir, source, movable);
+
     // Erase regions from the types passed in from typeck so we can compare them with
     // MIR types
     let allowed_upvars = tcx.erase_regions(upvars);
@@ -527,7 +537,6 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
     }
 
-    let upvar_len = upvars.len();
     let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), mir.span);
 
     // Gather live locals and their indices replacing values in mir.local_decls with a dummy
@@ -537,37 +546,52 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         (local, var)
     });
 
+    // For now we will access everything via variant #3, leaving empty variants
+    // for the UNRESUMED, RETURNED, and POISONED states.
+    // If there were a yield-less generator without a variant #3, it would not
+    // have any vars to remap, so we would never use this.
+    let variant_index = VariantIdx::new(3);
+
     // Create a map from local indices to generator struct indices.
-    // These are offset by (upvar_len + 1) because of fields which comes before locals.
     // We also create a vector of the LocalDecls of these locals.
-    let (remap, vars) = live_decls.enumerate().map(|(idx, (local, var))| {
-        ((local, (var.ty, upvar_len + 1 + idx)), var)
-    }).unzip();
+    let mut remap = FxHashMap::default();
+    let mut decls = IndexVec::new();
+    for (idx, (local, var)) in live_decls.enumerate() {
+        remap.insert(local, (var.ty, variant_index, idx));
+        decls.push(var);
+    }
+    let field_tys = decls.iter().map(|field| field.ty).collect::<IndexVec<_, _>>();
+
+    // Put every var in each variant, for now.
+    let all_vars = (0..field_tys.len()).map(GeneratorSavedLocal::from).collect();
+    let empty_variants = iter::repeat(IndexVec::new()).take(3);
+    let state_variants = iter::repeat(all_vars).take(suspending_blocks.count());
 
     let layout = GeneratorLayout {
-        fields: vars
+        field_tys,
+        variant_fields: empty_variants.chain(state_variants).collect(),
+        __local_debuginfo_codegen_only_do_not_use: decls,
     };
 
     (remap, layout, storage_liveness)
 }
 
-fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                           mir: &mut Mir<'tcx>,
-                           cases: Vec<(u32, BasicBlock)>,
+fn insert_switch<'a, 'tcx>(mir: &mut Mir<'tcx>,
+                           cases: Vec<(usize, BasicBlock)>,
                            transform: &TransformVisitor<'a, 'tcx>,
                            default: TerminatorKind<'tcx>) {
     let default_block = insert_term_block(mir, default);
-
+    let (assign, discr) = transform.get_discr(mir);
     let switch = TerminatorKind::SwitchInt {
-        discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)),
-        switch_ty: tcx.types.u32,
-        values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::<Vec<_>>()),
-        targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(),
+        discr: Operand::Move(discr),
+        switch_ty: transform.discr_ty,
+        values: Cow::from(cases.iter().map(|&(i, _)| i as u128).collect::<Vec<_>>()),
+        targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(),
     };
 
     let source_info = source_info(mir);
     mir.basic_blocks_mut().raw.insert(0, BasicBlockData {
-        statements: Vec::new(),
+        statements: vec![assign],
         terminator: Some(Terminator {
             source_info,
             kind: switch,
@@ -652,7 +676,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
     // The returned state and the poisoned state fall through to the default
     // case which is just to return
 
-    insert_switch(tcx, &mut mir, cases, &transform, TerminatorKind::Return);
+    insert_switch(&mut mir, cases, &transform, TerminatorKind::Return);
 
     for block in mir.basic_blocks_mut() {
         let kind = &mut block.terminator_mut().kind;
@@ -766,7 +790,8 @@ fn create_generator_resume_function<'a, 'tcx>(
     for block in mir.basic_blocks_mut() {
         let source_info = block.terminator().source_info;
         if let &TerminatorKind::Resume = &block.terminator().kind {
-            block.statements.push(transform.set_state(POISONED, source_info));
+            block.statements.push(
+                transform.set_discr(VariantIdx::new(POISONED), source_info));
         }
     }
 
@@ -784,7 +809,7 @@ fn create_generator_resume_function<'a, 'tcx>(
     // Panic when resumed on the poisoned state
     cases.insert(2, (POISONED, insert_panic_block(tcx, mir, GeneratorResumedAfterPanic)));
 
-    insert_switch(tcx, mir, cases, &transform, TerminatorKind::Unreachable);
+    insert_switch(mir, cases, &transform, TerminatorKind::Unreachable);
 
     make_generator_state_argument_indirect(tcx, def_id, mir);
     make_generator_state_argument_pinned(tcx, mir);
@@ -830,7 +855,7 @@ fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock {
 
 fn create_cases<'a, 'tcx, F>(mir: &mut Mir<'tcx>,
                           transform: &TransformVisitor<'a, 'tcx>,
-                          target: F) -> Vec<(u32, BasicBlock)>
+                          target: F) -> Vec<(usize, BasicBlock)>
     where F: Fn(&SuspensionPoint) -> Option<BasicBlock> {
     let source_info = source_info(mir);
 
@@ -888,10 +913,11 @@ impl MirPass for StateTransform {
         let gen_ty = mir.local_decls.raw[1].ty;
 
         // Get the interior types and substs which typeck computed
-        let (upvars, interior, movable) = match gen_ty.sty {
+        let (upvars, interior, discr_ty, movable) = match gen_ty.sty {
             ty::Generator(_, substs, movability) => {
                 (substs.upvar_tys(def_id, tcx).collect(),
                  substs.witness(def_id, tcx),
+                 substs.discr_ty(tcx),
                  movability == hir::GeneratorMovability::Movable)
             }
             _ => bug!(),
@@ -921,8 +947,6 @@ impl MirPass for StateTransform {
             movable,
             mir);
 
-        let state_field = upvars.len();
-
         // Run the transformation which converts Places from Local to generator struct
         // accesses for locals in `remap`.
         // It also rewrites `return x` and `yield y` as writing a new generator state and returning
@@ -935,7 +959,7 @@ impl MirPass for StateTransform {
             storage_liveness,
             suspension_points: Vec::new(),
             new_ret_local,
-            state_field,
+            discr_ty,
         };
         transform.visit_mir(mir);
 
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
index 4302076f1c3..8d9a878fc9e 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 {
diff --git a/src/test/debuginfo/generator-locals.rs b/src/test/debuginfo/generator-locals.rs
new file mode 100644
index 00000000000..59dbfecc39f
--- /dev/null
+++ b/src/test/debuginfo/generator-locals.rs
@@ -0,0 +1,87 @@
+// min-lldb-version: 310
+
+// compile-flags:-g
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command:run
+// gdb-command:print a
+// gdb-check:$1 = 5
+// gdb-command:print c
+// gdb-check:$2 = 6
+// gdb-command:print d
+// gdb-check:$3 = 7
+// gdb-command:continue
+// gdb-command:print a
+// gdb-check:$4 = 7
+// gdb-command:print c
+// gdb-check:$5 = 6
+// gdb-command:print e
+// gdb-check:$6 = 8
+// gdb-command:continue
+// gdb-command:print a
+// gdb-check:$7 = 8
+// gdb-command:print c
+// gdb-check:$8 = 6
+
+// === LLDB TESTS ==================================================================================
+
+// lldb-command:run
+// lldb-command:print a
+// lldbg-check:(int) $0 = 5
+// lldbr-check:(int) a = 5
+// lldb-command:print c
+// lldbg-check:(int) $1 = 6
+// lldbr-check:(int) c = 6
+// lldb-command:print d
+// lldbg-check:(int) $2 = 7
+// lldbr-check:(int) d = 7
+// lldb-command:continue
+// lldb-command:print a
+// lldbg-check:(int) $3 = 7
+// lldbr-check:(int) a = 7
+// lldb-command:print c
+// lldbg-check:(int) $4 = 6
+// lldbr-check:(int) c = 6
+// lldb-command:print e
+// lldbg-check:(int) $5 = 8
+// lldbr-check:(int) e = 8
+// lldb-command:continue
+// lldb-command:print a
+// lldbg-check:(int) $6 = 8
+// lldbr-check:(int) a = 8
+// lldb-command:print c
+// lldbg-check:(int) $7 = 6
+// lldbr-check:(int) c = 6
+
+#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
+#![omit_gdb_pretty_printer_section]
+
+use std::ops::Generator;
+use std::pin::Pin;
+
+fn main() {
+    let mut a = 5;
+    let mut b = || {
+        let c = 6; // Live across multiple yield points
+
+        let d = 7; // Live across only one yield point
+        yield;
+        _zzz(); // #break
+        a = d;
+
+        let e = 8; // Live across zero yield points
+        _zzz(); // #break
+        a = e;
+
+        yield;
+        _zzz(); // #break
+        a = c;
+    };
+    Pin::new(&mut b).resume();
+    Pin::new(&mut b).resume();
+    Pin::new(&mut b).resume();
+    _zzz(); // #break
+}
+
+fn _zzz() {()}
diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs
new file mode 100644
index 00000000000..c6f98e5782b
--- /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[...], <<variant>>: {__state: 0, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {[...]}}}
+// gdb-command:continue
+// gdb-command:print b
+// gdb-check:$2 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 3, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {c: 6, d: 7}, 4: generator_objects::main::generator::Suspend1 {[...]}}}
+// gdb-command:continue
+// gdb-command:print b
+// gdb-check:$3 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 4, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {c: 7, d: 8}}}
+// gdb-command:continue
+// gdb-command:print b
+// gdb-check:$4 = generator_objects::main::generator {__0: 0x[...], <<variant>>: {__state: 1, 0: generator_objects::main::generator::Unresumed, 1: generator_objects::main::generator::Returned, 2: generator_objects::main::generator::Panicked, 3: generator_objects::main::generator::Suspend0 {[...]}, 4: generator_objects::main::generator::Suspend1 {[...]}}}
+
+// === LLDB TESTS ==================================================================================
+
+// lldb-command:run
+// lldb-command:print b
+// lldbg-check:(generator_objects::main::generator) $0 = generator(&0x[...])
+// lldb-command:continue
+// lldb-command:print b
+// lldbg-check:(generator_objects::main::generator) $1 = generator(&0x[...])
+// lldb-command:continue
+// lldb-command:print b
+// lldbg-check:(generator_objects::main::generator) $2 = generator(&0x[...])
+// lldb-command:continue
+// lldb-command:print b
+// lldbg-check:(generator_objects::main::generator) $3 = generator(&0x[...])
+
+#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
+#![omit_gdb_pretty_printer_section]
+
+use std::ops::Generator;
+use std::pin::Pin;
+
+fn main() {
+    let mut a = 5;
+    let mut b = || {
+        let mut c = 6;
+        let mut d = 7;
+
+        yield;
+        a += 1;
+        c += 1;
+        d += 1;
+
+        yield;
+        println!("{} {} {}", a, c, d);
+    };
+    _zzz(); // #break
+    Pin::new(&mut b).resume();
+    _zzz(); // #break
+    Pin::new(&mut b).resume();
+    _zzz(); // #break
+    Pin::new(&mut b).resume();
+    _zzz(); // #break
+}
+
+fn _zzz() {()}
diff --git a/src/test/debuginfo/generators.rs b/src/test/debuginfo/generators.rs
deleted file mode 100644
index 35a67217f16..00000000000
--- a/src/test/debuginfo/generators.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-// min-lldb-version: 310
-
-// compile-flags:-g
-
-// === GDB TESTS ===================================================================================
-
-// gdb-command:run
-// gdb-command:print a
-// gdb-check:$1 = 5
-// gdb-command:print d
-// gdb-check:$2 = 6
-
-// === LLDB TESTS ==================================================================================
-
-// lldb-command:run
-// lldb-command:print a
-// lldbg-check:(int) $0 = 5
-// lldbr-check:(int) a = 5
-// lldb-command:print d
-// lldbg-check:(int) $1 = 6
-// lldbr-check:(int) d = 6
-
-#![feature(omit_gdb_pretty_printer_section, generators, generator_trait)]
-#![omit_gdb_pretty_printer_section]
-
-use std::ops::Generator;
-use std::pin::Pin;
-
-fn main() {
-    let mut a = 5;
-    let mut b = || {
-        let d = 6;
-        yield;
-        _zzz(); // #break
-        a = d;
-    };
-    Pin::new(&mut b).resume();
-    Pin::new(&mut b).resume();
-    _zzz(); // #break
-}
-
-fn _zzz() {()}
diff --git a/src/test/mir-opt/generator-drop-cleanup.rs b/src/test/mir-opt/generator-drop-cleanup.rs
index 48398691271..9cc4272fafa 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;