about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDavid Wood <david@davidtw.co>2018-03-15 18:49:10 +0000
committerDavid Wood <david@davidtw.co>2018-03-22 21:11:02 +0000
commit692a931887b23cf11ddf69191ea8d6d509a7bd10 (patch)
tree0b249a80e66fec5b05e232caba6159b49042533c /src
parent5d2a60c57e00c8ced93240c5ed1039f3ddb37397 (diff)
UserAssertTy can handle inference variables.
This commit modifies the UserAssertTy statement to take a canonicalized
type rather than a regular type so that we can handle the case where the
user provided type contains a inference variable.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ich/impls_mir.rs4
-rw-r--r--src/librustc/infer/canonical.rs29
-rw-r--r--src/librustc/mir/mod.rs7
-rw-r--r--src/librustc/mir/visit.rs13
-rw-r--r--src/librustc/ty/context.rs39
-rw-r--r--src/librustc/ty/mod.rs12
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/renumber.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs8
-rw-r--r--src/librustc_mir/build/matches/mod.rs18
-rw-r--r--src/librustc_mir/hair/cx/block.rs2
-rw-r--r--src/librustc_mir/hair/mod.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs13
-rw-r--r--src/librustc_typeck/check/writeback.rs28
14 files changed, 146 insertions, 38 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index ff73f135ce6..c73f171806e 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -277,8 +277,8 @@ for mir::StatementKind<'gcx> {
                 op.hash_stable(hcx, hasher);
                 places.hash_stable(hcx, hasher);
             }
-            mir::StatementKind::UserAssertTy(ref ty, ref local) => {
-                ty.hash_stable(hcx, hasher);
+            mir::StatementKind::UserAssertTy(ref c_ty, ref local) => {
+                c_ty.hash_stable(hcx, hasher);
                 local.hash_stable(hcx, hasher);
             }
             mir::StatementKind::Nop => {}
diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs
index 22526c7751d..debddd708ea 100644
--- a/src/librustc/infer/canonical.rs
+++ b/src/librustc/infer/canonical.rs
@@ -33,6 +33,7 @@
 
 use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin};
 use rustc_data_structures::indexed_vec::Idx;
+use serialize::UseSpecializedDecodable;
 use std::fmt::Debug;
 use std::ops::Index;
 use syntax::codemap::Span;
@@ -49,7 +50,7 @@ use rustc_data_structures::fx::FxHashMap;
 /// A "canonicalized" type `V` is one where all free inference
 /// variables have been rewriten to "canonical vars". These are
 /// numbered starting from 0 in order of first appearance.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
 pub struct Canonical<'gcx, V> {
     pub variables: CanonicalVarInfos<'gcx>,
     pub value: V,
@@ -57,6 +58,8 @@ pub struct Canonical<'gcx, V> {
 
 pub type CanonicalVarInfos<'gcx> = &'gcx Slice<CanonicalVarInfo>;
 
+impl<'gcx> UseSpecializedDecodable for CanonicalVarInfos<'gcx> { }
+
 /// A set of values corresponding to the canonical variables from some
 /// `Canonical`. You can give these values to
 /// `canonical_value.substitute` to substitute them into the canonical
@@ -69,7 +72,7 @@ pub type CanonicalVarInfos<'gcx> = &'gcx Slice<CanonicalVarInfo>;
 /// You can also use `infcx.fresh_inference_vars_for_canonical_vars`
 /// to get back a `CanonicalVarValues` containing fresh inference
 /// variables.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
 pub struct CanonicalVarValues<'tcx> {
     pub var_values: IndexVec<CanonicalVar, Kind<'tcx>>,
 }
@@ -78,7 +81,7 @@ pub struct CanonicalVarValues<'tcx> {
 /// canonical value. This is sufficient information for code to create
 /// a copy of the canonical value in some other inference context,
 /// with fresh inference variables replacing the canonical values.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
 pub struct CanonicalVarInfo {
     pub kind: CanonicalVarKind,
 }
@@ -86,7 +89,7 @@ pub struct CanonicalVarInfo {
 /// Describes the "kind" of the canonical variable. This is a "kind"
 /// in the type-theory sense of the term -- i.e., a "meta" type system
 /// that analyzes type-like values.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
 pub enum CanonicalVarKind {
     /// Some kind of type inference variable.
     Ty(CanonicalTyVarKind),
@@ -100,7 +103,7 @@ pub enum CanonicalVarKind {
 /// 22.) can only be instantiated with integral/float types (e.g.,
 /// usize or f32). In order to faithfully reproduce a type, we need to
 /// know what set of types a given type variable can be unified with.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
 pub enum CanonicalTyVarKind {
     /// General type variable `?T` that can be unified with arbitrary types.
     General,
@@ -855,11 +858,14 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g
 }
 
 CloneTypeFoldableAndLiftImpls! {
+    ::infer::canonical::Certainty,
+    ::infer::canonical::CanonicalVarInfo,
+    ::infer::canonical::CanonicalVarKind,
+}
+
+CloneTypeFoldableImpls! {
     for <'tcx> {
-        ::infer::canonical::Certainty,
-        ::infer::canonical::CanonicalVarInfo,
         ::infer::canonical::CanonicalVarInfos<'tcx>,
-        ::infer::canonical::CanonicalVarKind,
     }
 }
 
@@ -870,6 +876,13 @@ BraceStructTypeFoldableImpl! {
     } where C: TypeFoldable<'tcx>
 }
 
+BraceStructLiftImpl! {
+    impl<'a, 'tcx, T> Lift<'tcx> for Canonical<'a, T> {
+        type Lifted = Canonical<'tcx, T::Lifted>;
+        variables, value
+    } where T: Lift<'tcx>
+}
+
 impl<'tcx> CanonicalVarValues<'tcx> {
     fn iter<'a>(&'a self) -> impl Iterator<Item = Kind<'tcx>> + 'a {
         self.var_values.iter().cloned()
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index c36e401e8d6..9229724f094 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -27,7 +27,7 @@ use hir::def_id::DefId;
 use mir::visit::MirVisitable;
 use mir::interpret::{Value, PrimVal};
 use ty::subst::{Subst, Substs};
-use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior};
+use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use ty::TypeAndMut;
 use util::ppaux;
@@ -1260,7 +1260,7 @@ pub enum StatementKind<'tcx> {
     ///
     /// Here we would insert a `UserAssertTy<(T, U)>(y)` instruction to check that the type of `y`
     /// is the right thing.
-    UserAssertTy(Ty<'tcx>, Local),
+    UserAssertTy(CanonicalTy<'tcx>, Local),
 
     /// No-op. Useful for deleting instructions without affecting statement indices.
     Nop,
@@ -1333,7 +1333,8 @@ impl<'tcx> Debug for Statement<'tcx> {
             InlineAsm { ref asm, ref outputs, ref inputs } => {
                 write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs)
             },
-            UserAssertTy(ref ty, ref local) => write!(fmt, "UserAssertTy({:?}, {:?})", ty, local),
+            UserAssertTy(ref c_ty, ref local) => write!(fmt, "UserAssertTy({:?}, {:?})",
+                                                        c_ty, local),
             Nop => write!(fmt, "nop"),
         }
     }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index cc5d9692e7a..a3fdb6f73ab 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -10,7 +10,7 @@
 
 use hir::def_id::DefId;
 use ty::subst::Substs;
-use ty::{ClosureSubsts, Region, Ty, GeneratorInterior};
+use ty::{CanonicalTy, ClosureSubsts, Region, Ty, GeneratorInterior};
 use mir::*;
 use syntax_pos::Span;
 
@@ -145,10 +145,10 @@ macro_rules! make_mir_visitor {
             }
 
             fn visit_user_assert_ty(&mut self,
-                                    ty: & $($mutability)* Ty<'tcx>,
+                                    c_ty: & $($mutability)* CanonicalTy<'tcx>,
                                     local: & $($mutability)* Local,
                                     location: Location) {
-                self.super_user_assert_ty(ty, local, location);
+                self.super_user_assert_ty(c_ty, local, location);
             }
 
             fn visit_place(&mut self,
@@ -383,9 +383,9 @@ macro_rules! make_mir_visitor {
                             self.visit_operand(input, location);
                         }
                     }
-                    StatementKind::UserAssertTy(ref $($mutability)* ty,
+                    StatementKind::UserAssertTy(ref $($mutability)* c_ty,
                                                 ref $($mutability)* local) => {
-                        self.visit_user_assert_ty(ty, local, location);
+                        self.visit_user_assert_ty(c_ty, local, location);
                     }
                     StatementKind::Nop => {}
                 }
@@ -631,10 +631,9 @@ macro_rules! make_mir_visitor {
             }
 
             fn super_user_assert_ty(&mut self,
-                                    ty: & $($mutability)* Ty<'tcx>,
+                                    _c_ty: & $($mutability)* CanonicalTy<'tcx>,
                                     local: & $($mutability)* Local,
                                     location: Location) {
-                self.visit_ty(ty, TyContext::Location(location));
                 self.visit_local(local, PlaceContext::Validate, location);
             }
 
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index fd3465f59eb..573d2771322 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -48,6 +48,7 @@ use ty::layout::{LayoutDetails, TargetDataLayout};
 use ty::maps;
 use ty::steal::Steal;
 use ty::BindingMode;
+use ty::CanonicalTy;
 use util::nodemap::{NodeMap, DefIdSet, ItemLocalMap};
 use util::nodemap::{FxHashMap, FxHashSet};
 use rustc_data_structures::accumulate_vec::AccumulateVec;
@@ -344,6 +345,9 @@ pub struct TypeckTables<'tcx> {
     /// method calls, including those of overloaded operators.
     type_dependent_defs: ItemLocalMap<Def>,
 
+    /// Stores the canonicalized types provided by the user.
+    user_provided_tys: ItemLocalMap<CanonicalTy<'tcx>>,
+
     /// Stores the types for various nodes in the AST.  Note that this table
     /// is not guaranteed to be populated until after typeck.  See
     /// typeck::check::fn_ctxt for details.
@@ -420,6 +424,7 @@ impl<'tcx> TypeckTables<'tcx> {
         TypeckTables {
             local_id_root,
             type_dependent_defs: ItemLocalMap(),
+            user_provided_tys: ItemLocalMap(),
             node_types: ItemLocalMap(),
             node_substs: ItemLocalMap(),
             adjustments: ItemLocalMap(),
@@ -461,6 +466,20 @@ impl<'tcx> TypeckTables<'tcx> {
         }
     }
 
+    pub fn user_provided_tys(&self) -> LocalTableInContext<CanonicalTy<'tcx>> {
+        LocalTableInContext {
+            local_id_root: self.local_id_root,
+            data: &self.user_provided_tys
+        }
+    }
+
+    pub fn user_provided_tys_mut(&mut self) -> LocalTableInContextMut<CanonicalTy<'tcx>> {
+        LocalTableInContextMut {
+            local_id_root: self.local_id_root,
+            data: &mut self.user_provided_tys
+        }
+    }
+
     pub fn node_types(&self) -> LocalTableInContext<Ty<'tcx>> {
         LocalTableInContext {
             local_id_root: self.local_id_root,
@@ -685,6 +704,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
         let ty::TypeckTables {
             local_id_root,
             ref type_dependent_defs,
+            ref user_provided_tys,
             ref node_types,
             ref node_substs,
             ref adjustments,
@@ -704,6 +724,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
 
         hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
             type_dependent_defs.hash_stable(hcx, hasher);
+            user_provided_tys.hash_stable(hcx, hasher);
             node_types.hash_stable(hcx, hasher);
             node_substs.hash_stable(hcx, hasher);
             adjustments.hash_stable(hcx, hasher);
@@ -1635,6 +1656,24 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice<Predicate<'a>> {
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for &'a Slice<CanonicalVarInfo> {
+    type Lifted = &'tcx Slice<CanonicalVarInfo>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        if self.len() == 0 {
+            return Some(Slice::empty());
+        }
+        if tcx.interners.arena.in_arena(*self as *const _) {
+            return Some(unsafe { mem::transmute(*self) });
+        }
+        // Also try in the global tcx if we're not that.
+        if !tcx.is_global() {
+            self.lift_to_tcx(tcx.global_tcx())
+        } else {
+            None
+        }
+    }
+}
+
 pub mod tls {
     use super::{CtxtInterners, GlobalCtxt, TyCtxt};
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 95c5cd377d7..68bc2449711 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -21,6 +21,7 @@ use hir::map::DefPathData;
 use hir::svh::Svh;
 use ich::Fingerprint;
 use ich::StableHashingContext;
+use infer::canonical::{Canonical, Canonicalize};
 use middle::const_val::ConstVal;
 use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
 use middle::privacy::AccessLevels;
@@ -554,6 +555,17 @@ pub type Ty<'tcx> = &'tcx TyS<'tcx>;
 impl<'tcx> serialize::UseSpecializedEncodable for Ty<'tcx> {}
 impl<'tcx> serialize::UseSpecializedDecodable for Ty<'tcx> {}
 
+pub type CanonicalTy<'gcx> = Canonical<'gcx, Ty<'gcx>>;
+
+impl <'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for Ty<'tcx> {
+    type Canonicalized = CanonicalTy<'gcx>;
+
+    fn intern(_gcx: TyCtxt<'_, 'gcx, 'gcx>,
+              value: Canonical<'gcx, Self::Lifted>) -> Self::Canonicalized {
+        value
+    }
+}
+
 /// A wrapper for slices with the additional invariant
 /// that the slice is interned and no other slice with
 /// the same contents can exist in the same context.
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index c06f77b2d84..afaedecdf0a 100644
--- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -15,7 +15,7 @@ use rustc::mir::Place::Projection;
 use rustc::mir::{Local, PlaceProjection, ProjectionElem};
 use rustc::mir::visit::TyContext;
 use rustc::infer::InferCtxt;
-use rustc::ty::{self, ClosureSubsts, Ty};
+use rustc::ty::{self, CanonicalTy, ClosureSubsts};
 use rustc::ty::subst::Substs;
 use rustc::ty::fold::TypeFoldable;
 
@@ -107,7 +107,8 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
         self.super_rvalue(rvalue, location);
     }
 
-    fn visit_user_assert_ty(&mut self, _ty: &Ty<'tcx>, _local: &Local, _location: Location) { }
+    fn visit_user_assert_ty(&mut self, _c_ty: &CanonicalTy<'tcx>,
+                            _local: &Local, _location: Location) { }
 }
 
 impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
index 38a14be8eb2..04c206b5c0c 100644
--- a/src/librustc_mir/borrow_check/nll/renumber.rs
+++ b/src/librustc_mir/borrow_check/nll/renumber.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use rustc::ty::subst::Substs;
-use rustc::ty::{self, ClosureSubsts, GeneratorInterior, Ty, TypeFoldable};
+use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorInterior, Ty, TypeFoldable};
 use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
 use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
@@ -118,7 +118,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
         debug!("visit_closure_substs: substs={:?}", substs);
     }
 
-    fn visit_user_assert_ty(&mut self, _ty: &mut Ty<'tcx>, _local: &mut Local,
+    fn visit_user_assert_ty(&mut self, _c_ty: &mut CanonicalTy<'tcx>, _local: &mut Local,
                             _location: Location) {
         // User-assert-ty statements represent types that the user added explicitly.
         // We don't want to erase the regions from these types: rather, we want to
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 2c06c93cd9d..80a439b1830 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -761,12 +761,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     );
                 };
             }
-            StatementKind::UserAssertTy(ref ty, ref local) => {
+            StatementKind::UserAssertTy(ref c_ty, ref local) => {
                 let local_ty = mir.local_decls()[*local].ty;
+                let (ty, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
+                    stmt.source_info.span, c_ty);
                 debug!("check_stmt: user_assert_ty ty={:?} local_ty={:?}", ty, local_ty);
-                if let Err(terr) =
-                    self.eq_types(ty, local_ty, location.at_self())
-                {
+                if let Err(terr) = self.eq_types(ty, local_ty, location.at_self()) {
                     span_mirbug!(
                         self,
                         stmt,
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 4353eb5e4bd..b9a6616fd07 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -145,18 +145,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         end_block.unit()
     }
 
-    pub fn user_assert_ty(&mut self, block: BasicBlock, ty: Ty<'tcx>, var: NodeId, span: Span) {
+    pub fn user_assert_ty(&mut self, block: BasicBlock, hir_id: hir::HirId,
+                          var: NodeId, span: Span) {
         let local_id = self.var_indices[&var];
         let source_info = self.source_info(span);
-        self.cfg.push(block, Statement {
-            source_info,
-            kind: StatementKind::UserAssertTy(ty, local_id),
-        });
+
+        debug!("user_assert_ty: local_id={:?}", hir_id.local_id);
+        if let Some(c_ty) = self.hir.tables.user_provided_tys().get(hir_id) {
+            debug!("user_assert_ty: c_ty={:?}", c_ty);
+            self.cfg.push(block, Statement {
+                source_info,
+                kind: StatementKind::UserAssertTy(*c_ty, local_id),
+            });
+        }
     }
 
     pub fn expr_into_pattern(&mut self,
                              mut block: BasicBlock,
-                             ty: Option<Ty<'tcx>>,
+                             ty: Option<hir::HirId>,
                              irrefutable_pat: Pattern<'tcx>,
                              initializer: ExprRef<'tcx>)
                              -> BlockAnd<()> {
diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs
index f0497338303..14aa307f0ae 100644
--- a/src/librustc_mir/hair/cx/block.rs
+++ b/src/librustc_mir/hair/cx/block.rs
@@ -76,7 +76,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                             first_statement_index: region::FirstStatementIndex::new(index),
                         });
 
-                        let ty = local.ty.clone().map(|ty| cx.tables().node_id_to_type(ty.hir_id));
+                        let ty = local.ty.clone().map(|ty| ty.hir_id);
                         let pattern = cx.pattern_from_hir(&local.pat);
                         result.push(StmtRef::Mirror(Box::new(Stmt {
                             kind: StmtKind::Let {
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 138944189ff..fe82b8158f7 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -97,7 +97,7 @@ pub enum StmtKind<'tcx> {
         pattern: Pattern<'tcx>,
 
         /// let pat: <TY> = init ...
-        ty: Option<Ty<'tcx>>,
+        ty: Option<hir::HirId>,
 
         /// let pat: ty = <INIT> ...
         initializer: Option<ExprRef<'tcx>>,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 69879bbe85d..e2f6c965c18 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -960,10 +960,19 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
     // Add explicitly-declared locals.
     fn visit_local(&mut self, local: &'gcx hir::Local) {
         let o_ty = match local.ty {
-            Some(ref ty) => Some(self.fcx.to_ty(&ty)),
-            None => None
+            Some(ref ty) => {
+                let o_ty = self.fcx.to_ty(&ty);
+
+                let (c_ty, _orig_values) = self.fcx.inh.infcx.canonicalize_query(&o_ty);
+                debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
+                self.fcx.tables.borrow_mut().user_provided_tys_mut().insert(ty.hir_id, c_ty);
+
+                Some(o_ty)
+            },
+            None => None,
         };
         self.assign(local.span, local.id, o_ty);
+
         debug!("Local variable {:?} is assigned type {}",
                local.pat,
                self.fcx.ty_to_string(
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 862b15743c7..bbd04e0b19a 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -46,6 +46,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         wbcx.visit_anon_types();
         wbcx.visit_cast_types();
         wbcx.visit_free_region_map();
+        wbcx.visit_user_provided_tys();
 
         let used_trait_imports = mem::replace(
             &mut self.tables.borrow_mut().used_trait_imports,
@@ -341,6 +342,33 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
         self.tables.free_region_map = free_region_map;
     }
 
+    fn visit_user_provided_tys(&mut self) {
+        let fcx_tables = self.fcx.tables.borrow();
+        debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root);
+        let common_local_id_root = fcx_tables.local_id_root.unwrap();
+
+        for (&local_id, c_ty) in fcx_tables.user_provided_tys().iter() {
+            let hir_id = hir::HirId {
+                owner: common_local_id_root.index,
+                local_id,
+            };
+
+            let c_ty = if let Some(c_ty) = self.tcx().lift_to_global(c_ty) {
+                c_ty
+            } else {
+                span_bug!(
+                    hir_id.to_span(&self.fcx.tcx),
+                    "writeback: `{:?}` missing from the global type context",
+                    c_ty
+                );
+            };
+
+            self.tables
+                .user_provided_tys_mut()
+                .insert(hir_id, c_ty.clone());
+        }
+    }
+
     fn visit_anon_types(&mut self) {
         let gcx = self.tcx().global_tcx();
         for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() {