about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-08-09 11:56:53 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-08-24 13:27:38 -0400
commit56506cfa25a8f4c47b1e093cdc576a62b1eb08a5 (patch)
tree7812bedfbd3924ef3221e1dc8cbd0d2c94c45d31
parent2d1d3fef6205276e0123dea96cc346d1da7df6d0 (diff)
support user-given types in adts
-rw-r--r--src/librustc/ich/impls_mir.rs3
-rw-r--r--src/librustc/mir/mod.rs17
-rw-r--r--src/librustc/mir/tcx.rs2
-rw-r--r--src/librustc/mir/visit.rs1
-rw-r--r--src/librustc_codegen_llvm/mir/rvalue.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs48
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs4
-rw-r--r--src/librustc_mir/hair/cx/expr.rs47
-rw-r--r--src/librustc_mir/hair/mod.rs5
-rw-r--r--src/librustc_mir/interpret/step.rs2
-rw-r--r--src/librustc_mir/shim.rs2
-rw-r--r--src/librustc_mir/transform/deaggregator.rs2
-rw-r--r--src/librustc_mir/transform/generator.rs2
-rw-r--r--src/librustc_typeck/astconv.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs31
-rw-r--r--src/test/ui/nll/user-annotations/adt-brace-enums.rs62
-rw-r--r--src/test/ui/nll/user-annotations/adt-brace-enums.stderr41
-rw-r--r--src/test/ui/nll/user-annotations/adt-brace-structs.rs60
-rw-r--r--src/test/ui/nll/user-annotations/adt-brace-structs.stderr41
-rw-r--r--src/test/ui/nll/user-annotations/adt-nullary-enums.rs68
-rw-r--r--src/test/ui/nll/user-annotations/adt-nullary-enums.stderr41
-rw-r--r--src/test/ui/nll/user-annotations/adt-tuple-enums.rs64
-rw-r--r--src/test/ui/nll/user-annotations/adt-tuple-enums.stderr41
-rw-r--r--src/test/ui/nll/user-annotations/adt-tuple-struct.rs60
-rw-r--r--src/test/ui/nll/user-annotations/adt-tuple-struct.stderr41
-rw-r--r--src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs29
-rw-r--r--src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr8
27 files changed, 686 insertions, 40 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index ad20bab2b87..1c9387d02d5 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -479,10 +479,11 @@ for mir::AggregateKind<'gcx> {
             mir::AggregateKind::Array(t) => {
                 t.hash_stable(hcx, hasher);
             }
-            mir::AggregateKind::Adt(adt_def, idx, substs, active_field) => {
+            mir::AggregateKind::Adt(adt_def, idx, substs, user_substs, active_field) => {
                 adt_def.hash_stable(hcx, hasher);
                 idx.hash_stable(hcx, hasher);
                 substs.hash_stable(hcx, hasher);
+                user_substs.hash_stable(hcx, hasher);
                 active_field.hash_stable(hcx, hasher);
             }
             mir::AggregateKind::Closure(def_id, ref substs) => {
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index efe2e352138..7f292f6167f 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2009,7 +2009,7 @@ pub enum AggregateKind<'tcx> {
     /// active field number and is present only for union expressions
     /// -- e.g. for a union expression `SomeUnion { c: .. }`, the
     /// active field index would identity the field `c`
-    Adt(&'tcx AdtDef, usize, &'tcx Substs<'tcx>, Option<usize>),
+    Adt(&'tcx AdtDef, usize, &'tcx Substs<'tcx>, Option<CanonicalTy<'tcx>>, Option<usize>),
 
     Closure(DefId, ClosureSubsts<'tcx>),
     Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability),
@@ -2135,7 +2135,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                         _ => fmt_tuple(fmt, places),
                     },
 
-                    AggregateKind::Adt(adt_def, variant, substs, _) => {
+                    AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => {
                         let variant_def = &adt_def.variants[variant];
 
                         ppaux::parameterized(fmt, substs, variant_def.did, &[])?;
@@ -2813,8 +2813,14 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
                 let kind = box match **kind {
                     AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)),
                     AggregateKind::Tuple => AggregateKind::Tuple,
-                    AggregateKind::Adt(def, v, substs, n) => {
-                        AggregateKind::Adt(def, v, substs.fold_with(folder), n)
+                    AggregateKind::Adt(def, v, substs, user_ty, n) => {
+                        AggregateKind::Adt(
+                            def,
+                            v,
+                            substs.fold_with(folder),
+                            user_ty.fold_with(folder),
+                            n,
+                        )
                     }
                     AggregateKind::Closure(id, substs) => {
                         AggregateKind::Closure(id, substs.fold_with(folder))
@@ -2846,7 +2852,8 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
                 (match **kind {
                     AggregateKind::Array(ty) => ty.visit_with(visitor),
                     AggregateKind::Tuple => false,
-                    AggregateKind::Adt(_, _, substs, _) => substs.visit_with(visitor),
+                    AggregateKind::Adt(_, _, substs, user_ty, _) =>
+                        substs.visit_with(visitor) || user_ty.visit_with(visitor),
                     AggregateKind::Closure(_, substs) => substs.visit_with(visitor),
                     AggregateKind::Generator(_, substs, _) => substs.visit_with(visitor),
                 }) || fields.visit_with(visitor)
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index ec395478ec6..c928be4f9df 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -216,7 +216,7 @@ impl<'tcx> Rvalue<'tcx> {
                     AggregateKind::Tuple => {
                         tcx.mk_tup(ops.iter().map(|op| op.ty(local_decls, tcx)))
                     }
-                    AggregateKind::Adt(def, _, substs, _) => {
+                    AggregateKind::Adt(def, _, substs, _, _) => {
                         tcx.type_of(def.did).subst(tcx, substs)
                     }
                     AggregateKind::Closure(did, substs) => {
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 1eaecf7a522..c7723fdf191 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -589,6 +589,7 @@ macro_rules! make_mir_visitor {
                             AggregateKind::Adt(_adt_def,
                                                _variant_index,
                                                ref $($mutability)* substs,
+                                               _user_substs,
                                                _active_field_index) => {
                                 self.visit_substs(substs, location);
                             }
diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs
index 25f32360815..e301e5ae70b 100644
--- a/src/librustc_codegen_llvm/mir/rvalue.rs
+++ b/src/librustc_codegen_llvm/mir/rvalue.rs
@@ -148,7 +148,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
 
             mir::Rvalue::Aggregate(ref kind, ref operands) => {
                 let (dest, active_field_index) = match **kind {
-                    mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
+                    mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                         dest.codegen_set_discr(&bx, variant_index);
                         if adt_def.is_enum() {
                             (dest.project_downcast(&bx, variant_index), active_field_index)
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 67c7dfdafaa..1393045e75d 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -917,6 +917,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         terr
                     );
                 }
+
+                if let Some(user_ty) = self.rvalue_user_ty(rv) {
+                    if let Err(terr) = self.eq_canonical_type_and_type(user_ty, rv_ty, location.boring()) {
+                        span_mirbug!(
+                            self,
+                            stmt,
+                            "bad user type on rvalue ({:?} = {:?}): {:?}",
+                            user_ty,
+                            rv_ty,
+                            terr
+                        );
+                    }
+                }
+
                 self.check_rvalue(mir, rv, location);
                 if !self.tcx().features().unsized_locals {
                     let trait_ref = ty::TraitRef {
@@ -1391,7 +1405,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         let tcx = self.tcx();
 
         match *ak {
-            AggregateKind::Adt(def, variant_index, substs, active_field_index) => {
+            AggregateKind::Adt(def, variant_index, substs, _, active_field_index) => {
                 let variant = &def.variants[variant_index];
                 let adj_field_index = active_field_index.unwrap_or(field_index);
                 if let Some(field) = variant.fields.get(adj_field_index) {
@@ -1557,6 +1571,36 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
     }
 
+    /// If this rvalue supports a user-given type annotation, then
+    /// extract and return it. This represents the final type of the
+    /// rvalue and will be unified with the inferred type.
+    fn rvalue_user_ty(
+        &self,
+        rvalue: &Rvalue<'tcx>,
+    ) -> Option<CanonicalTy<'tcx>> {
+        match rvalue {
+            Rvalue::Use(_) |
+            Rvalue::Repeat(..) |
+            Rvalue::Ref(..) |
+            Rvalue::Len(..) |
+            Rvalue::Cast(..) |
+            Rvalue::BinaryOp(..) |
+            Rvalue::CheckedBinaryOp(..) |
+            Rvalue::NullaryOp(..) |
+            Rvalue::UnaryOp(..) |
+            Rvalue::Discriminant(..) =>
+                None,
+
+            Rvalue::Aggregate(aggregate, _) => match **aggregate {
+                AggregateKind::Adt(_, _, _, user_ty, _) => user_ty,
+                AggregateKind::Array(_) => None,
+                AggregateKind::Tuple => None,
+                AggregateKind::Closure(_, _) => None,
+                AggregateKind::Generator(_, _, _) => None,
+            }
+        }
+    }
+
     fn check_aggregate_rvalue(
         &mut self,
         mir: &Mir<'tcx>,
@@ -1750,7 +1794,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         );
 
         let instantiated_predicates = match aggregate_kind {
-            AggregateKind::Adt(def, _, substs, _) => {
+            AggregateKind::Adt(def, _, substs, _, _) => {
                 tcx.predicates_of(def.did).instantiate(tcx, substs)
             }
 
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index fab4f77a095..ad3862aca35 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -255,7 +255,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block.and(Rvalue::Aggregate(result, operands))
             }
             ExprKind::Adt {
-                adt_def, variant_index, substs, fields, base
+                adt_def, variant_index, substs, user_ty, fields, base
             } => { // see (*) above
                 let is_union = adt_def.is_union();
                 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
@@ -286,7 +286,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 };
 
                 let adt =
-                    box AggregateKind::Adt(adt_def, variant_index, substs, active_field_index);
+                    box AggregateKind::Adt(adt_def, variant_index, substs, user_ty, active_field_index);
                 block.and(Rvalue::Aggregate(adt, fields))
             }
             ExprKind::Assign { .. } |
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 9a771e3d451..7153a388467 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -256,6 +256,16 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 };
                 if let Some((adt_def, index)) = adt_data {
                     let substs = cx.tables().node_substs(fun.hir_id);
+
+                    let user_ty = cx.tables().user_substs(fun.hir_id)
+                        .map(|user_substs| {
+                            user_substs.unchecked_map(|user_substs| {
+                                // Here, we just pair an `AdtDef` with the
+                                // `user_substs`, so no new types etc are introduced.
+                                cx.tcx().mk_adt(adt_def, user_substs)
+                            })
+                        });
+
                     let field_refs = args.iter()
                         .enumerate()
                         .map(|(idx, e)| {
@@ -265,12 +275,12 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                             }
                         })
                         .collect();
-                    // FIXME(#47184) -- user given type annot on ADTs
                     ExprKind::Adt {
                         adt_def,
                         substs,
                         variant_index: index,
                         fields: field_refs,
+                        user_ty,
                         base: None,
                     }
                 } else {
@@ -424,11 +434,11 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 ty::Adt(adt, substs) => {
                     match adt.adt_kind() {
                         AdtKind::Struct | AdtKind::Union => {
-                            // FIXME(#47184) -- user given type annot on ADTs
                             ExprKind::Adt {
                                 adt_def: adt,
                                 variant_index: 0,
                                 substs,
+                                user_ty: user_annotated_ty_for_adt(cx, expr.hir_id, adt),
                                 fields: field_refs(cx, fields),
                                 base: base.as_ref().map(|base| {
                                     FruInfo {
@@ -450,11 +460,11 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                     assert!(base.is_none());
 
                                     let index = adt.variant_index_with_id(variant_id);
-                                    // FIXME(#47184) -- user given type annot on ADTs
                                     ExprKind::Adt {
                                         adt_def: adt,
                                         variant_index: index,
                                         substs,
+                                        user_ty: user_annotated_ty_for_adt(cx, expr.hir_id, adt),
                                         fields: field_refs(cx, fields),
                                         base: None,
                                     }
@@ -694,8 +704,7 @@ fn user_annotated_ty_for_def(
     hir_id: hir::HirId,
     def: &Def,
 ) -> Option<CanonicalTy<'tcx>> {
-    let user_substs = cx.tables().user_substs(hir_id)?;
-    Some(match def {
+    match def {
         // A reference to something callable -- e.g., a fn, method, or
         // a tuple-struct or tuple-variant. This has the type of a
         // `Fn` but with the user-given substitutions.
@@ -703,11 +712,11 @@ fn user_annotated_ty_for_def(
         Def::Method(_) |
         Def::StructCtor(_, CtorKind::Fn) |
         Def::VariantCtor(_, CtorKind::Fn) =>
-            user_substs.unchecked_map(|user_substs| {
+            Some(cx.tables().user_substs(hir_id)?.unchecked_map(|user_substs| {
                 // Here, we just pair a `DefId` with the
                 // `user_substs`, so no new types etc are introduced.
                 cx.tcx().mk_fn_def(def.def_id(), user_substs)
-            }),
+            })),
 
         Def::Const(_def_id) |
         Def::AssociatedConst(_def_id) =>
@@ -720,18 +729,26 @@ fn user_annotated_ty_for_def(
         Def::StructCtor(_def_id, CtorKind::Const) |
         Def::VariantCtor(_def_id, CtorKind::Const) =>
             match &cx.tables().node_id_to_type(hir_id).sty {
-                ty::TyAdt(adt_def, _) =>
-                    user_substs.unchecked_map(|user_substs| {
-                        // Here, we just pair an `AdtDef` with the
-                        // `user_substs`, so no new types etc are introduced.
-                        cx.tcx().mk_adt(adt_def, user_substs)
-                    }),
+                ty::Adt(adt_def, _) => user_annotated_ty_for_adt(cx, hir_id, adt_def),
                 sty => bug!("unexpected sty: {:?}", sty),
             },
 
         _ =>
             bug!("user_annotated_ty_for_def: unexpected def {:?} at {:?}", def, hir_id)
-    })
+    }
+}
+
+fn user_annotated_ty_for_adt(
+    cx: &mut Cx<'a, 'gcx, 'tcx>,
+    hir_id: hir::HirId,
+    adt_def: &'tcx AdtDef,
+) -> Option<CanonicalTy<'tcx>> {
+    let user_substs = cx.tables().user_substs(hir_id)?;
+    Some(user_substs.unchecked_map(|user_substs| {
+        // Here, we just pair an `AdtDef` with the
+        // `user_substs`, so no new types etc are introduced.
+        cx.tcx().mk_adt(adt_def, user_substs)
+    }))
 }
 
 fn method_callee<'a, 'gcx, 'tcx>(
@@ -835,7 +852,6 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
         Def::StructCtor(def_id, CtorKind::Const) |
         Def::VariantCtor(def_id, CtorKind::Const) => {
-            // FIXME(#47184) -- user given type annot on ADTs
             match cx.tables().node_id_to_type(expr.hir_id).sty {
                 // A unit struct/variant which is used as a value.
                 // We return a completely different ExprKind here to account for this special case.
@@ -844,6 +860,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                         adt_def,
                         variant_index: adt_def.variant_index_with_id(def_id),
                         substs,
+                        user_ty: user_annotated_ty_for_adt(cx, expr.hir_id, adt_def),
                         fields: vec![],
                         base: None,
                     }
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 5539db609c4..d6037adb53b 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -261,6 +261,11 @@ pub enum ExprKind<'tcx> {
         adt_def: &'tcx AdtDef,
         variant_index: usize,
         substs: &'tcx Substs<'tcx>,
+
+        /// Optional user-given substs: for something like `let x =
+        /// Bar::<T> { ... }`.
+        user_ty: Option<CanonicalTy<'tcx>>,
+
         fields: Vec<FieldExprRef<'tcx>>,
         base: Option<FruInfo<'tcx>>
     },
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index f39a5ee3e4e..db556db2417 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -208,7 +208,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
 
             Aggregate(ref kind, ref operands) => {
                 let (dest, active_field_index) = match **kind {
-                    mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
+                    mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                         self.write_discriminant_value(variant_index, dest)?;
                         if adt_def.is_enum() {
                             (self.place_downcast(dest, variant_index)?, active_field_index)
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index e6b480a27d1..7e7e7cfade6 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -850,7 +850,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
             kind: StatementKind::Assign(
                 Place::Local(RETURN_PLACE),
                 Rvalue::Aggregate(
-                    box AggregateKind::Adt(adt_def, variant_no, substs, None),
+                    box AggregateKind::Adt(adt_def, variant_no, substs, None, None),
                     (1..sig.inputs().len()+1).map(|i| {
                         Operand::Move(Place::Local(Local::new(i)))
                     }).collect()
diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs
index 8b2b9ef7e81..cff098c7b73 100644
--- a/src/librustc_mir/transform/deaggregator.rs
+++ b/src/librustc_mir/transform/deaggregator.rs
@@ -48,7 +48,7 @@ impl MirPass for Deaggregator {
 
                 let mut set_discriminant = None;
                 let active_field_index = match *kind {
-                    AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
+                    AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                         if adt_def.is_enum() {
                             set_discriminant = Some(Statement {
                                 kind: StatementKind::SetDiscriminant {
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index a7d6c08ec1f..db588884d8e 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -157,7 +157,7 @@ struct TransformVisitor<'a, 'tcx: 'a> {
 impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
     // Make a GeneratorState rvalue
     fn make_state(&self, idx: usize, val: Operand<'tcx>) -> Rvalue<'tcx> {
-        let adt = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None);
+        let adt = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None, None);
         Rvalue::Aggregate(box adt, vec![val])
     }
 
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index a528c8e9b4f..37f4f22cb02 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1367,7 +1367,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                      -> Ty<'tcx> {
         let tcx = self.tcx();
 
-        debug!("base_def_to_ty(def={:?}, opt_self_ty={:?}, path_segments={:?})",
+        debug!("def_to_ty(def={:?}, opt_self_ty={:?}, path_segments={:?})",
                path.def, opt_self_ty, path.segments);
 
         let span = path.span;
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 16894478df0..54acdd8c552 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -2113,16 +2113,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn write_user_substs(&self, node_id: hir::HirId, substs: CanonicalSubsts<'tcx>) {
+    /// Given the substs that we just converted from the HIR, try to
+    /// canonicalize them and store them as user-given substitutions
+    /// (i.e., substitutions that must be respected by the NLL check).
+    ///
+    /// This should be invoked **before any unifications have
+    /// occurred**, so that annotations like `Vec<_>` are preserved
+    /// properly.
+    pub fn write_user_substs_from_substs(&self, hir_id: hir::HirId, substs: &'tcx Substs<'tcx>) {
+        if !substs.is_noop() {
+            let user_substs = self.infcx.canonicalize_response(&substs);
+            debug!("instantiate_value_path: user_substs = {:?}", user_substs);
+            self.write_user_substs(hir_id, user_substs);
+        }
+    }
+
+    pub fn write_user_substs(&self, hir_id: hir::HirId, substs: CanonicalSubsts<'tcx>) {
         debug!(
             "write_user_substs({:?}, {:?}) in fcx {}",
-            node_id,
+            hir_id,
             substs,
             self.tag(),
         );
 
         if !substs.is_identity() {
-            self.tables.borrow_mut().user_substs_mut().insert(node_id, substs);
+            self.tables.borrow_mut().user_substs_mut().insert(hir_id, substs);
         } else {
             debug!("write_user_substs: skipping identity substs");
         }
@@ -3596,6 +3611,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         };
 
         if let Some((variant, did, substs)) = variant {
+            debug!("check_struct_path: did={:?} substs={:?}", did, substs);
+            let hir_id = self.tcx.hir.node_to_hir_id(node_id);
+            self.write_user_substs_from_substs(hir_id, substs);
+
             // Check bounds on type arguments used in the path.
             let bounds = self.instantiate_bounds(path_span, did, substs);
             let cause = traits::ObligationCause::new(path_span, self.body_id,
@@ -5138,11 +5157,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let hir_id = self.tcx.hir.node_to_hir_id(node_id);
         self.write_substs(hir_id, substs);
 
-        if !substs.is_noop() {
-            let user_substs = self.infcx.canonicalize_response(&substs);
-            debug!("instantiate_value_path: user_substs = {:?}", user_substs);
-            self.write_user_substs(hir_id, user_substs);
-        }
+        self.write_user_substs_from_substs(hir_id, substs);
 
         ty_substituted
     }
diff --git a/src/test/ui/nll/user-annotations/adt-brace-enums.rs b/src/test/ui/nll/user-annotations/adt-brace-enums.rs
new file mode 100644
index 00000000000..4465be414bc
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-brace-enums.rs
@@ -0,0 +1,62 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Unit test for the "user substitutions" that are annotated on each
+// node.
+
+#![feature(nll)]
+
+enum SomeEnum<T> {
+    SomeVariant { t: T }
+}
+
+fn no_annot() {
+    let c = 66;
+    SomeEnum::SomeVariant { t: &c };
+}
+
+fn annot_underscore() {
+    let c = 66;
+    SomeEnum::SomeVariant::<_> { t: &c };
+}
+
+fn annot_reference_any_lifetime() {
+    let c = 66;
+    SomeEnum::SomeVariant::<&u32> { t: &c };
+}
+
+fn annot_reference_static_lifetime() {
+    let c = 66;
+    SomeEnum::SomeVariant::<&'static u32> { t: &c }; //~ ERROR
+}
+
+fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+    let c = 66;
+    SomeEnum::SomeVariant::<&'a u32> { t: &c }; //~ ERROR
+}
+
+fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) {
+    SomeEnum::SomeVariant::<&'a u32> { t: c };
+}
+
+fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+    let _closure = || {
+        let c = 66;
+        SomeEnum::SomeVariant::<&'a u32> { t: &c }; //~ ERROR
+    };
+}
+
+fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) {
+    let _closure = || {
+        SomeEnum::SomeVariant::<&'a u32> { t: c };
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/adt-brace-enums.stderr b/src/test/ui/nll/user-annotations/adt-brace-enums.stderr
new file mode 100644
index 00000000000..842bb622bc3
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-brace-enums.stderr
@@ -0,0 +1,41 @@
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-brace-enums.rs:37:48
+   |
+LL |     SomeEnum::SomeVariant::<&'static u32> { t: &c }; //~ ERROR
+   |                                                ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-brace-enums.rs:42:43
+   |
+LL |     SomeEnum::SomeVariant::<&'a u32> { t: &c }; //~ ERROR
+   |                                           ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 40:35...
+  --> $DIR/adt-brace-enums.rs:40:35
+   |
+LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+   |                                   ^^
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-brace-enums.rs:52:47
+   |
+LL |         SomeEnum::SomeVariant::<&'a u32> { t: &c }; //~ ERROR
+   |                                               ^^ borrowed value does not live long enough
+LL |     };
+   |     - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 49:46...
+  --> $DIR/adt-brace-enums.rs:49:46
+   |
+LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+   |                                              ^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/user-annotations/adt-brace-structs.rs b/src/test/ui/nll/user-annotations/adt-brace-structs.rs
new file mode 100644
index 00000000000..c479e050564
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-brace-structs.rs
@@ -0,0 +1,60 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Unit test for the "user substitutions" that are annotated on each
+// node.
+
+#![feature(nll)]
+
+struct SomeStruct<T> { t: T }
+
+fn no_annot() {
+    let c = 66;
+    SomeStruct { t: &c };
+}
+
+fn annot_underscore() {
+    let c = 66;
+    SomeStruct::<_> { t: &c };
+}
+
+fn annot_reference_any_lifetime() {
+    let c = 66;
+    SomeStruct::<&u32> { t: &c };
+}
+
+fn annot_reference_static_lifetime() {
+    let c = 66;
+    SomeStruct::<&'static u32> { t: &c }; //~ ERROR
+}
+
+fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+    let c = 66;
+    SomeStruct::<&'a u32> { t: &c }; //~ ERROR
+}
+
+fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) {
+    SomeStruct::<&'a u32> { t: c };
+}
+
+fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+    let _closure = || {
+        let c = 66;
+        SomeStruct::<&'a u32> { t: &c }; //~ ERROR
+    };
+}
+
+fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) {
+    let _closure = || {
+        SomeStruct::<&'a u32> { t: c };
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/adt-brace-structs.stderr b/src/test/ui/nll/user-annotations/adt-brace-structs.stderr
new file mode 100644
index 00000000000..7ba76212dc5
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-brace-structs.stderr
@@ -0,0 +1,41 @@
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-brace-structs.rs:35:37
+   |
+LL |     SomeStruct::<&'static u32> { t: &c }; //~ ERROR
+   |                                     ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-brace-structs.rs:40:32
+   |
+LL |     SomeStruct::<&'a u32> { t: &c }; //~ ERROR
+   |                                ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 38:35...
+  --> $DIR/adt-brace-structs.rs:38:35
+   |
+LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+   |                                   ^^
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-brace-structs.rs:50:36
+   |
+LL |         SomeStruct::<&'a u32> { t: &c }; //~ ERROR
+   |                                    ^^ borrowed value does not live long enough
+LL |     };
+   |     - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 47:46...
+  --> $DIR/adt-brace-structs.rs:47:46
+   |
+LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+   |                                              ^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/user-annotations/adt-nullary-enums.rs b/src/test/ui/nll/user-annotations/adt-nullary-enums.rs
new file mode 100644
index 00000000000..d4ffba10cb6
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-nullary-enums.rs
@@ -0,0 +1,68 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Unit test for the "user substitutions" that are annotated on each
+// node.
+
+#![feature(nll)]
+#![allow(warnings)]
+
+use std::cell::Cell;
+
+enum SomeEnum<T> {
+    SomeVariant(T),
+    SomeOtherVariant,
+}
+
+fn combine<T>(_: T, _: T) { }
+
+fn no_annot() {
+    let c = 66;
+    combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant);
+}
+
+fn annot_underscore() {
+    let c = 66;
+    combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<_>>);
+}
+
+fn annot_reference_any_lifetime() {
+    let c = 66;
+    combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&u32>>);
+}
+
+fn annot_reference_static_lifetime() {
+    let c = 66;
+    combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&'static u32>>); //~ ERROR
+}
+
+fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+    let c = 66;
+    combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>); //~ ERROR
+}
+
+fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) {
+    combine(SomeEnum::SomeVariant(Cell::new(c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>);
+}
+
+fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+    let _closure = || {
+        let c = 66;
+        combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>); //~ ERROR
+    };
+}
+
+fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) {
+    let _closure = || {
+        combine(SomeEnum::SomeVariant(Cell::new(c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>);
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/adt-nullary-enums.stderr b/src/test/ui/nll/user-annotations/adt-nullary-enums.stderr
new file mode 100644
index 00000000000..b120d6c4e5d
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-nullary-enums.stderr
@@ -0,0 +1,41 @@
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-nullary-enums.rs:43:45
+   |
+LL |     combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&'static u32>>); //~ ERROR
+   |                                             ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-nullary-enums.rs:48:45
+   |
+LL |     combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>); //~ ERROR
+   |                                             ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 46:35...
+  --> $DIR/adt-nullary-enums.rs:46:35
+   |
+LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+   |                                   ^^
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-nullary-enums.rs:58:49
+   |
+LL |         combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>); //~ ERROR
+   |                                                 ^^ borrowed value does not live long enough
+LL |     };
+   |     - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 55:46...
+  --> $DIR/adt-nullary-enums.rs:55:46
+   |
+LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+   |                                              ^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/user-annotations/adt-tuple-enums.rs b/src/test/ui/nll/user-annotations/adt-tuple-enums.rs
new file mode 100644
index 00000000000..ae75a54f649
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-tuple-enums.rs
@@ -0,0 +1,64 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Unit test for the "user substitutions" that are annotated on each
+// node.
+
+#![feature(nll)]
+#![allow(warnings)]
+
+enum SomeEnum<T> {
+    SomeVariant(T),
+    SomeOtherVariant,
+}
+
+fn no_annot() {
+    let c = 66;
+    SomeEnum::SomeVariant(&c);
+}
+
+fn annot_underscore() {
+    let c = 66;
+    SomeEnum::SomeVariant::<_>(&c);
+}
+
+fn annot_reference_any_lifetime() {
+    let c = 66;
+    SomeEnum::SomeVariant::<&u32>(&c);
+}
+
+fn annot_reference_static_lifetime() {
+    let c = 66;
+    SomeEnum::SomeVariant::<&'static u32>(&c); //~ ERROR
+}
+
+fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+    let c = 66;
+    SomeEnum::SomeVariant::<&'a u32>(&c); //~ ERROR
+}
+
+fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) {
+    SomeEnum::SomeVariant::<&'a u32>(c);
+}
+
+fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+    let _closure = || {
+        let c = 66;
+        SomeEnum::SomeVariant::<&'a u32>(&c); //~ ERROR
+    };
+}
+
+fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) {
+    let _closure = || {
+        SomeEnum::SomeVariant::<&'a u32>(c);
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/adt-tuple-enums.stderr b/src/test/ui/nll/user-annotations/adt-tuple-enums.stderr
new file mode 100644
index 00000000000..18d1cc7e087
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-tuple-enums.stderr
@@ -0,0 +1,41 @@
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-tuple-enums.rs:39:43
+   |
+LL |     SomeEnum::SomeVariant::<&'static u32>(&c); //~ ERROR
+   |                                           ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-tuple-enums.rs:44:38
+   |
+LL |     SomeEnum::SomeVariant::<&'a u32>(&c); //~ ERROR
+   |                                      ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 42:35...
+  --> $DIR/adt-tuple-enums.rs:42:35
+   |
+LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+   |                                   ^^
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-tuple-enums.rs:54:42
+   |
+LL |         SomeEnum::SomeVariant::<&'a u32>(&c); //~ ERROR
+   |                                          ^^ borrowed value does not live long enough
+LL |     };
+   |     - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 51:46...
+  --> $DIR/adt-tuple-enums.rs:51:46
+   |
+LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+   |                                              ^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct.rs b/src/test/ui/nll/user-annotations/adt-tuple-struct.rs
new file mode 100644
index 00000000000..401a71c1fe5
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-tuple-struct.rs
@@ -0,0 +1,60 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Unit test for the "user substitutions" that are annotated on each
+// node.
+
+#![feature(nll)]
+
+struct SomeStruct<T>(T);
+
+fn no_annot() {
+    let c = 66;
+    SomeStruct(&c);
+}
+
+fn annot_underscore() {
+    let c = 66;
+    SomeStruct::<_>(&c);
+}
+
+fn annot_reference_any_lifetime() {
+    let c = 66;
+    SomeStruct::<&u32>(&c);
+}
+
+fn annot_reference_static_lifetime() {
+    let c = 66;
+    SomeStruct::<&'static u32>(&c); //~ ERROR
+}
+
+fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+    let c = 66;
+    SomeStruct::<&'a u32>(&c); //~ ERROR
+}
+
+fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) {
+    SomeStruct::<&'a u32>(c);
+}
+
+fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+    let _closure = || {
+        let c = 66;
+        SomeStruct::<&'a u32>(&c); //~ ERROR
+    };
+}
+
+fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) {
+    let _closure = || {
+        SomeStruct::<&'a u32>(c);
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct.stderr b/src/test/ui/nll/user-annotations/adt-tuple-struct.stderr
new file mode 100644
index 00000000000..397016e52d0
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/adt-tuple-struct.stderr
@@ -0,0 +1,41 @@
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-tuple-struct.rs:35:32
+   |
+LL |     SomeStruct::<&'static u32>(&c); //~ ERROR
+   |                                ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-tuple-struct.rs:40:27
+   |
+LL |     SomeStruct::<&'a u32>(&c); //~ ERROR
+   |                           ^^ borrowed value does not live long enough
+LL | }
+   | - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 38:35...
+  --> $DIR/adt-tuple-struct.rs:38:35
+   |
+LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
+   |                                   ^^
+
+error[E0597]: `c` does not live long enough
+  --> $DIR/adt-tuple-struct.rs:50:31
+   |
+LL |         SomeStruct::<&'a u32>(&c); //~ ERROR
+   |                               ^^ borrowed value does not live long enough
+LL |     };
+   |     - `c` dropped here while still borrowed
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 47:46...
+  --> $DIR/adt-tuple-struct.rs:47:46
+   |
+LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
+   |                                              ^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs
new file mode 100644
index 00000000000..970fbf98af4
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs
@@ -0,0 +1,29 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Unit test for the "user substitutions" that are annotated on each
+// node.
+
+// compile-flags:-Zverbose
+
+#![allow(warnings)]
+#![feature(nll)]
+#![feature(rustc_attrs)]
+
+struct SomeStruct<T> { t: T }
+
+#[rustc_dump_user_substs]
+fn main() {
+    SomeStruct { t: 22 }; // Nothing given, no annotation.
+
+    SomeStruct::<_> { t: 22 }; // Nothing interesting given, no annotation.
+
+    SomeStruct::<u32> { t: 22 }; //~ ERROR [u32]
+}
diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr
new file mode 100644
index 00000000000..2b0e5039d8d
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr
@@ -0,0 +1,8 @@
+error: user substs: Canonical { variables: [], value: [u32] }
+  --> $DIR/dump-adt-brace-struct.rs:28:5
+   |
+LL |     SomeStruct::<u32> { t: 22 }; //~ ERROR [u32]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+