diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 8b67f04e020..c4de95c60bf 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -16,8 +16,8 @@ //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html use infer::canonical::{ - Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, CanonicalVarValues, - Canonicalized, + Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized, + SmallCanonicalVarValues, }; use infer::InferCtxt; use std::sync::atomic::Ordering; @@ -26,7 +26,8 @@ use ty::subst::Kind; use ty::{self, CanonicalVar, Lift, Slice, Ty, TyCtxt, TypeFlags}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::small_vec::SmallVec; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Canonicalizes a query value `V`. When we canonicalize a query, @@ -47,7 +48,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { pub fn canonicalize_query( &self, value: &V, - ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) + var_values: &mut SmallCanonicalVarValues<'tcx> + ) -> Canonicalized<'gcx, V> where V: TypeFoldable<'tcx> + Lift<'gcx>, { @@ -65,6 +67,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { static_region: true, other_free_regions: true, }, + var_values, ) } @@ -96,10 +99,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { pub fn canonicalize_response( &self, value: &V, - ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) + ) -> Canonicalized<'gcx, V> where V: TypeFoldable<'tcx> + Lift<'gcx>, { + let mut var_values = SmallVec::new(); Canonicalizer::canonicalize( value, Some(self), @@ -108,6 +112,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { static_region: false, other_free_regions: false, }, + &mut var_values ) } @@ -123,7 +128,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { pub fn canonicalize_hr_query_hack( &self, value: &V, - ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) + var_values: &mut SmallCanonicalVarValues<'tcx> + ) -> Canonicalized<'gcx, V> where V: TypeFoldable<'tcx> + Lift<'gcx>, { @@ -141,6 +147,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { static_region: false, other_free_regions: true, }, + var_values ) } } @@ -163,9 +170,11 @@ impl CanonicalizeRegionMode { struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, tcx: TyCtxt<'cx, 'gcx, 'tcx>, - variables: IndexVec, + variables: SmallVec<[CanonicalVarInfo; 8]>, + var_values: &'cx mut SmallCanonicalVarValues<'tcx>, + // Note that indices is only used once `var_values` is big enough to be + // heap-allocated. indices: FxHashMap, CanonicalVar>, - var_values: IndexVec>, canonicalize_region_mode: CanonicalizeRegionMode, needs_canonical_flags: TypeFlags, } @@ -295,7 +304,8 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, tcx: TyCtxt<'cx, 'gcx, 'tcx>, canonicalize_region_mode: CanonicalizeRegionMode, - ) -> (Canonicalized<'gcx, V>, CanonicalVarValues<'tcx>) + var_values: &'cx mut SmallCanonicalVarValues<'tcx> + ) -> Canonicalized<'gcx, V> where V: TypeFoldable<'tcx> + Lift<'gcx>, { @@ -320,10 +330,7 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { variables: Slice::empty(), value: out_value, }; - let values = CanonicalVarValues { - var_values: IndexVec::default(), - }; - return (canon_value, values); + return canon_value; } let mut canonicalizer = Canonicalizer { @@ -331,9 +338,9 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { tcx, canonicalize_region_mode, needs_canonical_flags, - variables: IndexVec::default(), + variables: SmallVec::new(), + var_values, indices: FxHashMap::default(), - var_values: IndexVec::default(), }; let out_value = value.fold_with(&mut canonicalizer); @@ -348,16 +355,12 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { ) }); - let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables.raw); + let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables); - let canonical_value = Canonical { + Canonical { variables: canonical_variables, value: out_value, - }; - let canonical_var_values = CanonicalVarValues { - var_values: canonicalizer.var_values, - }; - (canonical_value, canonical_var_values) + } } /// Creates a canonical variable replacing `kind` from the input, @@ -366,21 +369,54 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { /// potentially a free region). fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar { let Canonicalizer { - indices, variables, var_values, + indices, .. } = self; - indices - .entry(kind) - .or_insert_with(|| { - let cvar1 = variables.push(info); - let cvar2 = var_values.push(kind); - assert_eq!(cvar1, cvar2); - cvar1 - }) - .clone() + // This code is hot. `variables` and `var_values` are usually small + // (fewer than 8 elements ~95% of the time). They are SmallVec's to + // avoid allocations in those cases. We also don't use `indices` to + // determine if a kind has been seen before until the limit of 8 has + // been exceeded, to also avoid allocations for `indices`. + if var_values.is_array() { + // `var_values` is stack-allocated. `indices` isn't used yet. Do a + // direct linear search of `var_values`. + if let Some(idx) = var_values.iter().position(|&k| k == kind) { + // `kind` is already present in `var_values`. + CanonicalVar::new(idx) + } else { + // `kind` isn't present in `var_values`. Append it. Likewise + // for `info` and `variables`. + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + + // If `var_values` has become big enough to be heap-allocated, + // fill up `indices` to facilitate subsequent lookups. + if !var_values.is_array() { + assert!(indices.is_empty()); + *indices = + var_values.iter() + .enumerate() + .map(|(i, &kind)| (kind, CanonicalVar::new(i))) + .collect(); + } + // The cv is the index of the appended element. + CanonicalVar::new(var_values.len() - 1) + } + } else { + // `var_values` is large. Do a hashmap search via `indices`. + *indices + .entry(kind) + .or_insert_with(|| { + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + CanonicalVar::new(variables.len() - 1) + }) + } } /// Given a type variable `ty_var` of the given kind, first check diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 62424ff9226..958b3391060 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -33,6 +33,7 @@ use infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::small_vec::SmallVec; use rustc_data_structures::sync::Lrc; use serialize::UseSpecializedDecodable; use std::ops::Index; @@ -74,6 +75,10 @@ pub struct CanonicalVarValues<'tcx> { pub var_values: IndexVec>, } +/// Like CanonicalVarValues, but for use in places where a SmallVec is +/// appropriate. +pub type SmallCanonicalVarValues<'tcx> = SmallVec<[Kind<'tcx>; 8]>; + /// Information about a canonical variable that is included with the /// canonical value. This is sufficient information for code to create /// a copy of the canonical value in some other inference context, @@ -281,10 +286,6 @@ BraceStructLiftImpl! { } impl<'tcx> CanonicalVarValues<'tcx> { - fn iter<'a>(&'a self) -> impl Iterator> + 'a { - self.var_values.iter().cloned() - } - fn len(&self) -> usize { self.var_values.len() } diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index b8b13e03afa..02684d962ba 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -19,7 +19,7 @@ use infer::canonical::substitute::substitute_value; use infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues, CanonicalizedQueryResult, - Certainty, QueryRegionConstraint, QueryResult}; + Certainty, QueryRegionConstraint, QueryResult, SmallCanonicalVarValues}; use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::InferCtxtBuilder; use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; @@ -103,7 +103,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { T: Debug + Lift<'gcx> + TypeFoldable<'tcx>, { let query_result = self.make_query_result(inference_vars, answer, fulfill_cx)?; - let (canonical_result, _) = self.canonicalize_response(&query_result); + let canonical_result = self.canonicalize_response(&query_result); debug!( "make_canonicalized_query_result: canonical_result = {:#?}", @@ -186,7 +186,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - original_values: &CanonicalVarValues<'tcx>, + original_values: &SmallCanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, ) -> InferResult<'tcx, R> where @@ -252,7 +252,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - original_values: &CanonicalVarValues<'tcx>, + original_values: &SmallCanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, output_query_region_constraints: &mut Vec>, ) -> InferResult<'tcx, R> @@ -274,10 +274,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // variable... let mut obligations = vec![]; - for (index, original_value) in original_values.var_values.iter_enumerated() { + for (index, original_value) in original_values.iter().enumerate() { // ...with the value `v_r` of that variable from the query. let result_value = query_result - .substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index]); + .substitute_projected(self.tcx, &result_subst, + |v| &v.var_values[CanonicalVar::new(index)]); match (original_value.unpack(), result_value.unpack()) { (UnpackedKind::Lifetime(ty::ReErased), UnpackedKind::Lifetime(ty::ReErased)) => { // no action needed @@ -341,7 +342,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - original_values: &CanonicalVarValues<'tcx>, + original_values: &SmallCanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> where @@ -382,7 +383,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { fn query_result_substitution_guess( &self, cause: &ObligationCause<'tcx>, - original_values: &CanonicalVarValues<'tcx>, + original_values: &SmallCanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, ) -> CanonicalVarValues<'tcx> where @@ -418,14 +419,14 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // e.g., here `result_value` might be `?0` in the example above... if let ty::TyInfer(ty::InferTy::CanonicalTy(index)) = result_value.sty { // in which case we would set `canonical_vars[0]` to `Some(?U)`. - opt_values[index] = Some(original_value); + opt_values[index] = Some(*original_value); } } UnpackedKind::Lifetime(result_value) => { // e.g., here `result_value` might be `'?1` in the example above... if let &ty::RegionKind::ReCanonical(index) = result_value { // in which case we would set `canonical_vars[0]` to `Some('static)`. - opt_values[index] = Some(original_value); + opt_values[index] = Some(*original_value); } } } @@ -459,7 +460,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - original_values: &CanonicalVarValues<'tcx>, + original_values: &SmallCanonicalVarValues<'tcx>, result_subst: &CanonicalVarValues<'tcx>, query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, ) -> InferResult<'tcx, ()> @@ -522,13 +523,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - variables1: &CanonicalVarValues<'tcx>, + variables1: &SmallCanonicalVarValues<'tcx>, variables2: impl Fn(CanonicalVar) -> Kind<'tcx>, ) -> InferResult<'tcx, ()> { self.commit_if_ok(|_| { let mut obligations = vec![]; - for (index, value1) in variables1.var_values.iter_enumerated() { - let value2 = variables2(index); + for (index, value1) in variables1.iter().enumerate() { + let value2 = variables2(CanonicalVar::new(index)); match (value1.unpack(), value2.unpack()) { (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { diff --git a/src/librustc/infer/canonical/substitute.rs b/src/librustc/infer/canonical/substitute.rs index 5bc1ae689a5..679829f43c5 100644 --- a/src/librustc/infer/canonical/substitute.rs +++ b/src/librustc/infer/canonical/substitute.rs @@ -46,7 +46,7 @@ impl<'tcx, V> Canonical<'tcx, V> { where T: TypeFoldable<'tcx>, { - assert_eq!(self.variables.len(), var_values.var_values.len()); + assert_eq!(self.variables.len(), var_values.len()); let value = projection_fn(&self.value); substitute_value(tcx, var_values, value) } diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 2aaa32aa032..73a9ff4e483 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -10,6 +10,7 @@ use infer::at::At; use infer::InferOk; +use rustc_data_structures::small_vec::SmallVec; use std::iter::FromIterator; use syntax::codemap::Span; use ty::subst::Kind; @@ -50,7 +51,8 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { } let gcx = tcx.global_tcx(); - let (c_ty, orig_values) = self.infcx.canonicalize_query(&self.param_env.and(ty)); + let mut orig_values = SmallVec::new(); + let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values); let span = self.cause.span; debug!("c_ty = {:?}", c_ty); match &gcx.dropck_outlives(c_ty) { diff --git a/src/librustc/traits/query/evaluate_obligation.rs b/src/librustc/traits/query/evaluate_obligation.rs index c81d1123d42..93fcadceb16 100644 --- a/src/librustc/traits/query/evaluate_obligation.rs +++ b/src/librustc/traits/query/evaluate_obligation.rs @@ -9,6 +9,7 @@ // except according to those terms. use infer::InferCtxt; +use rustc_data_structures::small_vec::SmallVec; use traits::{EvaluationResult, PredicateObligation, SelectionContext, TraitQueryMode, OverflowError}; @@ -38,8 +39,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, ) -> EvaluationResult { - let (c_pred, _) = - self.canonicalize_query(&obligation.param_env.and(obligation.predicate)); + let mut _orig_values = SmallVec::new(); + let c_pred = self.canonicalize_query(&obligation.param_env.and(obligation.predicate), + &mut _orig_values); // Run canonical query. If overflow occurs, rerun from scratch but this time // in standard trait query mode so that overflow is handled appropriately // within `SelectionContext`. diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index a67383fb79a..2203aefa314 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -15,6 +15,7 @@ use infer::{InferCtxt, InferOk}; use infer::at::At; use mir::interpret::{GlobalId, ConstValue}; +use rustc_data_structures::small_vec::SmallVec; use traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use traits::project::Normalized; use ty::{self, Ty, TyCtxt}; @@ -147,8 +148,9 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx let gcx = self.infcx.tcx.global_tcx(); - let (c_data, orig_values) = - self.infcx.canonicalize_query(&self.param_env.and(*data)); + let mut orig_values = SmallVec::new(); + let c_data = + self.infcx.canonicalize_query(&self.param_env.and(*data), &mut orig_values); debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); match gcx.normalize_projection_ty(c_data) { diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 3dfa66cd41a..be5e2838963 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -11,6 +11,7 @@ use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint, QueryResult}; use infer::{InferCtxt, InferOk}; +use rustc_data_structures::small_vec::SmallVec; use std::fmt; use std::rc::Rc; use traits::query::Fallible; @@ -103,7 +104,9 @@ pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>: // `canonicalize_hr_query_hack` here because of things // like the subtype query, which go awry around // `'static` otherwise. - let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key); + let mut canonical_var_values = SmallVec::new(); + let canonical_self = + infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values); let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result); diff --git a/src/librustc_data_structures/accumulate_vec.rs b/src/librustc_data_structures/accumulate_vec.rs index f50b8cadf15..2e8cca3f4f9 100644 --- a/src/librustc_data_structures/accumulate_vec.rs +++ b/src/librustc_data_structures/accumulate_vec.rs @@ -46,6 +46,13 @@ impl AccumulateVec { AccumulateVec::Array(ArrayVec::new()) } + pub fn is_array(&self) -> bool { + match self { + AccumulateVec::Array(..) => true, + AccumulateVec::Heap(..) => false, + } + } + pub fn one(el: A::Element) -> Self { iter::once(el).collect() } diff --git a/src/librustc_data_structures/small_vec.rs b/src/librustc_data_structures/small_vec.rs index 74738e61b44..83eb54fade1 100644 --- a/src/librustc_data_structures/small_vec.rs +++ b/src/librustc_data_structures/small_vec.rs @@ -50,6 +50,10 @@ impl SmallVec { SmallVec(AccumulateVec::new()) } + pub fn is_array(&self) -> bool { + self.0.is_array() + } + pub fn with_capacity(cap: usize) -> Self { let mut vec = SmallVec::new(); vec.reserve(cap); diff --git a/src/librustc_traits/chalk_context.rs b/src/librustc_traits/chalk_context.rs index 6062fe03e6a..b0f0b105f3e 100644 --- a/src/librustc_traits/chalk_context.rs +++ b/src/librustc_traits/chalk_context.rs @@ -25,6 +25,7 @@ use rustc::traits::{ use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use rustc::ty::subst::Kind; use rustc::ty::{self, TyCtxt}; +use rustc_data_structures::small_vec::SmallVec; use std::fmt::{self, Debug}; use std::marker::PhantomData; @@ -388,14 +389,15 @@ impl context::UnificationOps, ChalkArenas<'tcx>> &mut self, value: &ty::ParamEnvAnd<'tcx, Goal<'tcx>>, ) -> Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>> { - self.infcx.canonicalize_query(value).0 + let mut _orig_values = SmallVec::new(); + self.infcx.canonicalize_query(value, &mut _orig_values) } fn canonicalize_ex_clause( &mut self, value: &ChalkExClause<'tcx>, ) -> Canonical<'gcx, ChalkExClause<'gcx>> { - self.infcx.canonicalize_response(value).0 + self.infcx.canonicalize_response(value) } fn canonicalize_constrained_subst( @@ -403,9 +405,7 @@ impl context::UnificationOps, ChalkArenas<'tcx>> subst: CanonicalVarValues<'tcx>, constraints: Vec>, ) -> Canonical<'gcx, ConstrainedSubst<'gcx>> { - self.infcx - .canonicalize_response(&ConstrainedSubst { subst, constraints }) - .0 + self.infcx.canonicalize_response(&ConstrainedSubst { subst, constraints }) } fn u_canonicalize_goal( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9f83f8a00b1..ebf16c5938d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -942,7 +942,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> { Some(ref ty) => { let o_ty = self.fcx.to_ty(&ty); - let (c_ty, _orig_values) = self.fcx.inh.infcx.canonicalize_response(&o_ty); + let c_ty = self.fcx.inh.infcx.canonicalize_response(&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);