diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 8b7438cbe632..5f35c9fea0a3 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -24,6 +24,7 @@ impl_stable_hash_for!(enum mir::LocalKind { Var, Temp, Arg, ReturnPointer }); impl_stable_hash_for!(struct mir::LocalDecl<'tcx> { mutability, ty, + user_ty, name, source_info, visibility_scope, @@ -255,9 +256,10 @@ for mir::StatementKind<'gcx> { op.hash_stable(hcx, hasher); places.hash_stable(hcx, hasher); } - mir::StatementKind::UserAssertTy(ref c_ty, ref local) => { + mir::StatementKind::AscribeUserType(ref place, ref variance, ref c_ty) => { + place.hash_stable(hcx, hasher); + variance.hash_stable(hcx, hasher); c_ty.hash_stable(hcx, hasher); - local.hash_stable(hcx, hasher); } mir::StatementKind::Nop => {} mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index a092bac51878..e628a3458f9e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -10,41 +10,41 @@ //! See the Book for more information. +pub use self::freshen::TypeFreshener; pub use self::LateBoundRegionConversionTime::*; pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; pub use ty::IntVarValue; -pub use self::freshen::TypeFreshener; +use arena::SyncDroplessArena; +use errors::DiagnosticBuilder; use hir::def_id::DefId; use middle::free_region::RegionRelations; -use middle::region; use middle::lang_items; -use ty::subst::{Kind, Substs}; -use ty::{TyVid, IntVid, FloatVid}; -use ty::{self, Ty, TyCtxt, GenericParamDefKind}; -use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; -use ty::fold::TypeFoldable; -use ty::relate::RelateResult; -use traits::{self, ObligationCause, PredicateObligations, TraitEngine}; +use middle::region; use rustc_data_structures::unify as ut; -use std::cell::{Cell, RefCell, Ref, RefMut}; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::BTreeMap; use std::fmt; use syntax::ast; -use errors::DiagnosticBuilder; -use syntax_pos::{self, Span}; use syntax_pos::symbol::InternedString; +use syntax_pos::{self, Span}; +use traits::{self, ObligationCause, PredicateObligations, TraitEngine}; +use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; +use ty::fold::TypeFoldable; +use ty::relate::RelateResult; +use ty::subst::{Kind, Substs}; +use ty::{self, GenericParamDefKind, Ty, TyCtxt}; +use ty::{FloatVid, IntVid, TyVid}; use util::nodemap::FxHashMap; -use arena::SyncDroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; -use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; -use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarInfos}; use self::lexical_region_resolve::LexicalRegionResolutions; use self::outlives::env::OutlivesEnvironment; +use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound}; +use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -54,16 +54,16 @@ pub mod canonical; mod combine; mod equate; pub mod error_reporting; +mod freshen; mod fudge; mod glb; mod higher_ranked; pub mod lattice; -mod lub; -pub mod region_constraints; mod lexical_region_resolve; +mod lub; pub mod outlives; +pub mod region_constraints; pub mod resolve; -mod freshen; mod sub; pub mod type_variable; pub mod unify_key; @@ -80,7 +80,7 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult = Result; // "fixup result" -pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, /// During type-checking/inference of a body, `in_progress_tables` @@ -314,10 +314,10 @@ pub enum SubregionOrigin<'tcx> { /// Places that type/region parameters can appear. #[derive(Clone, Copy, Debug)] pub enum ParameterOrigin { - Path, // foo::bar - MethodCall, // foo.bar() <-- parameters on impl providing bar() + Path, // foo::bar + MethodCall, // foo.bar() <-- parameters on impl providing bar() OverloadedOperator, // a + b when overloaded - OverloadedDeref, // *a when overloaded + OverloadedDeref, // *a when overloaded } /// Times when we replace late-bound regions with variables: @@ -400,7 +400,7 @@ impl NLLRegionVariableOrigin { pub enum FixupError { UnresolvedIntTy(IntVid), UnresolvedFloatTy(FloatVid), - UnresolvedTy(TyVid) + UnresolvedTy(TyVid), } /// See the `region_obligations` field for more information. @@ -416,15 +416,17 @@ impl fmt::Display for FixupError { use self::FixupError::*; match *self { - UnresolvedIntTy(_) => { - write!(f, "cannot determine the type of this integer; \ - add a suffix to specify the type explicitly") - } - UnresolvedFloatTy(_) => { - write!(f, "cannot determine the type of this number; \ - add a suffix to specify the type explicitly") - } - UnresolvedTy(_) => write!(f, "unconstrained type") + UnresolvedIntTy(_) => write!( + f, + "cannot determine the type of this integer; \ + add a suffix to specify the type explicitly" + ), + UnresolvedFloatTy(_) => write!( + f, + "cannot determine the type of this number; \ + add a suffix to specify the type explicitly" + ), + UnresolvedTy(_) => write!(f, "unconstrained type"), } } } @@ -432,7 +434,7 @@ impl fmt::Display for FixupError { /// Helper type of a temporary returned by tcx.infer_ctxt(). /// Necessary because we can't write the following bound: /// F: for<'b, 'tcx> where 'gcx: 'tcx FnOnce(InferCtxt<'b, 'gcx, 'tcx>). -pub struct InferCtxtBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +pub struct InferCtxtBuilder<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { global_tcx: TyCtxt<'a, 'gcx, 'gcx>, arena: SyncDroplessArena, fresh_tables: Option>>, @@ -444,7 +446,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> { global_tcx: self, arena: SyncDroplessArena::new(), fresh_tables: None, - } } } @@ -458,7 +459,8 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { } pub fn enter(&'tcx mut self, f: F) -> R - where F: for<'b> FnOnce(InferCtxt<'b, 'gcx, 'tcx>) -> R + where + F: for<'b> FnOnce(InferCtxt<'b, 'gcx, 'tcx>) -> R, { let InferCtxtBuilder { global_tcx, @@ -466,40 +468,51 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { ref fresh_tables, } = *self; let in_progress_tables = fresh_tables.as_ref(); - global_tcx.enter_local(arena, |tcx| f(InferCtxt { - tcx, - in_progress_tables, - projection_cache: RefCell::new(traits::ProjectionCache::new()), - type_variables: RefCell::new(type_variable::TypeVariableTable::new()), - int_unification_table: RefCell::new(ut::UnificationTable::new()), - float_unification_table: RefCell::new(ut::UnificationTable::new()), - region_constraints: RefCell::new(Some(RegionConstraintCollector::new())), - lexical_region_resolutions: RefCell::new(None), - selection_cache: traits::SelectionCache::new(), - evaluation_cache: traits::EvaluationCache::new(), - reported_trait_errors: RefCell::new(FxHashMap()), - tainted_by_errors_flag: Cell::new(false), - err_count_on_creation: tcx.sess.err_count(), - in_snapshot: Cell::new(false), - region_obligations: RefCell::new(vec![]), - universe: Cell::new(ty::UniverseIndex::ROOT), - })) + global_tcx.enter_local(arena, |tcx| { + f(InferCtxt { + tcx, + in_progress_tables, + projection_cache: RefCell::new(traits::ProjectionCache::new()), + type_variables: RefCell::new(type_variable::TypeVariableTable::new()), + int_unification_table: RefCell::new(ut::UnificationTable::new()), + float_unification_table: RefCell::new(ut::UnificationTable::new()), + region_constraints: RefCell::new(Some(RegionConstraintCollector::new())), + lexical_region_resolutions: RefCell::new(None), + selection_cache: traits::SelectionCache::new(), + evaluation_cache: traits::EvaluationCache::new(), + reported_trait_errors: RefCell::new(FxHashMap()), + tainted_by_errors_flag: Cell::new(false), + err_count_on_creation: tcx.sess.err_count(), + in_snapshot: Cell::new(false), + region_obligations: RefCell::new(vec![]), + universe: Cell::new(ty::UniverseIndex::ROOT), + }) + }) } } impl ExpectedFound { pub fn new(a_is_expected: bool, a: T, b: T) -> Self { if a_is_expected { - ExpectedFound {expected: a, found: b} + ExpectedFound { + expected: a, + found: b, + } } else { - ExpectedFound {expected: b, found: a} + ExpectedFound { + expected: b, + found: a, + } } } } impl<'tcx, T> InferOk<'tcx, T> { pub fn unit(self) -> InferOk<'tcx, ()> { - InferOk { value: (), obligations: self.obligations } + InferOk { + value: (), + obligations: self.obligations, + } } /// Extract `value`, registering any obligations into `fulfill_cx` @@ -523,7 +536,7 @@ impl<'tcx> InferOk<'tcx, ()> { } #[must_use = "once you start a snapshot, you should always consume it"] -pub struct CombinedSnapshot<'a, 'tcx:'a> { +pub struct CombinedSnapshot<'a, 'tcx: 'a> { projection_cache_snapshot: traits::ProjectionCacheSnapshot, type_snapshot: type_variable::Snapshot<'tcx>, int_snapshot: ut::Snapshot>, @@ -540,14 +553,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.in_snapshot.get() } - pub fn freshen>(&self, t: T) -> T { + pub fn freshen>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } pub fn type_var_diverges(&'a self, ty: Ty) -> bool { match ty.sty { ty::Infer(ty::TyVar(vid)) => self.type_variables.borrow().var_diverges(vid), - _ => false + _ => false, } } @@ -557,22 +570,30 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn type_is_unconstrained_numeric(&'a self, ty: Ty) -> UnconstrainedNumeric { use ty::error::UnconstrainedNumeric::Neither; - use ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; + use ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; match ty.sty { ty::Infer(ty::IntVar(vid)) => { - if self.int_unification_table.borrow_mut().probe_value(vid).is_some() { + if self.int_unification_table + .borrow_mut() + .probe_value(vid) + .is_some() + { Neither } else { UnconstrainedInt } - }, + } ty::Infer(ty::FloatVar(vid)) => { - if self.float_unification_table.borrow_mut().probe_value(vid).is_some() { + if self.float_unification_table + .borrow_mut() + .probe_value(vid) + .is_some() + { Neither } else { UnconstrainedFloat } - }, + } _ => Neither, } } @@ -590,17 +611,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { (0..int_unification_table.len()) .map(|i| ty::IntVid { index: i as u32 }) .filter(|&vid| int_unification_table.probe_value(vid).is_none()) - .map(|v| self.tcx.mk_int_var(v)) - ).chain( + .map(|v| self.tcx.mk_int_var(v)), + ) + .chain( (0..float_unification_table.len()) .map(|i| ty::FloatVid { index: i as u32 }) .filter(|&vid| float_unification_table.probe_value(vid).is_none()) - .map(|v| self.tcx.mk_float_var(v)) - ).collect() + .map(|v| self.tcx.mk_float_var(v)), + ) + .collect() } - fn combine_fields(&'a self, trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>) - -> CombineFields<'a, 'gcx, 'tcx> { + fn combine_fields( + &'a self, + trace: TypeTrace<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> CombineFields<'a, 'gcx, 'tcx> { CombineFields { infcx: self, trace, @@ -627,7 +653,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // escaping obligations in the main cx. In those cases, you can // use this function. pub fn save_and_restore_in_snapshot_flag(&self, func: F) -> R - where F: FnOnce(&Self) -> R + where + F: FnOnce(&Self) -> R, { let flag = self.in_snapshot.get(); self.in_snapshot.set(false); @@ -653,23 +680,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. - _in_progress_tables: self.in_progress_tables.map(|tables| { - tables.borrow() - }) + _in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()), } } fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) { debug!("rollback_to(cause={})", cause); - let CombinedSnapshot { projection_cache_snapshot, - type_snapshot, - int_snapshot, - float_snapshot, - region_constraints_snapshot, - region_obligations_snapshot, - universe, - was_in_snapshot, - _in_progress_tables } = snapshot; + let CombinedSnapshot { + projection_cache_snapshot, + type_snapshot, + int_snapshot, + float_snapshot, + region_constraints_snapshot, + region_obligations_snapshot, + universe, + was_in_snapshot, + _in_progress_tables, + } = snapshot; self.in_snapshot.set(was_in_snapshot); self.universe.set(universe); @@ -677,9 +704,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.projection_cache .borrow_mut() .rollback_to(projection_cache_snapshot); - self.type_variables - .borrow_mut() - .rollback_to(type_snapshot); + self.type_variables.borrow_mut().rollback_to(type_snapshot); self.int_unification_table .borrow_mut() .rollback_to(int_snapshot); @@ -695,27 +720,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { debug!("commit_from()"); - let CombinedSnapshot { projection_cache_snapshot, - type_snapshot, - int_snapshot, - float_snapshot, - region_constraints_snapshot, - region_obligations_snapshot: _, - universe: _, - was_in_snapshot, - _in_progress_tables } = snapshot; + let CombinedSnapshot { + projection_cache_snapshot, + type_snapshot, + int_snapshot, + float_snapshot, + region_constraints_snapshot, + region_obligations_snapshot: _, + universe: _, + was_in_snapshot, + _in_progress_tables, + } = snapshot; self.in_snapshot.set(was_in_snapshot); self.projection_cache .borrow_mut() .commit(&projection_cache_snapshot); - self.type_variables - .borrow_mut() - .commit(type_snapshot); - self.int_unification_table - .borrow_mut() - .commit(int_snapshot); + self.type_variables.borrow_mut().commit(type_snapshot); + self.int_unification_table.borrow_mut().commit(int_snapshot); self.float_unification_table .borrow_mut() .commit(float_snapshot); @@ -724,7 +747,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } /// Execute `f` and commit the bindings - pub fn commit_unconditionally(&self, f: F) -> R where + pub fn commit_unconditionally(&self, f: F) -> R + where F: FnOnce() -> R, { debug!("commit()"); @@ -735,23 +759,29 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)` - pub fn commit_if_ok(&self, f: F) -> Result where - F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result + pub fn commit_if_ok(&self, f: F) -> Result + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result, { debug!("commit_if_ok()"); let snapshot = self.start_snapshot(); let r = f(&snapshot); debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok()); match r { - Ok(_) => { self.commit_from(snapshot); } - Err(_) => { self.rollback_to("commit_if_ok -- error", snapshot); } + Ok(_) => { + self.commit_from(snapshot); + } + Err(_) => { + self.rollback_to("commit_if_ok -- error", snapshot); + } } r } // Execute `f` in a snapshot, and commit the bindings it creates - pub fn in_snapshot(&self, f: F) -> T where - F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> T + pub fn in_snapshot(&self, f: F) -> T + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> T, { debug!("in_snapshot()"); let snapshot = self.start_snapshot(); @@ -761,7 +791,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } /// Execute `f` then unroll any bindings it creates - pub fn probe(&self, f: F) -> R where + pub fn probe(&self, f: F) -> R + where F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, { debug!("probe()"); @@ -771,59 +802,57 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r } - pub fn add_given(&self, - sub: ty::Region<'tcx>, - sup: ty::RegionVid) - { + pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) { self.borrow_region_constraints().add_given(sub, sup); } - pub fn can_sub(&self, - param_env: ty::ParamEnv<'tcx>, - a: T, - b: T) - -> UnitResult<'tcx> - where T: at::ToTrace<'tcx> + pub fn can_sub(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx> + where + T: at::ToTrace<'tcx>, { let origin = &ObligationCause::dummy(); self.probe(|_| { - self.at(origin, param_env).sub(a, b).map(|InferOk { obligations: _, .. }| { - // Ignore obligations, since we are unrolling - // everything anyway. - }) + self.at(origin, param_env) + .sub(a, b) + .map(|InferOk { obligations: _, .. }| { + // Ignore obligations, since we are unrolling + // everything anyway. + }) }) } - pub fn can_eq(&self, - param_env: ty::ParamEnv<'tcx>, - a: T, - b: T) - -> UnitResult<'tcx> - where T: at::ToTrace<'tcx> + pub fn can_eq(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx> + where + T: at::ToTrace<'tcx>, { let origin = &ObligationCause::dummy(); self.probe(|_| { - self.at(origin, param_env).eq(a, b).map(|InferOk { obligations: _, .. }| { - // Ignore obligations, since we are unrolling - // everything anyway. - }) + self.at(origin, param_env) + .eq(a, b) + .map(|InferOk { obligations: _, .. }| { + // Ignore obligations, since we are unrolling + // everything anyway. + }) }) } - pub fn sub_regions(&self, - origin: SubregionOrigin<'tcx>, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>) { + pub fn sub_regions( + &self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) { debug!("sub_regions({:?} <: {:?})", a, b); - self.borrow_region_constraints().make_subregion(origin, a, b); + self.borrow_region_constraints() + .make_subregion(origin, a, b); } - pub fn subtype_predicate(&self, - cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - predicate: &ty::PolySubtypePredicate<'tcx>) - -> Option> - { + pub fn subtype_predicate( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: &ty::PolySubtypePredicate<'tcx>, + ) -> Option> { // Subtle: it's ok to skip the binder here and resolve because // `shallow_resolve` just ignores anything that is not a type // variable, and because type variable's can't (at present, at @@ -845,8 +874,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } Some(self.commit_if_ok(|snapshot| { - let (ty::SubtypePredicate { a_is_expected, a, b}, skol_map) = - self.skolemize_late_bound_regions(predicate); + let ( + ty::SubtypePredicate { + a_is_expected, + a, + b, + }, + skol_map, + ) = self.skolemize_late_bound_regions(predicate); let cause_span = cause.span; let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; @@ -856,17 +891,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { })) } - pub fn region_outlives_predicate(&self, - cause: &traits::ObligationCause<'tcx>, - predicate: &ty::PolyRegionOutlivesPredicate<'tcx>) - -> UnitResult<'tcx> - { + pub fn region_outlives_predicate( + &self, + cause: &traits::ObligationCause<'tcx>, + predicate: &ty::PolyRegionOutlivesPredicate<'tcx>, + ) -> UnitResult<'tcx> { self.commit_if_ok(|snapshot| { let (ty::OutlivesPredicate(r_a, r_b), skol_map) = self.skolemize_late_bound_regions(predicate); - let origin = - SubregionOrigin::from_obligation_cause(cause, - || RelateRegionParamBound(cause.span)); + let origin = SubregionOrigin::from_obligation_cause(cause, || { + RelateRegionParamBound(cause.span) + }); self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` self.leak_check(false, cause.span, &skol_map, snapshot)?; Ok(self.pop_skolemized(skol_map, snapshot)) @@ -888,27 +923,30 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } pub fn next_int_var_id(&self) -> IntVid { - self.int_unification_table - .borrow_mut() - .new_key(None) + self.int_unification_table.borrow_mut().new_key(None) } pub fn next_float_var_id(&self) -> FloatVid { - self.float_unification_table - .borrow_mut() - .new_key(None) + self.float_unification_table.borrow_mut().new_key(None) } /// Create a fresh region variable with the next available index. - /// - /// # Parameters - /// - /// - `origin`: information about why we created this variable, for use - /// during diagnostics / error-reporting. - pub fn next_region_var(&self, origin: RegionVariableOrigin) - -> ty::Region<'tcx> { + /// The variable will be created in the maximum universe created + /// thus far, allowing it to name any region created thus far. + pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { + self.next_region_var_in_universe(origin, self.universe()) + } + + /// Create a fresh region variable with the next available index + /// in the given universe; typically, you can use + /// `next_region_var` and just use the maximal universe. + pub fn next_region_var_in_universe( + &self, + origin: RegionVariableOrigin, + universe: ty::UniverseIndex, + ) -> ty::Region<'tcx> { let region_var = self.borrow_region_constraints() - .new_region_var(self.universe(), origin); + .new_region_var(universe, origin); self.tcx.mk_region(ty::ReVar(region_var)) } @@ -918,22 +956,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } /// Just a convenient wrapper of `next_region_var` for using during NLL. - pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin) - -> ty::Region<'tcx> { + pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin) -> ty::Region<'tcx> { self.next_region_var(RegionVariableOrigin::NLL(origin)) } - pub fn var_for_def(&self, - span: Span, - param: &ty::GenericParamDef) - -> Kind<'tcx> { + /// Just a convenient wrapper of `next_region_var` for using during NLL. + pub fn next_nll_region_var_in_universe( + &self, + origin: NLLRegionVariableOrigin, + universe: ty::UniverseIndex, + ) -> ty::Region<'tcx> { + self.next_region_var_in_universe(RegionVariableOrigin::NLL(origin), universe) + } + + pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> Kind<'tcx> { match param.kind { GenericParamDefKind::Lifetime => { // Create a region inference variable for the given // region parameter definition. - self.next_region_var(EarlyBoundRegion(span, param.name)).into() + self.next_region_var(EarlyBoundRegion(span, param.name)) + .into() } - GenericParamDefKind::Type {..} => { + GenericParamDefKind::Type { .. } => { // Create a type inference variable for the given // type parameter definition. The substitutions are // for actual parameters that may be referred to by @@ -942,12 +986,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // used in a path such as `Foo::::new()` will // use an inference variable for `C` with `[T, U]` // as the substitutions for the default, `(T, U)`. - let ty_var_id = - self.type_variables - .borrow_mut() - .new_var(self.universe(), - false, - TypeVariableOrigin::TypeParameterDefinition(span, param.name)); + let ty_var_id = self.type_variables.borrow_mut().new_var( + self.universe(), + false, + TypeVariableOrigin::TypeParameterDefinition(span, param.name), + ); self.tcx.mk_var(ty_var_id).into() } @@ -956,13 +999,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// Given a set of generics defined on a type or impl, returns a substitution mapping each /// type/region parameter to a fresh inference variable. - pub fn fresh_substs_for_item(&self, - span: Span, - def_id: DefId) - -> &'tcx Substs<'tcx> { - Substs::for_item(self.tcx, def_id, |param, _| { - self.var_for_def(span, param) - }) + pub fn fresh_substs_for_item(&self, span: Span, def_id: DefId) -> &'tcx Substs<'tcx> { + Substs::for_item(self.tcx, def_id, |param, _| self.var_for_def(span, param)) } /// True if errors have been reported since this infcx was @@ -971,11 +1009,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// errors, but where it's hard to be 100% sure (e.g., unresolved /// inference variables, regionck errors). pub fn is_tainted_by_errors(&self) -> bool { - debug!("is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ - tainted_by_errors_flag={})", - self.tcx.sess.err_count(), - self.err_count_on_creation, - self.tainted_by_errors_flag.get()); + debug!( + "is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ + tainted_by_errors_flag={})", + self.tcx.sess.err_count(), + self.err_count_on_creation, + self.tainted_by_errors_flag.get() + ); if self.tcx.sess.err_count() > self.err_count_on_creation { return true; // errors reported since this infcx was made @@ -1018,12 +1058,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_map: ®ion::ScopeTree, outlives_env: &OutlivesEnvironment<'tcx>, ) { - self.resolve_regions_and_report_errors_inner( - region_context, - region_map, - outlives_env, - true, - ) + self.resolve_regions_and_report_errors_inner(region_context, region_map, outlives_env, true) } fn resolve_regions_and_report_errors_inner( @@ -1033,22 +1068,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { outlives_env: &OutlivesEnvironment<'tcx>, will_later_be_reported_by_nll: bool, ) { - assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), - "region_obligations not empty: {:#?}", - self.region_obligations.borrow()); - - let region_rels = &RegionRelations::new(self.tcx, - region_context, - region_map, - outlives_env.free_region_map()); - let (var_infos, data) = self.region_constraints.borrow_mut() - .take() - .expect("regions already resolved") - .into_infos_and_data(); + assert!( + self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), + "region_obligations not empty: {:#?}", + self.region_obligations.borrow() + ); + + let region_rels = &RegionRelations::new( + self.tcx, + region_context, + region_map, + outlives_env.free_region_map(), + ); + let (var_infos, data) = self.region_constraints + .borrow_mut() + .take() + .expect("regions already resolved") + .into_infos_and_data(); let (lexical_region_resolutions, errors) = lexical_region_resolve::resolve(region_rels, var_infos, data); - let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + let old_value = self.lexical_region_resolutions + .replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); if !self.is_tainted_by_errors() { @@ -1072,9 +1113,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// translate them into the form that the NLL solver /// understands. See the NLL module for mode details. pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> { - assert!(self.region_obligations.borrow().is_empty(), - "region_obligations not empty: {:#?}", - self.region_obligations.borrow()); + assert!( + self.region_obligations.borrow().is_empty(), + "region_obligations not empty: {:#?}", + self.region_obligations.borrow() + ); self.borrow_region_constraints().take_and_reset_data() } @@ -1095,10 +1138,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// called. This is used only during NLL processing to "hand off" ownership /// of the set of region vairables into the NLL region context. pub fn take_region_var_origins(&self) -> VarInfos { - let (var_infos, data) = self.region_constraints.borrow_mut() - .take() - .expect("regions already resolved") - .into_infos_and_data(); + let (var_infos, data) = self.region_constraints + .borrow_mut() + .take() + .expect("regions already resolved") + .into_infos_and_data(); assert!(data.is_empty()); var_infos } @@ -1132,32 +1176,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // structurally), and we prevent cycles in any case, // so this recursion should always be of very limited // depth. - self.type_variables.borrow_mut() - .probe(v) - .known() - .map(|t| self.shallow_resolve(t)) - .unwrap_or(typ) - } - - ty::Infer(ty::IntVar(v)) => { - self.int_unification_table + self.type_variables .borrow_mut() - .probe_value(v) - .map(|v| v.to_type(self.tcx)) + .probe(v) + .known() + .map(|t| self.shallow_resolve(t)) .unwrap_or(typ) } - ty::Infer(ty::FloatVar(v)) => { - self.float_unification_table - .borrow_mut() - .probe_value(v) - .map(|v| v.to_type(self.tcx)) - .unwrap_or(typ) - } + ty::Infer(ty::IntVar(v)) => self.int_unification_table + .borrow_mut() + .probe_value(v) + .map(|v| v.to_type(self.tcx)) + .unwrap_or(typ), - _ => { - typ - } + ty::Infer(ty::FloatVar(v)) => self.float_unification_table + .borrow_mut() + .probe_value(v) + .map(|v| v.to_type(self.tcx)) + .unwrap_or(typ), + + _ => typ, } } @@ -1166,7 +1205,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } pub fn resolve_type_vars_if_possible(&self, value: &T) -> T - where T: TypeFoldable<'tcx> + where + T: TypeFoldable<'tcx>, { /*! * Where possible, replaces type/int/float variables in @@ -1190,20 +1230,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// resolved type, so it's more efficient than /// `resolve_type_vars_if_possible()`. pub fn any_unresolved_type_vars(&self, value: &T) -> bool - where T: TypeFoldable<'tcx> + where + T: TypeFoldable<'tcx>, { let mut r = resolve::UnresolvedTypeFinder::new(self); value.visit_with(&mut r) } pub fn resolve_type_and_region_vars_if_possible(&self, value: &T) -> T - where T: TypeFoldable<'tcx> + where + T: TypeFoldable<'tcx>, { let mut r = resolve::OpportunisticTypeAndRegionResolver::new(self); value.fold_with(&mut r) } - pub fn fully_resolve>(&self, value: &T) -> FixupResult { + pub fn fully_resolve>(&self, value: &T) -> FixupResult { /*! * Attempts to resolve all type/region variables in * `value`. Region inference must have been run already (e.g., @@ -1228,12 +1270,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // types using one of these methods, and should not call span_err directly for such // errors. - pub fn type_error_struct_with_diag(&self, - sp: Span, - mk_diag: M, - actual_ty: Ty<'tcx>) - -> DiagnosticBuilder<'tcx> - where M: FnOnce(String) -> DiagnosticBuilder<'tcx>, + pub fn type_error_struct_with_diag( + &self, + sp: Span, + mk_diag: M, + actual_ty: Ty<'tcx>, + ) -> DiagnosticBuilder<'tcx> + where + M: FnOnce(String) -> DiagnosticBuilder<'tcx>, { let actual_ty = self.resolve_type_vars_if_possible(&actual_ty); debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); @@ -1246,12 +1290,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { mk_diag(self.ty_to_string(actual_ty)) } - pub fn report_mismatched_types(&self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - err: TypeError<'tcx>) - -> DiagnosticBuilder<'tcx> { + pub fn report_mismatched_types( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx> { let trace = TypeTrace::types(cause, true, expected, actual); self.report_and_explain_type_error(trace, &err) } @@ -1260,13 +1305,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { &self, span: Span, lbrct: LateBoundRegionConversionTime, - value: &ty::Binder) - -> (T, BTreeMap>) - where T : TypeFoldable<'tcx> + value: &ty::Binder, + ) -> (T, BTreeMap>) + where + T: TypeFoldable<'tcx>, { - self.tcx.replace_late_bound_regions( - value, - |br| self.next_region_var(LateBoundRegion(span, br, lbrct))) + self.tcx.replace_late_bound_regions(value, |br| { + self.next_region_var(LateBoundRegion(span, br, lbrct)) + }) } /// Given a higher-ranked projection predicate like: @@ -1284,43 +1330,51 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// /// See `higher_ranked_match` in `higher_ranked/mod.rs` for more /// details. - pub fn match_poly_projection_predicate(&self, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - match_a: ty::PolyProjectionPredicate<'tcx>, - match_b: ty::TraitRef<'tcx>) - -> InferResult<'tcx, HrMatchResult>> - { + pub fn match_poly_projection_predicate( + &self, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + match_a: ty::PolyProjectionPredicate<'tcx>, + match_b: ty::TraitRef<'tcx>, + ) -> InferResult<'tcx, HrMatchResult>> { let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref(self.tcx), p.ty)); let trace = TypeTrace { cause, - values: TraitRefs(ExpectedFound::new(true, match_pair.skip_binder().0, match_b)) + values: TraitRefs(ExpectedFound::new( + true, + match_pair.skip_binder().0, + match_b, + )), }; let mut combine = self.combine_fields(trace, param_env); let result = combine.higher_ranked_match(&match_pair, &match_b, true)?; - Ok(InferOk { value: result, obligations: combine.obligations }) + Ok(InferOk { + value: result, + obligations: combine.obligations, + }) } /// See `verify_generic_bound` method in `region_constraints` - pub fn verify_generic_bound(&self, - origin: SubregionOrigin<'tcx>, - kind: GenericKind<'tcx>, - a: ty::Region<'tcx>, - bound: VerifyBound<'tcx>) { - debug!("verify_generic_bound({:?}, {:?} <: {:?})", - kind, - a, - bound); - - self.borrow_region_constraints().verify_generic_bound(origin, kind, a, bound); - } - - pub fn type_moves_by_default(&self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span) - -> bool { + pub fn verify_generic_bound( + &self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + debug!("verify_generic_bound({:?}, {:?} <: {:?})", kind, a, bound); + + self.borrow_region_constraints() + .verify_generic_bound(origin, kind, a, bound); + } + + pub fn type_moves_by_default( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, + ) -> bool { let ty = self.resolve_type_vars_if_possible(&ty); // Even if the type may have no inference variables, during // type-checking closure types are in local tables only. @@ -1342,11 +1396,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// Obtains the latest type of the given closure; this may be a /// closure in the current function, in which case its /// `ClosureKind` may not yet be known. - pub fn closure_kind(&self, - closure_def_id: DefId, - closure_substs: ty::ClosureSubsts<'tcx>) - -> Option - { + pub fn closure_kind( + &self, + closure_def_id: DefId, + closure_substs: ty::ClosureSubsts<'tcx>, + ) -> Option { let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx); let closure_kind_ty = self.shallow_resolve(&closure_kind_ty); closure_kind_ty.to_opt_closure_kind() @@ -1359,7 +1413,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn closure_sig( &self, def_id: DefId, - substs: ty::ClosureSubsts<'tcx> + substs: ty::ClosureSubsts<'tcx>, ) -> ty::PolyFnSig<'tcx> { let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx); let closure_sig_ty = self.shallow_resolve(&closure_sig_ty); @@ -1368,29 +1422,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// Normalizes associated types in `value`, potentially returning /// new obligations that must further be processed. - pub fn partially_normalize_associated_types_in(&self, - span: Span, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'tcx>, - value: &T) - -> InferOk<'tcx, T> - where T : TypeFoldable<'tcx> + pub fn partially_normalize_associated_types_in( + &self, + span: Span, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T, + ) -> InferOk<'tcx, T> + where + T: TypeFoldable<'tcx>, { debug!("partially_normalize_associated_types_in(value={:?})", value); let mut selcx = traits::SelectionContext::new(self); let cause = ObligationCause::misc(span, body_id); let traits::Normalized { value, obligations } = traits::normalize(&mut selcx, param_env, cause, value); - debug!("partially_normalize_associated_types_in: result={:?} predicates={:?}", - value, - obligations); + debug!( + "partially_normalize_associated_types_in: result={:?} predicates={:?}", + value, obligations + ); InferOk { value, obligations } } pub fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> { - RefMut::map( - self.region_constraints.borrow_mut(), - |c| c.as_mut().expect("region constraints already solved")) + RefMut::map(self.region_constraints.borrow_mut(), |c| { + c.as_mut().expect("region constraints already solved") + }) } /// Clears the selection, evaluation, and projection cachesThis is useful when @@ -1423,14 +1480,15 @@ impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { self.cause.span } - pub fn types(cause: &ObligationCause<'tcx>, - a_is_expected: bool, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> TypeTrace<'tcx> { + pub fn types( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Ty<'tcx>, + b: Ty<'tcx>, + ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Types(ExpectedFound::new(a_is_expected, a, b)) + values: Types(ExpectedFound::new(a_is_expected, a, b)), } } @@ -1440,7 +1498,7 @@ impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { values: Types(ExpectedFound { expected: tcx.types.err, found: tcx.types.err, - }) + }), } } } @@ -1482,24 +1540,25 @@ impl<'tcx> SubregionOrigin<'tcx> { } } - pub fn from_obligation_cause(cause: &traits::ObligationCause<'tcx>, - default: F) - -> Self - where F: FnOnce() -> Self + pub fn from_obligation_cause(cause: &traits::ObligationCause<'tcx>, default: F) -> Self + where + F: FnOnce() -> Self, { match cause.code { - traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => - SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span), - - traits::ObligationCauseCode::CompareImplMethodObligation { item_name, - impl_item_def_id, - trait_item_def_id, } => - SubregionOrigin::CompareImplMethodObligation { - span: cause.span, - item_name, - impl_item_def_id, - trait_item_def_id, - }, + traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => { + SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) + } + + traits::ObligationCauseCode::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } => SubregionOrigin::CompareImplMethodObligation { + span: cause.span, + item_name, + impl_item_def_id, + trait_item_def_id, + }, _ => default(), } @@ -1534,8 +1593,10 @@ EnumTypeFoldableImpl! { impl<'tcx> fmt::Debug for RegionObligation<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", - self.sub_region, - self.sup_type) + write!( + f, + "RegionObligation(sub_region={:?}, sup_type={:?})", + self.sub_region, self.sup_type + ) } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 708ce8e20312..3450eec8082f 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -640,6 +640,12 @@ pub struct LocalDecl<'tcx> { /// Type of this local. pub ty: Ty<'tcx>, + /// If the user manually ascribed a type to this variable, + /// e.g. via `let x: T`, then we carry that type here. The MIR + /// borrow checker needs this information since it can affect + /// region inference. + pub user_ty: Option>, + /// Name of the local, used in debuginfo and pretty-printing. /// /// Note that function arguments can also have this set to `Some(_)` @@ -802,6 +808,7 @@ impl<'tcx> LocalDecl<'tcx> { LocalDecl { mutability, ty, + user_ty: None, name: None, source_info: SourceInfo { span, @@ -821,6 +828,7 @@ impl<'tcx> LocalDecl<'tcx> { LocalDecl { mutability: Mutability::Mut, ty: return_ty, + user_ty: None, source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE, @@ -1636,22 +1644,19 @@ pub enum StatementKind<'tcx> { /// (The starting point(s) arise implicitly from borrows.) EndRegion(region::Scope), - /// Encodes a user's type assertion. These need to be preserved intact so that NLL can respect - /// them. For example: - /// - /// let (a, b): (T, U) = y; - /// - /// Here we would insert a `UserAssertTy<(T, U)>(y)` instruction to check that the type of `y` - /// is the right thing. + /// Encodes a user's type ascription. These need to be preserved + /// intact so that NLL can respect them. For example: /// - /// `CanonicalTy` is used to capture "inference variables" from the user's types. For example: + /// let a: T = y; /// - /// let x: Vec<_> = ...; - /// let y: &u32 = ...; + /// The effect of this annotation is to relate the type `T_y` of the place `y` + /// to the user-given type `T`. The effect depends on the specified variance: /// - /// would result in `Vec` and `&'?0 u32` respectively (where `?0` is a canonicalized - /// variable). - UserAssertTy(CanonicalTy<'tcx>, Local), + /// - `Covariant` -- requires that `T_y <: T` + /// - `Contravariant` -- requires that `T_y :> T` + /// - `Invariant` -- requires that `T_y == T` + /// - `Bivariant` -- no effect + AscribeUserType(Place<'tcx>, ty::Variance, CanonicalTy<'tcx>), /// No-op. Useful for deleting instructions without affecting statement indices. Nop, @@ -1728,8 +1733,8 @@ impl<'tcx> Debug for Statement<'tcx> { ref outputs, ref inputs, } => write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs), - UserAssertTy(ref c_ty, ref local) => { - write!(fmt, "UserAssertTy({:?}, {:?})", c_ty, local) + AscribeUserType(ref place, ref variance, ref c_ty) => { + write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } Nop => write!(fmt, "nop"), } @@ -2616,6 +2621,7 @@ BraceStructTypeFoldableImpl! { is_user_variable, internal, ty, + user_ty, name, source_info, visibility_scope, @@ -2652,7 +2658,7 @@ EnumTypeFoldableImpl! { (StatementKind::InlineAsm) { asm, outputs, inputs }, (StatementKind::Validate)(a, b), (StatementKind::EndRegion)(a), - (StatementKind::UserAssertTy)(a, b), + (StatementKind::AscribeUserType)(a, v, b), (StatementKind::Nop), } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c7723fdf1913..0beb5ac0a3cb 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -144,11 +144,12 @@ macro_rules! make_mir_visitor { self.super_operand(operand, location); } - fn visit_user_assert_ty(&mut self, - c_ty: & $($mutability)* CanonicalTy<'tcx>, - local: & $($mutability)* Local, - location: Location) { - self.super_user_assert_ty(c_ty, local, location); + fn visit_ascribe_user_ty(&mut self, + place: & $($mutability)* Place<'tcx>, + variance: & $($mutability)* ty::Variance, + c_ty: & $($mutability)* CanonicalTy<'tcx>, + location: Location) { + self.super_ascribe_user_ty(place, variance, c_ty, location); } fn visit_place(&mut self, @@ -386,9 +387,12 @@ macro_rules! make_mir_visitor { self.visit_operand(input, location); } } - StatementKind::UserAssertTy(ref $($mutability)* c_ty, - ref $($mutability)* local) => { - self.visit_user_assert_ty(c_ty, local, location); + StatementKind::AscribeUserType( + ref $($mutability)* place, + ref $($mutability)* variance, + ref $($mutability)* c_ty, + ) => { + self.visit_ascribe_user_ty(place, variance, c_ty, location); } StatementKind::Nop => {} } @@ -629,12 +633,13 @@ macro_rules! make_mir_visitor { } } - fn super_user_assert_ty(&mut self, - c_ty: & $($mutability)* CanonicalTy<'tcx>, - local: & $($mutability)* Local, - location: Location) { + fn super_ascribe_user_ty(&mut self, + place: & $($mutability)* Place<'tcx>, + _variance: & $($mutability)* ty::Variance, + c_ty: & $($mutability)* CanonicalTy<'tcx>, + location: Location) { + self.visit_place(place, PlaceContext::Validate, location); self.visit_canonical_ty(c_ty); - self.visit_local(local, PlaceContext::Validate, location); } fn super_place(&mut self, @@ -716,6 +721,7 @@ macro_rules! make_mir_visitor { let LocalDecl { mutability: _, ref $($mutability)* ty, + ref $($mutability)* user_ty, name: _, ref $($mutability)* source_info, ref $($mutability)* visibility_scope, @@ -727,6 +733,9 @@ macro_rules! make_mir_visitor { local, source_info: *source_info, }); + if let Some(user_ty) = user_ty { + self.visit_canonical_ty(user_ty); + } self.visit_source_info(source_info); self.visit_source_scope(visibility_scope); } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index eb6f7140a7db..83d6b715e95b 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -356,8 +356,8 @@ pub struct TypeckTables<'tcx> { /// belongs, but it may not exist if it's a tuple field (`tuple.0`). field_indices: ItemLocalMap, - /// Stores the canonicalized types provided by the user. See also `UserAssertTy` statement in - /// MIR. + /// Stores the canonicalized types provided by the user. See also + /// `AscribeUserType` statement in MIR. user_provided_tys: ItemLocalMap>, /// Stores the types for various nodes in the AST. Note that this table diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 955f2740b929..bd9dfc6b8551 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -57,6 +57,7 @@ CloneTypeFoldableAndLiftImpls! { ::ty::ClosureKind, ::ty::IntVarValue, ::ty::ParamTy, + ::ty::Variance, ::syntax_pos::Span, } diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs index dd62a12553ca..0cb8f99efc33 100644 --- a/src/librustc_codegen_llvm/mir/statement.rs +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -92,7 +92,7 @@ impl FunctionCx<'a, 'll, 'tcx> { mir::StatementKind::ReadForMatch(_) | mir::StatementKind::EndRegion(_) | mir::StatementKind::Validate(..) | - mir::StatementKind::UserAssertTy(..) | + mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Nop => bx, } } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 76f6bcb5e566..60aeb92d91a6 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -535,10 +535,10 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // flow_state already handled). } StatementKind::Nop - | StatementKind::UserAssertTy(..) + | StatementKind::AscribeUserType(..) | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { - // `Nop`, `UserAssertTy`, `Validate`, and `StorageLive` are irrelevant + // `Nop`, `AscribeUserType`, `Validate`, and `StorageLive` are irrelevant // to borrow check. } StatementKind::StorageDead(local) => { diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 457499ded565..7e8e1b32d4d9 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -17,7 +17,7 @@ use rustc::infer::InferCtxt; use rustc::mir::visit::TyContext; use rustc::mir::visit::Visitor; use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue}; -use rustc::mir::{Local, Statement, Terminator}; +use rustc::mir::{Statement, Terminator}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts, RegionVid}; @@ -175,10 +175,11 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx self.super_terminator(block, terminator, location); } - fn visit_user_assert_ty( + fn visit_ascribe_user_ty( &mut self, + _place: &Place<'tcx>, + _variance: &ty::Variance, _c_ty: &CanonicalTy<'tcx>, - _local: &Local, _location: Location, ) { } diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index f233a17597a5..71345f22e443 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -144,10 +144,10 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc // EndRegion matters to older NLL/MIR AST borrowck, not to alias NLL StatementKind::EndRegion(..) | StatementKind::Nop | - StatementKind::UserAssertTy(..) | + StatementKind::AscribeUserType(..) | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { - // `Nop`, `UserAssertTy`, `Validate`, and `StorageLive` are irrelevant + // `Nop`, `AscribeUserType`, `Validate`, and `StorageLive` are irrelevant // to borrow check. } StatementKind::StorageDead(local) => { diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index cb15c88bb3e6..214628600b37 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -319,23 +319,34 @@ impl<'tcx> RegionInferenceContext<'tcx> { } for variable in self.definitions.indices() { + let scc = self.constraint_sccs.scc(variable); + match self.definitions[variable].origin { NLLRegionVariableOrigin::FreeRegion => { // For each free, universally quantified region X: // Add all nodes in the CFG to liveness constraints - let variable_scc = self.constraint_sccs.scc(variable); self.liveness_constraints.add_all_points(variable); - self.scc_values.add_all_points(variable_scc); + self.scc_values.add_all_points(scc); // Add `end(X)` into the set for X. - self.add_element_to_scc_of(variable, variable); + self.scc_values.add_element(scc, variable); } NLLRegionVariableOrigin::BoundRegion(ui) => { // Each placeholder region X outlives its - // associated universe but nothing else. - self.add_element_to_scc_of(variable, ui); + // associated universe but nothing else. Every + // placeholder region is always in a universe that + // contains `ui` -- but when placeholder regions + // are placed into an SCC, that SCC may include + // things from other universes that do not include + // `ui`. + let scc_universe = self.scc_universes[scc]; + if ui.is_subset_of(scc_universe) { + self.scc_values.add_element(scc, ui); + } else { + self.add_incompatible_universe(scc); + } } NLLRegionVariableOrigin::Existential => { @@ -383,13 +394,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_universes[scc] } - /// Adds `elem` to the value of the SCC in which `v` appears. - fn add_element_to_scc_of(&mut self, v: RegionVid, elem: impl ToElementIndex) { - debug!("add_live_element({:?}, {:?})", v, elem); - let scc = self.constraint_sccs.scc(v); - self.scc_values.add_element(scc, elem); - } - /// Perform region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -516,22 +520,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // merge the bits. self.scc_values.add_region(scc_a, scc_b); } else { - // Otherwise, the only way for `A` to outlive `B` - // is for it to outlive static. This is actually stricter - // than necessary: ideally, we'd support bounds like `for<'a: 'b`>` - // that might then allow us to approximate `'a` with `'b` and not - // `'static`. But it will have to do for now. - // - // The code here is a bit hacky: we grab the current - // value of the SCC in which `'static` appears, but - // this value may not be fully computed yet. That's ok - // though: it will contain the base liveness values, - // which include (a) the static free region element - // and (b) all the points in the CFG, so it is "good - // enough" to bring it in here for our purposes. - let fr_static = self.universal_regions.fr_static; - let scc_static = constraint_sccs.scc(fr_static); - self.scc_values.add_region(scc_a, scc_static); + self.add_incompatible_universe(scc_a); } } @@ -563,6 +552,19 @@ impl<'tcx> RegionInferenceContext<'tcx> { .all(|u| u.is_subset_of(universe_a)) } + /// Extend `scc` so that it can outlive some placeholder region + /// from a universe it can't name; at present, the only way for + /// this to be true is if `scc` outlives `'static`. This is + /// actually stricter than necessary: ideally, we'd support bounds + /// like `for<'a: 'b`>` that might then allow us to approximate + /// `'a` with `'b` and not `'static`. But it will have to do for + /// now. + fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) { + let fr_static = self.universal_regions.fr_static; + self.scc_values.add_all_points(scc); + self.scc_values.add_element(scc, fr_static); + } + /// Once regions have been propagated, this method is used to see /// whether the "type tests" produced by typeck were satisfied; /// type tests encode type-outlives relationships like `T: diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index e1bd8530629d..d77863d598f6 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -10,7 +10,7 @@ use rustc::ty::subst::Substs; use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; +use rustc::mir::{BasicBlock, Location, Mir, Place, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; @@ -112,8 +112,13 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { debug!("visit_closure_substs: substs={:?}", substs); } - fn visit_user_assert_ty(&mut self, _c_ty: &mut CanonicalTy<'tcx>, _local: &mut Local, - _location: Location) { + fn visit_ascribe_user_ty( + &mut self, + _place: &mut Place<'tcx>, + _variance: &mut ty::Variance, + _c_ty: &mut CanonicalTy<'tcx>, + _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 // add them as constraints at type-check time. 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 82158acc9e6a..de96539ec30f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -17,7 +17,9 @@ use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements}; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; -use borrow_check::nll::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; +use borrow_check::nll::type_check::free_region_relations::{ + CreateResult, UniversalRegionRelations, +}; use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::ToRegionVid; use dataflow::move_paths::MoveData; @@ -246,10 +248,12 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { self.sanitize_type(constant, constant.ty); if let Some(user_ty) = constant.user_ty { - if let Err(terr) = - self.cx - .eq_canonical_type_and_type(user_ty, constant.ty, location.boring()) - { + if let Err(terr) = self.cx.relate_type_and_user_type( + constant.ty, + ty::Variance::Invariant, + user_ty, + location.boring(), + ) { span_mirbug!( self, constant, @@ -271,6 +275,25 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { self.super_local_decl(local, local_decl); self.sanitize_type(local_decl, local_decl.ty); + + if let Some(user_ty) = local_decl.user_ty { + if let Err(terr) = self.cx.relate_type_and_user_type( + local_decl.ty, + ty::Variance::Invariant, + user_ty, + Locations::All, + ) { + span_mirbug!( + self, + local, + "bad user type on variable {:?}: {:?} != {:?} ({:?})", + local, + local_decl.ty, + local_decl.user_ty, + terr, + ); + } + } } fn visit_mir(&mut self, mir: &Mir<'tcx>) { @@ -850,15 +873,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ) } - fn eq_canonical_type_and_type( + fn relate_type_and_user_type( &mut self, - a: CanonicalTy<'tcx>, - b: Ty<'tcx>, + a: Ty<'tcx>, + v: ty::Variance, + b: CanonicalTy<'tcx>, locations: Locations, ) -> Fallible<()> { - relate_tys::eq_canonical_type_and_type( + relate_tys::relate_type_and_user_type( self.infcx, a, + v, b, locations, self.borrowck_context.as_mut().map(|x| &mut **x), @@ -879,8 +904,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // of lowering. Assignments to other sorts of places *are* interesting // though. let is_temp = if let Place::Local(l) = *place { - l != RETURN_PLACE && - !mir.local_decls[l].is_user_variable.is_some() + l != RETURN_PLACE && !mir.local_decls[l].is_user_variable.is_some() } else { false }; @@ -905,9 +929,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } if let Some(user_ty) = self.rvalue_user_ty(rv) { - if let Err(terr) = self.eq_canonical_type_and_type( - user_ty, + if let Err(terr) = self.relate_type_and_user_type( rv_ty, + ty::Variance::Invariant, + user_ty, location.boring(), ) { span_mirbug!( @@ -955,15 +980,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); }; } - StatementKind::UserAssertTy(c_ty, local) => { - let local_ty = mir.local_decls()[local].ty; - if let Err(terr) = self.eq_canonical_type_and_type(c_ty, local_ty, Locations::All) { + StatementKind::AscribeUserType(ref place, variance, c_ty) => { + let place_ty = place.ty(mir, tcx).to_ty(tcx); + if let Err(terr) = + self.relate_type_and_user_type(place_ty, variance, c_ty, Locations::All) + { span_mirbug!( self, stmt, - "bad type assert ({:?} = {:?}): {:?}", + "bad type assert ({:?} <: {:?}): {:?}", + place_ty, c_ty, - local_ty, terr ); } @@ -1142,8 +1169,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { Some((ref dest, _target_block)) => { let dest_ty = dest.ty(mir, tcx).to_ty(tcx); let is_temp = if let Place::Local(l) = *dest { - l != RETURN_PLACE && - !mir.local_decls[l].is_user_variable.is_some() + l != RETURN_PLACE && !mir.local_decls[l].is_user_variable.is_some() } else { false }; @@ -1562,22 +1588,18 @@ 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> { + fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option> { match rvalue { - Rvalue::Use(_) | - Rvalue::Repeat(..) | - Rvalue::Ref(..) | - Rvalue::Len(..) | - Rvalue::Cast(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::NullaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) => - None, + 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, @@ -1585,7 +1607,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { AggregateKind::Tuple => None, AggregateKind::Closure(_, _) => None, AggregateKind::Generator(_, _, _) => None, - } + }, } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index 8ffce9c94926..06cb44ac9714 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -21,8 +21,8 @@ use rustc::ty::subst::Kind; use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::IndexVec; -use std::mem; +/// Adds sufficient constraints to ensure that `a <: b`. pub(super) fn sub_types<'tcx>( infcx: &InferCtxt<'_, '_, 'tcx>, a: Ty<'tcx>, @@ -41,6 +41,7 @@ pub(super) fn sub_types<'tcx>( Ok(()) } +/// Adds sufficient constraints to ensure that `a == b`. pub(super) fn eq_types<'tcx>( infcx: &InferCtxt<'_, '_, 'tcx>, a: Ty<'tcx>, @@ -59,28 +60,38 @@ pub(super) fn eq_types<'tcx>( Ok(()) } -pub(super) fn eq_canonical_type_and_type<'tcx>( +/// Adds sufficient constraints to ensure that `a <: b`, where `b` is +/// a user-given type (which means it may have canonical variables +/// encoding things like `_`). +pub(super) fn relate_type_and_user_type<'tcx>( infcx: &InferCtxt<'_, '_, 'tcx>, - a: CanonicalTy<'tcx>, - b: Ty<'tcx>, + a: Ty<'tcx>, + v: ty::Variance, + b: CanonicalTy<'tcx>, locations: Locations, borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>, ) -> Fallible<()> { debug!( - "eq_canonical_type_and_type(a={:?}, b={:?}, locations={:?})", + "sub_type_and_user_type(a={:?}, b={:?}, locations={:?})", a, b, locations ); let Canonical { - variables: a_variables, - value: a_value, - } = a; + variables: b_variables, + value: b_value, + } = b; + + // The `TypeRelating` code assumes that the "canonical variables" + // appear in the "a" side, so flip `Contravariant` ambient + // variance to get the right relationship. + let v1 = ty::Contravariant.xform(v); + TypeRelating::new( infcx, - ty::Variance::Invariant, + v1, locations, borrowck_context, - a_variables, - ).relate(&a_value, &b)?; + b_variables, + ).relate(&b_value, &a)?; Ok(()) } @@ -128,7 +139,7 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { /// how can we enforce that? I guess I could add some kind of /// "minimum universe constraint" that we can feed to the NLL checker. /// --> also, we know this doesn't happen - canonical_var_values: IndexVec>>, + canonical_var_values: IndexVec>>, } #[derive(Clone, Debug)] @@ -194,23 +205,44 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { scope } + /// When we encounter binders during the type traversal, we record + /// the value to substitute for each of the things contained in + /// that binder. (This will be either a universal placeholder or + /// an existential inference variable.) Given the debruijn index + /// `debruijn` (and name `br`) of some binder we have now + /// encountered, this routine finds the value that we instantiated + /// the region with; to do so, it indexes backwards into the list + /// of ambient scopes `scopes`. + fn lookup_bound_region( + debruijn: ty::DebruijnIndex, + br: &ty::BoundRegion, + first_free_index: ty::DebruijnIndex, + scopes: &[BoundRegionScope], + ) -> RegionVid { + // The debruijn index is a "reverse index" into the + // scopes listing. So when we have INNERMOST (0), we + // want the *last* scope pushed, and so forth. + let debruijn_index = debruijn.index() - first_free_index.index(); + let scope = &scopes[scopes.len() - debruijn_index - 1]; + + // Find this bound region in that scope to map to a + // particular region. + scope.map[br] + } + + /// If `r` is a bound region, find the scope in which it is bound + /// (from `scopes`) and return the value that we instantiated it + /// with. Otherwise just return `r`. fn replace_bound_region( &self, universal_regions: &UniversalRegions<'tcx>, r: ty::Region<'tcx>, + first_free_index: ty::DebruijnIndex, scopes: &[BoundRegionScope], ) -> RegionVid { match r { ty::ReLateBound(debruijn, br) => { - // The debruijn index is a "reverse index" into the - // scopes listing. So when we have INNERMOST (0), we - // want the *last* scope pushed, and so forth. - let debruijn_index = debruijn.index() - ty::INNERMOST.index(); - let scope = &scopes[scopes.len() - debruijn_index - 1]; - - // Find this bound region in that scope to map to a - // particular region. - scope.map[br] + Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) } ty::ReVar(v) => *v, @@ -219,6 +251,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { } } + /// Push a new outlives requirement into our output set of + /// constraints. fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) { debug!("push_outlives({:?}: {:?})", sup, sub); @@ -236,46 +270,55 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { } } - fn equate_var( + /// When we encounter a canonical variable `var` in the output, + /// equate it with `kind`. If the variable has been previously + /// equated, then equate it again. + fn relate_var( &mut self, var: CanonicalVar, b_kind: Kind<'tcx>, ) -> RelateResult<'tcx, Kind<'tcx>> { debug!("equate_var(var={:?}, b_kind={:?})", var, b_kind); - // We only encounter canonical variables when equating. - assert_eq!(self.ambient_variance, ty::Variance::Invariant); - - // The canonical variable already had a value. Equate that - // value with `b`. - let old_value = self.canonical_var_values[var].clone(); - if let Some(ScopesAndKind { scopes, kind }) = old_value { - debug!("equate_var: installing kind={:?} scopes={:?}", kind, scopes); - let old_a_scopes = mem::replace(&mut self.a_scopes, scopes); - let result = self.relate(&kind, &b_kind); - self.a_scopes = old_a_scopes; - debug!("equate_var: complete, result = {:?}", result); - return result; - } + let generalized_kind = match self.canonical_var_values[var] { + Some(v) => v, + None => { + let generalized_kind = self.generalize_value(b_kind); + self.canonical_var_values[var] = Some(generalized_kind); + generalized_kind + } + }; - // Not yet. Capture the value from the RHS and carry on. - self.canonical_var_values[var] = Some(ScopesAndKind { - scopes: self.b_scopes.clone(), - kind: b_kind, - }); - debug!( - "equate_var: capturing value {:?}", - self.canonical_var_values[var] - ); + // The generalized values we extract from `canonical_var_values` have + // been fully instantiated and hence the set of scopes we have + // doesn't matter -- just to be sure, put an empty vector + // in there. + let old_a_scopes = ::std::mem::replace(&mut self.a_scopes, vec![]); + + // Relate the generalized kind to the original one. + let result = self.relate(&generalized_kind, &b_kind); + + // Restore the old scopes now. + self.a_scopes = old_a_scopes; + + debug!("equate_var: complete, result = {:?}", result); + return result; + } - // FIXME -- technically, we should add some sort of - // assertion that this value can be named in the universe - // of the canonical variable. But in practice these - // canonical variables only arise presently in cases where - // they are in the root universe and the main typeck has - // ensured there are no universe errors. So we just kind - // of over look this right now. - Ok(b_kind) + fn generalize_value( + &self, + kind: Kind<'tcx>, + ) -> Kind<'tcx> { + TypeGeneralizer { + type_rel: self, + first_free_index: ty::INNERMOST, + ambient_variance: self.ambient_variance, + + // These always correspond to an `_` or `'_` written by + // user, and those are always in the root universe. + universe: ty::UniverseIndex::ROOT, + }.relate(&kind, &kind) + .unwrap() } } @@ -326,7 +369,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> // Watch out for the case that we are matching a `?T` against the // right-hand side. if let ty::Infer(ty::CanonicalTy(var)) = a.sty { - self.equate_var(var, b.into())?; + self.relate_var(var, b.into())?; Ok(a) } else { debug!( @@ -348,7 +391,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> }) = self.borrowck_context { if let ty::ReCanonical(var) = a { - self.equate_var(*var, b.into())?; + self.relate_var(*var, b.into())?; return Ok(a); } @@ -357,8 +400,10 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> a, b, self.ambient_variance ); - let v_a = self.replace_bound_region(universal_regions, a, &self.a_scopes); - let v_b = self.replace_bound_region(universal_regions, b, &self.b_scopes); + let v_a = + self.replace_bound_region(universal_regions, a, ty::INNERMOST, &self.a_scopes); + let v_b = + self.replace_bound_region(universal_regions, b, ty::INNERMOST, &self.b_scopes); debug!("regions: v_a = {:?}", v_a); debug!("regions: v_b = {:?}", v_b); @@ -425,19 +470,30 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> self.b_scopes.push(b_scope); self.a_scopes.push(a_scope); - // FIXME -- to be fully correct, we would set the ambient - // variance to Covariant here. As is, we will sometimes - // propagate down an ambient variance of Equal -- this in - // turn causes us to report errors in some cases where - // types perhaps *ought* to be equal. See the - // `hr-fn-aau-eq-abu.rs` test for an example. Fixing this - // though is a bit nontrivial: in particular, it would - // require a more involved handling of canonical - // variables, since we would no longer be able to rely on - // having an `==` relationship for canonical variables. + // Reset the ambient variance to covariant. This is needed + // to correctly handle cases like + // + // for<'a> fn(&'a u32, &'a u3) == for<'b, 'c> fn(&'b u32, &'c u32) + // + // Somewhat surprisingly, these two types are actually + // **equal**, even though the one on the right looks more + // polymorphic. The reason is due to subtyping. To see it, + // consider that each function can call the other: + // + // - The left function can call the right with `'b` and + // `'c` both equal to `'a` + // + // - The right function can call the left with `'a` set to + // `{P}`, where P is the point in the CFG where the call + // itself occurs. Note that `'b` and `'c` must both + // include P. At the point, the call works because of + // subtyping (i.e., `&'b u32 <: &{P} u32`). + let variance = ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); self.relate(a.skip_binder(), b.skip_binder())?; + self.ambient_variance = variance; + self.b_scopes.pop().unwrap(); self.a_scopes.pop().unwrap(); } @@ -458,8 +514,17 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> self.a_scopes.push(a_scope); self.b_scopes.push(b_scope); + // Reset ambient variance to contravariance. See the + // covariant case above for an explanation. + let variance = ::std::mem::replace( + &mut self.ambient_variance, + ty::Variance::Contravariant, + ); + self.relate(a.skip_binder(), b.skip_binder())?; + self.ambient_variance = variance; + self.b_scopes.pop().unwrap(); self.a_scopes.pop().unwrap(); } @@ -468,7 +533,14 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> } } -struct ScopeInstantiator<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> { +/// When we encounter a binder like `for<..> fn(..)`, we actually have +/// to walk the `fn` value to find all the values bound by the `for` +/// (these are not explicitly present in the ty representation right +/// now). This visitor handles that: it descends the type, tracking +/// binder depth, and finds late-bound regions targeting the +/// `for<..`>. For each of those, it creates an entry in +/// `bound_region_scope`. +struct ScopeInstantiator<'cx, 'gcx: 'tcx, 'tcx: 'cx> { infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, // The debruijn index of the scope we are instantiating. target_index: ty::DebruijnIndex, @@ -510,3 +582,143 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> { false } } + +/// The "type generalize" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the type `B` -- this replaces +/// all the lifetimes in the type `B` with fresh inference +/// variables. (You can read more about the strategy in this [blog +/// post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// As a side-effect of this generalization procedure, we also replace +/// all the bound regions that we have traversed with concrete values, +/// so that the resulting generalized type is independent from the +/// scopes. +/// +/// [blog post]: https://is.gd/0hKvIr +struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> { + type_rel: &'me TypeRelating<'me, 'bccx, 'gcx, 'tcx>, + + /// After we generalize this type, we are going to relative it to + /// some other type. What will be the variance at this point? + ambient_variance: ty::Variance, + + first_free_index: ty::DebruijnIndex, + + universe: ty::UniverseIndex, +} + +impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'bbcx, 'gcx, 'tcx> { + fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> { + self.type_rel.infcx.tcx + } + + fn tag(&self) -> &'static str { + "nll::generalizer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + a: &T, + b: &T, + ) -> RelateResult<'tcx, T> { + debug!( + "TypeGeneralizer::relate_with_variance(variance={:?}, a={:?}, b={:?})", + variance, a, b + ); + + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + debug!( + "TypeGeneralizer::relate_with_variance: ambient_variance = {:?}", + self.ambient_variance + ); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!("TypeGeneralizer::relate_with_variance: r={:?}", r); + + Ok(r) + } + + fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("TypeGeneralizer::tys(a={:?})", a,); + + match a.sty { + ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => { + bug!( + "unexpected inference variable encountered in NLL generalization: {:?}", + a + ); + } + + _ => relate::super_relate_tys(self, a, a), + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + _: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("TypeGeneralizer::regions(a={:?})", a,); + + if let ty::ReLateBound(debruijn, _) = a { + if *debruijn < self.first_free_index { + return Ok(a); + } + } + + // For now, we just always create a fresh region variable to + // replace all the regions in the source type. In the main + // type checker, we special case the case where the ambient + // variance is `Invariant` and try to avoid creating a fresh + // region variable, but since this comes up so much less in + // NLL (only when users use `_` etc) it is much less + // important. + // + // As an aside, since these new variables are created in + // `self.universe` universe, this also serves to enforce the + // universe scoping rules. + // + // FIXME(#54105) -- if the ambient variance is bivariant, + // though, we may however need to check well-formedness or + // risk a problem like #41677 again. + + let replacement_region_vid = self.type_rel + .infcx + .next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, self.universe); + + Ok(replacement_region_vid) + } + + fn binders( + &mut self, + a: &ty::Binder, + _: &ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + where + T: Relate<'tcx>, + { + debug!("TypeGeneralizer::binders(a={:?})", a,); + + self.first_free_index.shift_in(1); + let result = self.relate(a.skip_binder(), a.skip_binder())?; + self.first_free_index.shift_out(1); + Ok(ty::Binder::bind(result)) + } +} diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index c3637a5abebd..bfb6daee6041 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -106,7 +106,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { remainder_scope, init_scope, pattern, - ty, initializer, lint_level } => { @@ -136,7 +135,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { opt_destruction_scope.map(|de|(de, source_info)), block, |this| { let scope = (init_scope, source_info); this.in_scope(scope, lint_level, block, |this| { - this.expr_into_pattern(block, ty, pattern, init) + this.expr_into_pattern(block, pattern, init) }) })); } else { @@ -144,16 +143,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { None, remainder_span, lint_level, slice::from_ref(&pattern), ArmHasGuard(false), None); - // FIXME(#47184): We currently only insert `UserAssertTy` statements for - // patterns that are bindings, this is as we do not want to deconstruct - // the type being assertion to match the pattern. - if let PatternKind::Binding { var, .. } = *pattern.kind { - if let Some(ty) = ty { - this.user_assert_ty(block, ty, var, span); - } - } - - this.visit_bindings(&pattern, &mut |this, _, _, _, node, span, _| { + this.visit_bindings(&pattern, None, &mut |this, _, _, _, node, span, _, _| { this.storage_live_binding(block, node, span, OutsideGuard); this.schedule_drop_for_binding(node, span, OutsideGuard); }) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 59ebb7703ff6..5708ac4e6b50 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -296,6 +296,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let ptr_temp = this.local_decls.push(LocalDecl { mutability: Mutability::Mut, ty: ptr_ty, + user_ty: None, name: None, source_info, visibility_scope: source_info.scope, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index d3e67ea7b7d7..cef1fb77e5c9 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -13,16 +13,16 @@ //! includes the high-level algorithm, the submodules contain the //! details. +use build::scope::{CachedBlock, DropKind}; +use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard}; use build::{BlockAnd, BlockAndExtension, Builder}; use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; -use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard}; -use build::scope::{CachedBlock, DropKind}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::bitvec::BitArray; -use rustc::ty::{self, Ty}; -use rustc::mir::*; -use rustc::hir; use hair::*; +use rustc::hir; +use rustc::mir::*; +use rustc::ty::{self, CanonicalTy, Ty}; +use rustc_data_structures::bitvec::BitArray; +use rustc_data_structures::fx::FxHashMap; use syntax::ast::{Name, NodeId}; use syntax_pos::Span; @@ -37,13 +37,14 @@ mod util; pub(crate) struct ArmHasGuard(pub bool); impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { - pub fn match_expr(&mut self, - destination: &Place<'tcx>, - span: Span, - mut block: BasicBlock, - discriminant: ExprRef<'tcx>, - arms: Vec>) - -> BlockAnd<()> { + pub fn match_expr( + &mut self, + destination: &Place<'tcx>, + span: Span, + mut block: BasicBlock, + discriminant: ExprRef<'tcx>, + arms: Vec>, + ) -> BlockAnd<()> { let tcx = self.hir.tcx(); let discriminant_span = discriminant.span(); let discriminant_place = unpack!(block = self.as_place(block, discriminant)); @@ -67,7 +68,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let dummy_access = Rvalue::Discriminant(discriminant_place.clone()); let dummy_ty = dummy_access.ty(&self.local_decls, tcx); let dummy_temp = self.temp(dummy_ty, dummy_source_info.span); - self.cfg.push_assign(block, dummy_source_info, &dummy_temp, dummy_access); + self.cfg + .push_assign(block, dummy_source_info, &dummy_temp, dummy_access); let source_info = self.source_info(discriminant_span); let borrowed_input_temp = if tcx.generate_borrow_of_any_match_input() { @@ -75,38 +77,46 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // inference to find an appropriate one. Therefore you can // only use this when NLL is turned on. assert!(tcx.use_mir_borrowck()); - let borrowed_input = - Rvalue::Ref(tcx.types.re_empty, BorrowKind::Shared, discriminant_place.clone()); + let borrowed_input = Rvalue::Ref( + tcx.types.re_empty, + BorrowKind::Shared, + discriminant_place.clone(), + ); let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx); let borrowed_input_temp = self.temp(borrowed_input_ty, span); - self.cfg.push_assign(block, source_info, &borrowed_input_temp, borrowed_input); + self.cfg + .push_assign(block, source_info, &borrowed_input_temp, borrowed_input); Some(borrowed_input_temp) } else { None }; let mut arm_blocks = ArmBlocks { - blocks: arms.iter() - .map(|_| self.cfg.start_new_block()) - .collect(), + blocks: arms.iter().map(|_| self.cfg.start_new_block()).collect(), }; // Get the arm bodies and their scopes, while declaring bindings. - let arm_bodies: Vec<_> = arms.iter().map(|arm| { - // BUG: use arm lint level - let body = self.hir.mirror(arm.body.clone()); - let scope = self.declare_bindings(None, body.span, - LintLevel::Inherited, - &arm.patterns[..], - ArmHasGuard(arm.guard.is_some()), - Some((Some(&discriminant_place), discriminant_span))); - (body, scope.unwrap_or(self.source_scope)) - }).collect(); + let arm_bodies: Vec<_> = arms.iter() + .map(|arm| { + // BUG: use arm lint level + let body = self.hir.mirror(arm.body.clone()); + let scope = self.declare_bindings( + None, + body.span, + LintLevel::Inherited, + &arm.patterns[..], + ArmHasGuard(arm.guard.is_some()), + Some((Some(&discriminant_place), discriminant_span)), + ); + (body, scope.unwrap_or(self.source_scope)) + }) + .collect(); // create binding start block for link them by false edges let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len()); let pre_binding_blocks: Vec<_> = (0..candidate_count + 1) - .map(|_| self.cfg.start_new_block()).collect(); + .map(|_| self.cfg.start_new_block()) + .collect(); // assemble a list of candidates: there is one candidate per // pattern, which means there may be more than one candidate @@ -114,21 +124,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // highest priority candidate comes first in the list. // (i.e. same order as in source) - let candidates: Vec<_> = - arms.iter() - .enumerate() - .flat_map(|(arm_index, arm)| { - arm.patterns.iter().enumerate() - .map(move |(pat_index, pat)| { - (arm_index, pat_index, pat, arm.guard.clone()) - }) - }) - .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1))) - .map(|((arm_index, pat_index, pattern, guard), - (pre_binding_block, next_candidate_pre_binding_block))| { - - if let (true, Some(borrow_temp)) = (tcx.emit_read_for_match(), - borrowed_input_temp.clone()) { + let candidates: Vec<_> = arms.iter() + .enumerate() + .flat_map(|(arm_index, arm)| { + arm.patterns + .iter() + .enumerate() + .map(move |(pat_index, pat)| (arm_index, pat_index, pat, arm.guard.clone())) + }) + .zip( + pre_binding_blocks + .iter() + .zip(pre_binding_blocks.iter().skip(1)), + ) + .map( + |( + (arm_index, pat_index, pattern, guard), + (pre_binding_block, next_candidate_pre_binding_block), + )| { + if let (true, Some(borrow_temp)) = + (tcx.emit_read_for_match(), borrowed_input_temp.clone()) + { // inject a fake read of the borrowed input at // the start of each arm's pattern testing // code. @@ -137,10 +153,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // the variant for an enum while you are in // the midst of matching on it. let pattern_source_info = self.source_info(pattern.span); - self.cfg.push(*pre_binding_block, Statement { - source_info: pattern_source_info, - kind: StatementKind::ReadForMatch(borrow_temp.clone()), - }); + self.cfg.push( + *pre_binding_block, + Statement { + source_info: pattern_source_info, + kind: StatementKind::ReadForMatch(borrow_temp.clone()), + }, + ); } // One might ask: why not build up the match pair such that it @@ -168,18 +187,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span: pattern.span, match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)], bindings: vec![], + ascriptions: vec![], guard, arm_index, pat_index, pre_binding_block: *pre_binding_block, next_candidate_pre_binding_block: *next_candidate_pre_binding_block, } - }) - .collect(); + }, + ) + .collect(); let outer_source_info = self.source_info(span); - self.cfg.terminate(*pre_binding_blocks.last().unwrap(), - outer_source_info, TerminatorKind::Unreachable); + self.cfg.terminate( + *pre_binding_blocks.last().unwrap(), + outer_source_info, + TerminatorKind::Unreachable, + ); // this will generate code to test discriminant_place and // branch to the appropriate arm block @@ -198,7 +222,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { otherwise.sort(); otherwise.dedup(); // variant switches can introduce duplicate target blocks for block in otherwise { - self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + self.cfg + .terminate(block, source_info, TerminatorKind::Unreachable); } } @@ -211,50 +236,75 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Re-enter the source scope we created the bindings in. self.source_scope = source_scope; unpack!(arm_block = self.into(destination, arm_block, body)); - self.cfg.terminate(arm_block, outer_source_info, - TerminatorKind::Goto { target: end_block }); + self.cfg.terminate( + arm_block, + outer_source_info, + TerminatorKind::Goto { target: end_block }, + ); } self.source_scope = outer_source_info.scope; end_block.unit() } - pub fn user_assert_ty(&mut self, block: BasicBlock, hir_id: hir::HirId, - var: NodeId, span: Span) { - if self.hir.tcx().sess.opts.debugging_opts.disable_nll_user_type_assert { return; } - - let local_id = self.var_local_id(var, OutsideGuard); - let source_info = self.source_info(span); - - 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, - irrefutable_pat: Pattern<'tcx>, - initializer: ExprRef<'tcx>) - -> BlockAnd<()> { - // optimize the case of `let x = ...` + pub fn expr_into_pattern( + &mut self, + mut block: BasicBlock, + irrefutable_pat: Pattern<'tcx>, + initializer: ExprRef<'tcx>, + ) -> BlockAnd<()> { match *irrefutable_pat.kind { - PatternKind::Binding { mode: BindingMode::ByValue, - var, - subpattern: None, .. } => { - let place = self.storage_live_binding(block, var, irrefutable_pat.span, - OutsideGuard); - - if let Some(ty) = ty { - self.user_assert_ty(block, ty, var, irrefutable_pat.span); - } + // Optimize the case of `let x = ...` to write directly into `x` + PatternKind::Binding { + mode: BindingMode::ByValue, + var, + subpattern: None, + .. + } => { + let place = + self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); + unpack!(block = self.into(&place, block, initializer)); + self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); + block.unit() + } + // Optimize the case of `let x: T = ...` to write directly + // into `x` and then require that `T == typeof(x)`. + // + // Weirdly, this is needed to prevent the + // `intrinsic-move-val.rs` test case from crashing. That + // test works with uninitialized values in a rather + // dubious way, so it may be that the test is kind of + // broken. + PatternKind::AscribeUserType { + subpattern: Pattern { + kind: box PatternKind::Binding { + mode: BindingMode::ByValue, + var, + subpattern: None, + .. + }, + .. + }, + user_ty: ascription_user_ty, + } => { + let place = + self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); unpack!(block = self.into(&place, block, initializer)); + + let source_info = self.source_info(irrefutable_pat.span); + self.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::AscribeUserType( + place.clone(), + ty::Variance::Invariant, + ascription_user_ty, + ), + }, + ); + self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); block.unit() } @@ -265,24 +315,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - pub fn place_into_pattern(&mut self, - mut block: BasicBlock, - irrefutable_pat: Pattern<'tcx>, - initializer: &Place<'tcx>, - set_match_place: bool) - -> BlockAnd<()> { + pub fn place_into_pattern( + &mut self, + mut block: BasicBlock, + irrefutable_pat: Pattern<'tcx>, + initializer: &Place<'tcx>, + set_match_place: bool, + ) -> BlockAnd<()> { // create a dummy candidate let mut candidate = Candidate { span: irrefutable_pat.span, match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)], bindings: vec![], + ascriptions: vec![], guard: None, // since we don't call `match_candidates`, next fields is unused arm_index: 0, pat_index: 0, pre_binding_block: block, - next_candidate_pre_binding_block: block + next_candidate_pre_binding_block: block, }; // Simplify the candidate. Since the pattern is irrefutable, this should @@ -290,10 +342,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { unpack!(block = self.simplify_candidate(block, &mut candidate)); if !candidate.match_pairs.is_empty() { - span_bug!(candidate.match_pairs[0].pattern.span, - "match pairs {:?} remaining after simplifying \ - irrefutable pattern", - candidate.match_pairs); + span_bug!( + candidate.match_pairs[0].pattern.span, + "match pairs {:?} remaining after simplifying \ + irrefutable pattern", + candidate.match_pairs + ); } // for matches and function arguments, the place that is being matched @@ -304,9 +358,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { for binding in &candidate.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); - if let Some(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm {opt_match_place: Some((ref mut match_place, _)), .. } - ))) = self.local_decls[local].is_user_variable + if let Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + opt_match_place: Some((ref mut match_place, _)), + .. + }))) = self.local_decls[local].is_user_variable { *match_place = Some(initializer.clone()); } else { @@ -315,6 +370,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } + self.ascribe_types(block, &candidate.ascriptions); + // now apply the bindings, which will also declare the variables self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); @@ -325,116 +382,183 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// scope for the bindings in these patterns, if such a scope had to be /// created. NOTE: Declaring the bindings should always be done in their /// drop scope. - pub fn declare_bindings(&mut self, - mut visibility_scope: Option, - scope_span: Span, - lint_level: LintLevel, - patterns: &[Pattern<'tcx>], - has_guard: ArmHasGuard, - opt_match_place: Option<(Option<&Place<'tcx>>, Span)>) - -> Option { - assert!(!(visibility_scope.is_some() && lint_level.is_explicit()), - "can't have both a visibility and a lint scope at the same time"); + pub fn declare_bindings( + &mut self, + mut visibility_scope: Option, + scope_span: Span, + lint_level: LintLevel, + patterns: &[Pattern<'tcx>], + has_guard: ArmHasGuard, + opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, + ) -> Option { + assert!( + !(visibility_scope.is_some() && lint_level.is_explicit()), + "can't have both a visibility and a lint scope at the same time" + ); let mut scope = self.source_scope; let num_patterns = patterns.len(); - self.visit_bindings(&patterns[0], &mut |this, mutability, name, mode, var, span, ty| { - if visibility_scope.is_none() { - visibility_scope = Some(this.new_source_scope(scope_span, - LintLevel::Inherited, - None)); - // If we have lints, create a new source scope - // that marks the lints for the locals. See the comment - // on the `source_info` field for why this is needed. - if lint_level.is_explicit() { - scope = - this.new_source_scope(scope_span, lint_level, None); + self.visit_bindings( + &patterns[0], + None, + &mut |this, mutability, name, mode, var, span, ty, user_ty| { + if visibility_scope.is_none() { + visibility_scope = + Some(this.new_source_scope(scope_span, LintLevel::Inherited, None)); + // If we have lints, create a new source scope + // that marks the lints for the locals. See the comment + // on the `source_info` field for why this is needed. + if lint_level.is_explicit() { + scope = this.new_source_scope(scope_span, lint_level, None); + } } - } - let source_info = SourceInfo { - span, - scope, - }; - let visibility_scope = visibility_scope.unwrap(); - this.declare_binding(source_info, visibility_scope, mutability, name, mode, - num_patterns, var, ty, has_guard, - opt_match_place.map(|(x, y)| (x.cloned(), y)), - patterns[0].span); - }); + let source_info = SourceInfo { span, scope }; + let visibility_scope = visibility_scope.unwrap(); + this.declare_binding( + source_info, + visibility_scope, + mutability, + name, + mode, + num_patterns, + var, + ty, + user_ty, + has_guard, + opt_match_place.map(|(x, y)| (x.cloned(), y)), + patterns[0].span, + ); + }, + ); visibility_scope } - pub fn storage_live_binding(&mut self, - block: BasicBlock, - var: NodeId, - span: Span, - for_guard: ForGuard) - -> Place<'tcx> - { + pub fn storage_live_binding( + &mut self, + block: BasicBlock, + var: NodeId, + span: Span, + for_guard: ForGuard, + ) -> Place<'tcx> { let local_id = self.var_local_id(var, for_guard); let source_info = self.source_info(span); - self.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(local_id) - }); + self.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::StorageLive(local_id), + }, + ); let place = Place::Local(local_id); let var_ty = self.local_decls[local_id].ty; let hir_id = self.hir.tcx().hir.node_to_hir_id(var); let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id); - self.schedule_drop( - span, region_scope, &place, var_ty, - DropKind::Storage, - ); + self.schedule_drop(span, region_scope, &place, var_ty, DropKind::Storage); place } - pub fn schedule_drop_for_binding(&mut self, - var: NodeId, - span: Span, - for_guard: ForGuard) { + pub fn schedule_drop_for_binding(&mut self, var: NodeId, span: Span, for_guard: ForGuard) { let local_id = self.var_local_id(var, for_guard); let var_ty = self.local_decls[local_id].ty; let hir_id = self.hir.tcx().hir.node_to_hir_id(var); let region_scope = self.hir.region_scope_tree.var_scope(hir_id.local_id); self.schedule_drop( - span, region_scope, &Place::Local(local_id), var_ty, + span, + region_scope, + &Place::Local(local_id), + var_ty, DropKind::Value { cached_block: CachedBlock::default(), }, ); } - pub fn visit_bindings(&mut self, pattern: &Pattern<'tcx>, f: &mut F) - where F: FnMut(&mut Self, Mutability, Name, BindingMode, NodeId, Span, Ty<'tcx>) - { + pub fn visit_bindings( + &mut self, + pattern: &Pattern<'tcx>, + mut pattern_user_ty: Option>, + f: &mut impl FnMut( + &mut Self, + Mutability, + Name, + BindingMode, + NodeId, + Span, + Ty<'tcx>, + Option>, + ), + ) { match *pattern.kind { - PatternKind::Binding { mutability, name, mode, var, ty, ref subpattern, .. } => { - f(self, mutability, name, mode, var, pattern.span, ty); + PatternKind::Binding { + mutability, + name, + mode, + var, + ty, + ref subpattern, + .. + } => { + match mode { + BindingMode::ByValue => { } + BindingMode::ByRef(..) => { + // If this is a `ref` binding (e.g., `let ref + // x: T = ..`), then the type of `x` is not + // `T` but rather `&T`, so ignore + // `pattern_user_ty` for now. + // + // FIXME(#47184): extract or handle `pattern_user_ty` somehow + pattern_user_ty = None; + } + } + + f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty); if let Some(subpattern) = subpattern.as_ref() { - self.visit_bindings(subpattern, f); + self.visit_bindings(subpattern, pattern_user_ty, f); } } - PatternKind::Array { ref prefix, ref slice, ref suffix } | - PatternKind::Slice { ref prefix, ref slice, ref suffix } => { + PatternKind::Array { + ref prefix, + ref slice, + ref suffix, + } + | PatternKind::Slice { + ref prefix, + ref slice, + ref suffix, + } => { + // FIXME(#47184): extract or handle `pattern_user_ty` somehow for subpattern in prefix.iter().chain(slice).chain(suffix) { - self.visit_bindings(subpattern, f); + self.visit_bindings(subpattern, None, f); } } - PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => { - } + PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {} PatternKind::Deref { ref subpattern } => { - self.visit_bindings(subpattern, f); + // FIXME(#47184): extract or handle `pattern_user_ty` somehow + self.visit_bindings(subpattern, None, f); } - PatternKind::Leaf { ref subpatterns } | - PatternKind::Variant { ref subpatterns, .. } => { + PatternKind::AscribeUserType { ref subpattern, user_ty } => { + // This corresponds to something like + // + // ``` + // let (p1: T1): T2 = ...; + // ``` + // + // Not presently possible, though maybe someday. + assert!(pattern_user_ty.is_none()); + self.visit_bindings(subpattern, Some(user_ty), f) + } + PatternKind::Leaf { ref subpatterns } + | PatternKind::Variant { + ref subpatterns, .. + } => { + // FIXME(#47184): extract or handle `pattern_user_ty` somehow for subpattern in subpatterns { - self.visit_bindings(&subpattern.pattern, f); + self.visit_bindings(&subpattern.pattern, None, f); } } } } } - /// List of blocks for each arm (and potentially other metadata in the /// future). struct ArmBlocks { @@ -442,7 +566,7 @@ struct ArmBlocks { } #[derive(Clone, Debug)] -pub struct Candidate<'pat, 'tcx:'pat> { +pub struct Candidate<'pat, 'tcx: 'pat> { // span of the original pattern that gave rise to this candidate span: Span, @@ -452,6 +576,9 @@ pub struct Candidate<'pat, 'tcx:'pat> { // ...these bindings established... bindings: Vec>, + // ...these types asserted... + ascriptions: Vec>, + // ...and the guard must be evaluated... guard: Option>, @@ -477,8 +604,18 @@ struct Binding<'tcx> { binding_mode: BindingMode<'tcx>, } +/// Indicates that the type of `source` must be a subtype of the +/// user-given type `user_ty`; this is basically a no-op but can +/// influence region inference. +#[derive(Clone, Debug)] +struct Ascription<'tcx> { + span: Span, + source: Place<'tcx>, + user_ty: CanonicalTy<'tcx>, +} + #[derive(Clone, Debug)] -pub struct MatchPair<'pat, 'tcx:'pat> { +pub struct MatchPair<'pat, 'tcx: 'pat> { // this place... place: Place<'tcx>, @@ -490,7 +627,7 @@ pub struct MatchPair<'pat, 'tcx:'pat> { // the "rest" part of the pattern right now has type &[T] and // as such, it requires an Rvalue::Slice to be generated. // See RFC 495 / issue #23121 for the eventual (proper) solution. - slice_len_checked: bool + slice_len_checked: bool, } #[derive(Clone, Debug, PartialEq)] @@ -559,15 +696,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// up the list of candidates and recurse with a non-exhaustive /// list. This is important to keep the size of the generated code /// under control. See `test_candidates` for more details. - fn match_candidates<'pat>(&mut self, - span: Span, - arm_blocks: &mut ArmBlocks, - mut candidates: Vec>, - mut block: BasicBlock) - -> Vec - { - debug!("matched_candidate(span={:?}, block={:?}, candidates={:?})", - span, block, candidates); + fn match_candidates<'pat>( + &mut self, + span: Span, + arm_blocks: &mut ArmBlocks, + mut candidates: Vec>, + mut block: BasicBlock, + ) -> Vec { + debug!( + "matched_candidate(span={:?}, block={:?}, candidates={:?})", + span, block, candidates + ); // Start by simplifying candidates. Once this process is // complete, all the match pairs which remain require some @@ -580,13 +719,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // whether the higher priority candidates (and hence at // the front of the vec) have satisfied all their match // pairs. - let fully_matched = - candidates.iter().take_while(|c| c.match_pairs.is_empty()).count(); - debug!("match_candidates: {:?} candidates fully matched", fully_matched); + let fully_matched = candidates + .iter() + .take_while(|c| c.match_pairs.is_empty()) + .count(); + debug!( + "match_candidates: {:?} candidates fully matched", + fully_matched + ); let mut unmatched_candidates = candidates.split_off(fully_matched); - let fully_matched_with_guard = - candidates.iter().take_while(|c| c.guard.is_some()).count(); + let fully_matched_with_guard = candidates.iter().take_while(|c| c.guard.is_some()).count(); let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() { candidates.split_off(fully_matched_with_guard + 1) @@ -603,20 +746,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // if None is returned, then any remaining candidates // are unreachable (at least not through this path). // Link them with false edges. - debug!("match_candidates: add false edges for unreachable {:?} and unmatched {:?}", - unreachable_candidates, unmatched_candidates); + debug!( + "match_candidates: add false edges for unreachable {:?} and unmatched {:?}", + unreachable_candidates, unmatched_candidates + ); for candidate in unreachable_candidates { let source_info = self.source_info(candidate.span); let target = self.cfg.start_new_block(); - if let Some(otherwise) = self.bind_and_guard_matched_candidate(target, - arm_blocks, - candidate) { - self.cfg.terminate(otherwise, source_info, TerminatorKind::Unreachable); + if let Some(otherwise) = + self.bind_and_guard_matched_candidate(target, arm_blocks, candidate) + { + self.cfg + .terminate(otherwise, source_info, TerminatorKind::Unreachable); } } if unmatched_candidates.is_empty() { - return vec![] + return vec![]; } else { let target = self.cfg.start_new_block(); return self.match_candidates(span, arm_blocks, unmatched_candidates, target); @@ -649,11 +795,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.match_candidates(span, arm_blocks, untested_candidates, join_block) } - fn join_otherwise_blocks(&mut self, - span: Span, - mut otherwise: Vec) - -> BasicBlock - { + fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec) -> BasicBlock { let source_info = self.source_info(span); otherwise.sort(); otherwise.dedup(); // variant switches can introduce duplicate target blocks @@ -662,8 +804,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } else { let join_block = self.cfg.start_new_block(); for block in otherwise { - self.cfg.terminate(block, source_info, - TerminatorKind::Goto { target: join_block }); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); } join_block } @@ -781,13 +926,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// In addition to avoiding exponential-time blowups, this algorithm /// also has nice property that each guard and arm is only generated /// once. - fn test_candidates<'pat>(&mut self, - span: Span, - arm_blocks: &mut ArmBlocks, - candidates: &[Candidate<'pat, 'tcx>], - block: BasicBlock) - -> (Vec, usize) - { + fn test_candidates<'pat>( + &mut self, + span: Span, + arm_blocks: &mut ArmBlocks, + candidates: &[Candidate<'pat, 'tcx>], + block: BasicBlock, + ) -> (Vec, usize) { // extract the match-pair from the highest priority candidate let match_pair = &candidates.first().unwrap().match_pairs[0]; let mut test = self.test(match_pair); @@ -797,34 +942,44 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // may want to add cases based on the candidates that are // available match test.kind { - TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => { + TestKind::SwitchInt { + switch_ty, + ref mut options, + ref mut indices, + } => { for candidate in candidates.iter() { - if !self.add_cases_to_switch(&match_pair.place, - candidate, - switch_ty, - options, - indices) { + if !self.add_cases_to_switch( + &match_pair.place, + candidate, + switch_ty, + options, + indices, + ) { break; } } } - TestKind::Switch { adt_def: _, ref mut variants} => { + TestKind::Switch { + adt_def: _, + ref mut variants, + } => { for candidate in candidates.iter() { - if !self.add_variants_to_switch(&match_pair.place, - candidate, - variants) { + if !self.add_variants_to_switch(&match_pair.place, candidate, variants) { break; } } } - _ => { } + _ => {} } // perform the test, branching to one of N blocks. For each of // those N possible outcomes, create a (initially empty) // vector of candidates. Those are the candidates that still // apply if the test has that particular outcome. - debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); + debug!( + "match_candidates: test={:?} match_pair={:?}", + test, match_pair + ); let target_blocks = self.perform_test(block, &match_pair.place, &test); let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect(); @@ -832,31 +987,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // `target_candidates`. Note that at some point we may // encounter a candidate where the test is not relevant; at // that point, we stop sorting. - let tested_candidates = - candidates.iter() - .take_while(|c| self.sort_candidate(&match_pair.place, - &test, - c, - &mut target_candidates)) - .count(); + let tested_candidates = candidates + .iter() + .take_while(|c| { + self.sort_candidate(&match_pair.place, &test, c, &mut target_candidates) + }) + .count(); assert!(tested_candidates > 0); // at least the last candidate ought to be tested debug!("tested_candidates: {}", tested_candidates); - debug!("untested_candidates: {}", candidates.len() - tested_candidates); + debug!( + "untested_candidates: {}", + candidates.len() - tested_candidates + ); // For each outcome of test, process the candidates that still // apply. Collect a list of blocks where control flow will // branch if one of the `target_candidate` sets is not // exhaustive. - let otherwise: Vec<_> = - target_blocks.into_iter() - .zip(target_candidates) - .flat_map(|(target_block, target_candidates)| { - self.match_candidates(span, - arm_blocks, - target_candidates, - target_block) - }) - .collect(); + let otherwise: Vec<_> = target_blocks + .into_iter() + .zip(target_candidates) + .flat_map(|(target_block, target_candidates)| { + self.match_candidates(span, arm_blocks, target_candidates, target_block) + }) + .collect(); (otherwise, tested_candidates) } @@ -873,30 +1027,41 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// bindings, further tests would be a use-after-move (which would /// in turn be detected by the borrowck code that runs on the /// MIR). - fn bind_and_guard_matched_candidate<'pat>(&mut self, - mut block: BasicBlock, - arm_blocks: &mut ArmBlocks, - candidate: Candidate<'pat, 'tcx>) - -> Option { - debug!("bind_and_guard_matched_candidate(block={:?}, candidate={:?})", - block, candidate); + fn bind_and_guard_matched_candidate<'pat>( + &mut self, + mut block: BasicBlock, + arm_blocks: &mut ArmBlocks, + candidate: Candidate<'pat, 'tcx>, + ) -> Option { + debug!( + "bind_and_guard_matched_candidate(block={:?}, candidate={:?})", + block, candidate + ); debug_assert!(candidate.match_pairs.is_empty()); + self.ascribe_types(block, &candidate.ascriptions); + let arm_block = arm_blocks.blocks[candidate.arm_index]; let candidate_source_info = self.source_info(candidate.span); - self.cfg.terminate(block, candidate_source_info, - TerminatorKind::Goto { target: candidate.pre_binding_block }); + self.cfg.terminate( + block, + candidate_source_info, + TerminatorKind::Goto { + target: candidate.pre_binding_block, + }, + ); block = self.cfg.start_new_block(); - self.cfg.terminate(candidate.pre_binding_block, candidate_source_info, - TerminatorKind::FalseEdges { - real_target: block, - imaginary_targets: - vec![candidate.next_candidate_pre_binding_block], - }); - + self.cfg.terminate( + candidate.pre_binding_block, + candidate_source_info, + TerminatorKind::FalseEdges { + real_target: block, + imaginary_targets: vec![candidate.next_candidate_pre_binding_block], + }, + ); // rust-lang/rust#27282: The `autoref` business deserves some // explanation here. @@ -980,13 +1145,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // match input itself; it is up to us to create a place // holding a `&` or `&mut` that we can then borrow). - let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards(); + let autoref = self.hir + .tcx() + .all_pat_vars_are_implicit_refs_within_guards(); if let Some(guard) = candidate.guard { if autoref { self.bind_matched_candidate_for_guard( - block, candidate.pat_index, &candidate.bindings); + block, + candidate.pat_index, + &candidate.bindings, + ); let guard_frame = GuardFrame { - locals: candidate.bindings.iter() + locals: candidate + .bindings + .iter() .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)) .collect(), }; @@ -1005,7 +1177,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let cond = unpack!(block = self.as_local_operand(block, guard)); if autoref { let guard_frame = self.guard_context.pop().unwrap(); - debug!("Exiting guard building context with locals: {:?}", guard_frame); + debug!( + "Exiting guard building context with locals: {:?}", + guard_frame + ); } let false_edge_block = self.cfg.start_new_block(); @@ -1037,45 +1212,82 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // // and that is clearly not correct. let post_guard_block = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, - TerminatorKind::if_(self.hir.tcx(), cond, post_guard_block, - false_edge_block)); + self.cfg.terminate( + block, + source_info, + TerminatorKind::if_(self.hir.tcx(), cond, post_guard_block, false_edge_block), + ); if autoref { self.bind_matched_candidate_for_arm_body(post_guard_block, &candidate.bindings); } - self.cfg.terminate(post_guard_block, source_info, - TerminatorKind::Goto { target: arm_block }); + self.cfg.terminate( + post_guard_block, + source_info, + TerminatorKind::Goto { target: arm_block }, + ); let otherwise = self.cfg.start_new_block(); - self.cfg.terminate(false_edge_block, source_info, - TerminatorKind::FalseEdges { - real_target: otherwise, - imaginary_targets: - vec![candidate.next_candidate_pre_binding_block], - }); + self.cfg.terminate( + false_edge_block, + source_info, + TerminatorKind::FalseEdges { + real_target: otherwise, + imaginary_targets: vec![candidate.next_candidate_pre_binding_block], + }, + ); Some(otherwise) } else { // (Here, it is not too early to bind the matched // candidate on `block`, because there is no guard result // that we have to inspect before we bind them.) self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); - self.cfg.terminate(block, candidate_source_info, - TerminatorKind::Goto { target: arm_block }); + self.cfg.terminate( + block, + candidate_source_info, + TerminatorKind::Goto { target: arm_block }, + ); None } } + /// Append `AscribeUserType` statements onto the end of `block` + /// for each ascription + fn ascribe_types<'pat>( + &mut self, + block: BasicBlock, + ascriptions: &[Ascription<'tcx>], + ) { + for ascription in ascriptions { + let source_info = self.source_info(ascription.span); + self.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::AscribeUserType( + ascription.source.clone(), + ty::Variance::Covariant, + ascription.user_ty, + ), + }, + ); + } + } + // Only called when all_pat_vars_are_implicit_refs_within_guards, // and thus all code/comments assume we are in that context. - fn bind_matched_candidate_for_guard(&mut self, - block: BasicBlock, - pat_index: usize, - bindings: &[Binding<'tcx>]) { - debug!("bind_matched_candidate_for_guard(block={:?}, pat_index={:?}, bindings={:?})", - block, pat_index, bindings); + fn bind_matched_candidate_for_guard( + &mut self, + block: BasicBlock, + pat_index: usize, + bindings: &[Binding<'tcx>], + ) { + debug!( + "bind_matched_candidate_for_guard(block={:?}, pat_index={:?}, bindings={:?})", + block, pat_index, bindings + ); // Assign each of the bindings. Since we are binding for a // guard expression, this will never trigger moves out of the @@ -1088,8 +1300,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // a reference R: &T pointing to the location matched by // the pattern, and every occurrence of P within a guard // denotes *R. - let ref_for_guard = self.storage_live_binding( - block, binding.var_id, binding.span, RefWithinGuard); + let ref_for_guard = + self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard); // Question: Why schedule drops if bindings are all // shared-&'s? Answer: Because schedule_drop_for_binding // also emits StorageDead's for those locals. @@ -1097,7 +1309,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match binding.binding_mode { BindingMode::ByValue => { let rvalue = Rvalue::Ref(re_empty, BorrowKind::Shared, binding.source.clone()); - self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue); + self.cfg + .push_assign(block, source_info, &ref_for_guard, rvalue); } BindingMode::ByRef(region, borrow_kind) => { // Tricky business: For `ref id` and `ref mut id` @@ -1114,9 +1327,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // used by the arm body itself. This eases // observing two-phase borrow restrictions. let val_for_guard = self.storage_live_binding( - block, binding.var_id, binding.span, ValWithinGuard(pat_index)); + block, + binding.var_id, + binding.span, + ValWithinGuard(pat_index), + ); self.schedule_drop_for_binding( - binding.var_id, binding.span, ValWithinGuard(pat_index)); + binding.var_id, + binding.span, + ValWithinGuard(pat_index), + ); // rust-lang/rust#27282: We reuse the two-phase // borrow infrastructure so that the mutable @@ -1126,27 +1346,36 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // discussion on rust-lang/rust#49870. let borrow_kind = match borrow_kind { BorrowKind::Shared | BorrowKind::Unique => borrow_kind, - BorrowKind::Mut { .. } => BorrowKind::Mut { allow_two_phase_borrow: true }, + BorrowKind::Mut { .. } => BorrowKind::Mut { + allow_two_phase_borrow: true, + }, }; let rvalue = Rvalue::Ref(region, borrow_kind, binding.source.clone()); - self.cfg.push_assign(block, source_info, &val_for_guard, rvalue); + self.cfg + .push_assign(block, source_info, &val_for_guard, rvalue); let rvalue = Rvalue::Ref(region, BorrowKind::Shared, val_for_guard); - self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue); + self.cfg + .push_assign(block, source_info, &ref_for_guard, rvalue); } } } } - fn bind_matched_candidate_for_arm_body(&mut self, - block: BasicBlock, - bindings: &[Binding<'tcx>]) { - debug!("bind_matched_candidate_for_arm_body(block={:?}, bindings={:?}", block, bindings); + fn bind_matched_candidate_for_arm_body( + &mut self, + block: BasicBlock, + bindings: &[Binding<'tcx>], + ) { + debug!( + "bind_matched_candidate_for_arm_body(block={:?}, bindings={:?}", + block, bindings + ); // Assign each of the bindings. This may trigger moves out of the candidate. for binding in bindings { let source_info = self.source_info(binding.span); - let local = self.storage_live_binding(block, binding.var_id, binding.span, - OutsideGuard); + let local = + self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard); self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); let rvalue = match binding.binding_mode { BindingMode::ByValue => { @@ -1175,22 +1404,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// parts of the matched data, and we want them to be distinct /// temps in order to simplify checks performed by our internal /// leveraging of two-phase borrows). - fn declare_binding(&mut self, - source_info: SourceInfo, - visibility_scope: SourceScope, - mutability: Mutability, - name: Name, - mode: BindingMode, - num_patterns: usize, - var_id: NodeId, - var_ty: Ty<'tcx>, - has_guard: ArmHasGuard, - opt_match_place: Option<(Option>, Span)>, - pat_span: Span) - { - debug!("declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ - visibility_scope={:?}, source_info={:?})", - var_id, name, mode, var_ty, visibility_scope, source_info); + fn declare_binding( + &mut self, + source_info: SourceInfo, + visibility_scope: SourceScope, + mutability: Mutability, + name: Name, + mode: BindingMode, + num_patterns: usize, + var_id: NodeId, + var_ty: Ty<'tcx>, + user_var_ty: Option>, + has_guard: ArmHasGuard, + opt_match_place: Option<(Option>, Span)>, + pat_span: Span, + ) { + debug!( + "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ + visibility_scope={:?}, source_info={:?})", + var_id, name, mode, var_ty, visibility_scope, source_info + ); let tcx = self.hir.tcx(); let binding_mode = match mode { @@ -1199,7 +1432,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; let local = LocalDecl::<'tcx> { mutability, - ty: var_ty.clone(), + ty: var_ty, + user_ty: user_var_ty, name: Some(name), source_info, visibility_scope, @@ -1231,6 +1465,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // See previous comment. mutability: Mutability::Not, ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty), + user_ty: None, name: Some(name), source_info, visibility_scope, @@ -1238,7 +1473,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { internal: false, is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)), }); - LocalsForNode::ForGuard { vals_for_guard, ref_for_guard, for_arm_body } + LocalsForNode::ForGuard { + vals_for_guard, + ref_for_guard, + for_arm_body, + } } else { LocalsForNode::One(for_arm_body) }; diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 147b8cc2175a..14da8e908389 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -23,7 +23,7 @@ //! testing a value against a constant. use build::{BlockAnd, BlockAndExtension, Builder}; -use build::matches::{Binding, MatchPair, Candidate}; +use build::matches::{Ascription, Binding, MatchPair, Candidate}; use hair::*; use rustc::mir::*; @@ -63,6 +63,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { candidate: &mut Candidate<'pat, 'tcx>) -> Result<(), MatchPair<'pat, 'tcx>> { match *match_pair.pattern.kind { + PatternKind::AscribeUserType { ref subpattern, user_ty } => { + candidate.ascriptions.push(Ascription { + span: match_pair.pattern.span, + user_ty, + source: match_pair.place.clone(), + }); + + candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); + + Ok(()) + } + PatternKind::Wild => { // nothing left to do Ok(()) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 4a0b4b0c8858..2c606ebf322a 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -96,6 +96,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } + PatternKind::AscribeUserType { .. } | PatternKind::Array { .. } | PatternKind::Slice { .. } | PatternKind::Wild | @@ -138,6 +139,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Array { .. } | PatternKind::Wild | PatternKind::Binding { .. } | + PatternKind::AscribeUserType { .. } | PatternKind::Leaf { .. } | PatternKind::Deref { .. } => { // don't know how to add these patterns to a switch @@ -638,6 +640,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span: candidate.span, match_pairs: other_match_pairs, bindings: candidate.bindings.clone(), + ascriptions: candidate.ascriptions.clone(), guard: candidate.guard.clone(), arm_index: candidate.arm_index, pat_index: candidate.pat_index, @@ -702,6 +705,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span: candidate.span, match_pairs: all_match_pairs, bindings: candidate.bindings.clone(), + ascriptions: candidate.ascriptions.clone(), guard: candidate.guard.clone(), arm_index: candidate.arm_index, pat_index: candidate.pat_index, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 322a6977bedd..576c91a02b08 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -730,6 +730,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.local_decls.push(LocalDecl { mutability: Mutability::Mut, ty, + user_ty: None, source_info, visibility_scope: source_info.scope, name, diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index f42612b4b7a7..cc92cdecc607 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -338,7 +338,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { mir::StatementKind::SetDiscriminant { .. } | mir::StatementKind::StorageLive(..) | mir::StatementKind::Validate(..) | - mir::StatementKind::UserAssertTy(..) | + mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Nop => {} } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index dde2e819fd20..5451d27082db 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -304,7 +304,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } StatementKind::EndRegion(_) | StatementKind::Validate(..) | - StatementKind::UserAssertTy(..) | + StatementKind::AscribeUserType(..) | StatementKind::Nop => {} } } diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 6c8b5d97b6ff..85a9734a6010 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -76,14 +76,26 @@ 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| ty.hir_id); - let pattern = cx.pattern_from_hir(&local.pat); + let mut pattern = cx.pattern_from_hir(&local.pat); + + if let Some(ty) = &local.ty { + if let Some(user_ty) = cx.tables.user_provided_tys().get(ty.hir_id) { + pattern = Pattern { + ty: pattern.ty, + span: pattern.span, + kind: Box::new(PatternKind::AscribeUserType { + user_ty: *user_ty, + subpattern: pattern + }) + }; + } + } + result.push(StmtRef::Mirror(Box::new(Stmt { kind: StmtKind::Let { remainder_scope: remainder_scope, init_scope: region::Scope::Node(hir_id.local_id), pattern, - ty, initializer: local.init.to_ref(), lint_level: cx.lint_level_of(local.id), }, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 2ddb810f4914..d86aee543126 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -93,12 +93,11 @@ pub enum StmtKind<'tcx> { /// lifetime of temporaries init_scope: region::Scope, - /// let : ty = ... + /// `let = ...` + /// + /// if a type is included, it is added as an ascription pattern pattern: Pattern<'tcx>, - /// let pat: = init ... - ty: Option, - /// let pat: ty = ... initializer: Option>, diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index d7fbbc88cc16..e05e0bb0470f 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1235,6 +1235,8 @@ fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt, -> Option>> { match *pat.kind { + PatternKind::AscribeUserType { ref subpattern, .. } => + pat_constructors(cx, subpattern, pcx), PatternKind::Binding { .. } | PatternKind::Wild => None, PatternKind::Leaf { .. } | PatternKind::Deref { .. } => Some(vec![Single]), PatternKind::Variant { adt_def, variant_index, .. } => { @@ -1606,6 +1608,9 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( let pat = &r[0]; let head: Option> = match *pat.kind { + PatternKind::AscribeUserType { ref subpattern, .. } => + specialize(cx, ::std::slice::from_ref(&subpattern), constructor, wild_patterns), + PatternKind::Binding { .. } | PatternKind::Wild => { Some(wild_patterns.to_owned()) } diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 384ee0079dc8..cd44869211a7 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -20,7 +20,7 @@ use interpret::{const_field, const_variant_index}; use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability}; use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend}; -use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; +use rustc::ty::{self, CanonicalTy, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; use rustc::hir::def::{Def, CtorKind}; @@ -66,6 +66,11 @@ pub struct Pattern<'tcx> { pub enum PatternKind<'tcx> { Wild, + AscribeUserType { + user_ty: CanonicalTy<'tcx>, + subpattern: Pattern<'tcx>, + }, + /// x, ref x, x @ P, etc Binding { mutability: Mutability, @@ -125,6 +130,8 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self.kind { PatternKind::Wild => write!(f, "_"), + PatternKind::AscribeUserType { ref subpattern, .. } => + write!(f, "{}: _", subpattern), PatternKind::Binding { mutability, name, mode, ref subpattern, .. } => { let is_mut = match mode { BindingMode::ByValue => mutability == Mutability::Mut, @@ -939,7 +946,7 @@ macro_rules! CloneImpls { CloneImpls!{ <'tcx> Span, Field, Mutability, ast::Name, ast::NodeId, usize, &'tcx ty::Const<'tcx>, Region<'tcx>, Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef, - &'tcx Substs<'tcx>, &'tcx Kind<'tcx> + &'tcx Substs<'tcx>, &'tcx Kind<'tcx>, CanonicalTy<'tcx> } impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { @@ -973,6 +980,13 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { fn super_fold_with>(&self, folder: &mut F) -> Self { match *self { PatternKind::Wild => PatternKind::Wild, + PatternKind::AscribeUserType { + ref subpattern, + user_ty, + } => PatternKind::AscribeUserType { + subpattern: subpattern.fold_with(folder), + user_ty: user_ty.fold_with(folder), + }, PatternKind::Binding { mutability, name, diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 545333e87917..cb8e1284d096 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -159,7 +159,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } EndRegion(..) => {} - UserAssertTy(..) => {} + AscribeUserType(..) => {} // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index d4024981c375..f2c011ccee6a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -39,6 +39,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(if_while_or_patterns)] #![feature(try_from)] #![feature(reverse_bits)] +#![feature(underscore_imports)] #![recursion_limit="256"] diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 7e7e7cfade62..a6c039756857 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -140,7 +140,9 @@ enum CallKind { fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl { let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span }; LocalDecl { - mutability, ty, name: None, + mutability, ty, + user_ty: None, + name: None, source_info, visibility_scope: source_info.scope, internal: false, diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index ec7fd371a442..6fbc2f85c08d 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -114,7 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { StatementKind::StorageDead(..) | StatementKind::EndRegion(..) | StatementKind::Validate(..) | - StatementKind::UserAssertTy(..) | + StatementKind::AscribeUserType(..) | StatementKind::Nop => { // safe (at least as emitted during MIR construction) } diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs index 256b1fd66e9a..9edb1a1f76a6 100644 --- a/src/librustc_mir/transform/cleanup_post_borrowck.rs +++ b/src/librustc_mir/transform/cleanup_post_borrowck.rs @@ -12,7 +12,7 @@ //! //! - `CleanEndRegions`, that reduces the set of `EndRegion` statements //! in the MIR. -//! - `CleanUserAssertTy`, that replaces all `UserAssertTy` statements +//! - `CleanAscribeUserType`, that replaces all `AscribeUserType` statements //! with `Nop`. //! //! The `CleanEndRegions` "pass" is actually implemented as two @@ -24,10 +24,10 @@ //! MIR and removes any `EndRegion` that is applied to a region that //! was not seen in the previous pass. //! -//! The `CleanUserAssertTy` pass runs at a distinct time from the -//! `CleanEndRegions` pass. It is important that the `CleanUserAssertTy` +//! The `CleanAscribeUserType` pass runs at a distinct time from the +//! `CleanEndRegions` pass. It is important that the `CleanAscribeUserType` //! pass runs after the MIR borrowck so that the NLL type checker can -//! perform the type assertion when it encounters the `UserAssertTy` +//! perform the type assertion when it encounters the `AscribeUserType` //! statements. use rustc_data_structures::fx::FxHashSet; @@ -110,26 +110,26 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> { } } -pub struct CleanUserAssertTy; +pub struct CleanAscribeUserType; -pub struct DeleteUserAssertTy; +pub struct DeleteAscribeUserType; -impl MirPass for CleanUserAssertTy { +impl MirPass for CleanAscribeUserType { fn run_pass<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _source: MirSource, mir: &mut Mir<'tcx>) { - let mut delete = DeleteUserAssertTy; + let mut delete = DeleteAscribeUserType; delete.visit_mir(mir); } } -impl<'tcx> MutVisitor<'tcx> for DeleteUserAssertTy { +impl<'tcx> MutVisitor<'tcx> for DeleteAscribeUserType { fn visit_statement(&mut self, block: BasicBlock, statement: &mut Statement<'tcx>, location: Location) { - if let StatementKind::UserAssertTy(..) = statement.kind { + if let StatementKind::AscribeUserType(..) = statement.kind { statement.make_nop(); } self.super_statement(block, statement, location); diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index dcbf92b57b13..f1f42768ce32 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -303,6 +303,7 @@ fn replace_result_variable<'tcx>( let new_ret = LocalDecl { mutability: Mutability::Mut, ty: ret_ty, + user_ty: None, name: None, source_info, visibility_scope: source_info.scope, @@ -656,6 +657,7 @@ fn create_generator_drop_shim<'a, 'tcx>( mir.local_decls[RETURN_PLACE] = LocalDecl { mutability: Mutability::Mut, ty: tcx.mk_nil(), + user_ty: None, name: None, source_info, visibility_scope: source_info.scope, @@ -672,6 +674,7 @@ fn create_generator_drop_shim<'a, 'tcx>( ty: gen_ty, mutbl: hir::Mutability::MutMutable, }), + user_ty: None, name: None, source_info, visibility_scope: source_info.scope, diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 1e05b07030ef..19fb35be9d4e 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -238,8 +238,8 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx simplify_branches::SimplifyBranches::new("initial"), remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("early-opt"), - // Remove all `UserAssertTy` statements. - cleanup_post_borrowck::CleanUserAssertTy, + // Remove all `AscribeUserType` statements. + cleanup_post_borrowck::CleanAscribeUserType, // These next passes must be executed together add_call_guards::CriticalCallEdges, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 81fc235c2334..a2175dce33a8 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1098,7 +1098,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { StatementKind::InlineAsm {..} | StatementKind::EndRegion(_) | StatementKind::Validate(..) | - StatementKind::UserAssertTy(..) | + StatementKind::AscribeUserType(..) | StatementKind::Nop => {} } }); diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index eacc23c9c4fe..f7e44dde186f 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -230,7 +230,7 @@ fn check_statement( | StatementKind::StorageDead(_) | StatementKind::Validate(..) | StatementKind::EndRegion(_) - | StatementKind::UserAssertTy(..) + | StatementKind::AscribeUserType(..) | StatementKind::Nop => Ok(()), } } diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index 04a7a81eb126..a2561d3d7938 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -53,7 +53,7 @@ impl RemoveNoopLandingPads { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::EndRegion(_) | - StatementKind::UserAssertTy(..) | + StatementKind::AscribeUserType(..) | StatementKind::Nop => { // These are all nops in a landing pad (there's some // borrowck interaction between EndRegion and storage diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 9faaeea3f5b7..f3e0f5573632 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -163,7 +163,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir::StatementKind::InlineAsm { .. } | mir::StatementKind::EndRegion(_) | mir::StatementKind::Validate(..) | - mir::StatementKind::UserAssertTy(..) | + mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Nop => continue, mir::StatementKind::SetDiscriminant{ .. } => span_bug!(stmt.source_info.span, diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 9f5b5040b095..710ccb2053b8 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -17,6 +17,7 @@ use rustc::ty::item_path; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; use std::fmt::Display; +use std::fmt::Write as _; use std::fs; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -493,14 +494,18 @@ fn write_scope_tree( }; let indent = indent + INDENT.len(); - let indented_var = format!( - "{0:1$}let {2}{3:?}: {4:?};", + let mut indented_var = format!( + "{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, var.ty ); + if let Some(user_ty) = var.user_ty { + write!(indented_var, " as {:?}", user_ty).unwrap(); + } + indented_var.push_str(";"); writeln!( w, "{0:1$} // \"{2}\" in {3}", diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 3206fa6e1726..7c9f77042cb2 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -92,7 +92,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { StatementKind::StorageLive(..) => "StatementKind::StorageLive", StatementKind::StorageDead(..) => "StatementKind::StorageDead", StatementKind::InlineAsm { .. } => "StatementKind::InlineAsm", - StatementKind::UserAssertTy(..) => "StatementKind::UserAssertTy", + StatementKind::AscribeUserType(..) => "StatementKind::AscribeUserType", StatementKind::Nop => "StatementKind::Nop", }, &statement.kind); self.super_statement(block, statement, location); diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 957cb336d9e6..097cd6653753 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -49,7 +49,7 @@ pub fn add_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,TypeckTables,MirValidated")] + except="HirBody,TypeckTables,MirValidated,MirOptimized")] #[rustc_clean(cfg="cfail3")] pub fn add_type() { let _x: u32 = 2u32; diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index 54b7a3821caa..f04d43289296 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -19,9 +19,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// check that codegen of assignment expressions is sane. Assignments -// tend to be absent in simple code, so subtle breakage in them can -// leave a quite hard-to-find trail of destruction. +// Check codegen for assignments (`a = b`) where the left-hand-side is +// not yet initialized. Assignments tend to be absent in simple code, +// so subtle breakage in them can leave a quite hard-to-find trail of +// destruction. // ignore-tidy-linelength @@ -29,60 +30,42 @@ fn main() { let nodrop_x = false; let nodrop_y; + // Since boolean does not require drop, this can be a simple + // assignment: nodrop_y = nodrop_x; let drop_x : Option> = None; let drop_y; + // Since the type of `drop_y` has drop, we generate a `replace` + // terminator: drop_y = drop_x; } // END RUST SOURCE // START rustc.main.SimplifyCfg-initial.after.mir -// bb0: { -// StorageLive(_1); -// _1 = const false; -// StorageLive(_2); -// StorageLive(_3); -// _3 = _1; -// _2 = move _3; -// StorageDead(_3); -// StorageLive(_4); -// UserAssertTy(Canonical { variables: [], value: std::option::Option> }, _4); -// _4 = std::option::Option>::None; -// StorageLive(_5); -// StorageLive(_6); -// _6 = move _4; -// replace(_5 <-move _6) -> [return: bb2, unwind: bb5]; -// } -// bb1: { -// resume; -// } -// bb2: { -// drop(_6) -> [return: bb6, unwind: bb4]; -// } -// bb3: { -// drop(_4) -> bb1; -// } -// bb4: { -// drop(_5) -> bb3; -// } -// bb5: { -// drop(_6) -> bb4; -// } -// bb6: { -// StorageDead(_6); -// _0 = (); -// drop(_5) -> [return: bb7, unwind: bb3]; -// } -// bb7: { -// StorageDead(_5); -// drop(_4) -> [return: bb8, unwind: bb1]; -// } -// bb8: { -// StorageDead(_4); -// StorageDead(_2); -// StorageDead(_1); -// return; -// } +// bb0: { +// StorageLive(_1); +// _1 = const false; +// StorageLive(_2); +// StorageLive(_3); +// _3 = _1; +// _2 = move _3; +// StorageDead(_3); +// StorageLive(_4); +// _4 = std::option::Option>::None; +// AscribeUserType(_4, o, Canonical { variables: [], value: std::option::Option> }); +// StorageLive(_5); +// StorageLive(_6); +// _6 = move _4; +// replace(_5 <- move _6) -> [return: bb2, unwind: bb5]; +// } +// ... +// bb2: { +// drop(_6) -> [return: bb6, unwind: bb4]; +// } +// ... +// bb5: { +// drop(_6) -> bb4; +// } // END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs deleted file mode 100644 index 8a7ea8962fc5..000000000000 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Basic test for reborrow constraints: the region (`R5`) that appears -// in the type of `r_a` must outlive the region (`R7`) that appears in -// the type of `r_b` - -// compile-flags:-Zborrowck=mir -Zverbose -// ^^^^^^^^^ force compiler to dump more region information - -#![allow(warnings)] - -fn use_x(_: &mut i32) -> bool { true } - -fn main() { - let mut foo: i32 = 22; - let r_a: &mut i32 = &mut foo; - let r_b: &mut i32 = &mut *r_a; - use_x(r_b); -} - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// | '_#7r | U0 | {bb0[4], bb0[8..=17]} -// ... -// | '_#9r | U0 | {bb0[10], bb0[14..=17]} -// ... -// let _4: &'_#9r mut i32; -// ... -// let _2: &'_#7r mut i32; -// END rustc.main.nll.0.mir diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr deleted file mode 100644 index ed3e38486baf..000000000000 --- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: `foo` is not yet stable as a const fn - --> $DIR/dont_promote_unstable_const_fn.rs:25:25 - | -LL | const fn bar() -> u32 { foo() } //~ ERROR `foo` is not yet stable as a const fn - | ^^^^^ - | - = help: in Nightly builds, add `#![feature(foo)]` to the crate attributes to enable - -error[E0597]: borrowed value does not live long enough - --> $DIR/dont_promote_unstable_const_fn.rs:33:26 - | -LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough -LL | //~^ does not live long enough -LL | } - | - temporary value only lives until here - | - = note: borrowed value must be valid for the static lifetime... - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.nll.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.nll.stderr deleted file mode 100644 index 4c8f2f47d1e8..000000000000 --- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn_cross_crate.nll.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0597]: borrowed value does not live long enough - --> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:19:29 - | -LL | let _x: &'static u32 = &foo(); //~ ERROR does not live long enough - | ^^^^^ temporary value does not live long enough -LL | } - | - temporary value only lives until here - | - = note: borrowed value must be valid for the static lifetime... - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs index 84c305f5907d..dd1c2a447300 100644 --- a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs @@ -20,8 +20,17 @@ fn make_it() -> for<'a> fn(&'a u32, &'a u32) -> &'a u32 { panic!() } -fn main() { +fn foo() { let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); //~^ ERROR higher-ranked subtype error drop(a); } + +fn bar() { + // The code path for patterns is mildly different, so go ahead and + // test that too: + let _: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + //~^ ERROR higher-ranked subtype error +} + +fn main() { } diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr index e08d848b4714..c9195395b878 100644 --- a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr @@ -4,5 +4,11 @@ error: higher-ranked subtype error LL | let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); | ^^^^^^^^^ -error: aborting due to previous error +error: higher-ranked subtype error + --> $DIR/hr-fn-aaa-as-aba.rs:32:58 + | +LL | let _: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + | ^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs index 9b8268d9736a..bc7b031f72c0 100644 --- a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs +++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs @@ -16,9 +16,7 @@ // another -- effectively, the single lifetime `'a` is just inferred // to be the intersection of the two distinct lifetimes. // -// FIXME: However, we currently reject this example with an error, -// because of how we handle binders and equality in `relate_tys`. -// +// compile-pass // compile-flags:-Zno-leak-check #![feature(nll)] @@ -31,7 +29,6 @@ fn make_cell_aa() -> Cell fn(&'a u32, &'a u32)> { fn aa_eq_ab() { let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); - //~^ ERROR higher-ranked subtype error drop(a); } diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr deleted file mode 100644 index 17e8a32cb2ad..000000000000 --- a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: higher-ranked subtype error - --> $DIR/hr-fn-aau-eq-abu.rs:33:53 - | -LL | let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); - | ^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/nll/relate_tys/universe-violation.rs b/src/test/ui/nll/relate_tys/universe-violation.rs new file mode 100644 index 000000000000..cc86c8d02d3a --- /dev/null +++ b/src/test/ui/nll/relate_tys/universe-violation.rs @@ -0,0 +1,17 @@ +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning either argument CANNOT be upcast to one +// that returns always its first argument. +// +// compile-flags:-Zno-leak-check + +#![feature(nll)] + +fn make_it() -> fn(&'static u32) -> &'static u32 { + panic!() +} + +fn main() { + let a: fn(_) -> _ = make_it(); + let b: fn(&u32) -> &u32 = a; + drop(a); +} diff --git a/src/test/ui/nll/relate_tys/universe-violation.stderr b/src/test/ui/nll/relate_tys/universe-violation.stderr new file mode 100644 index 000000000000..6dc78789564c --- /dev/null +++ b/src/test/ui/nll/relate_tys/universe-violation.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/universe-violation.rs:15:31 + | +LL | let b: fn(&u32) -> &u32 = a; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/relate_tys/var-appears-twice.rs b/src/test/ui/nll/relate_tys/var-appears-twice.rs new file mode 100644 index 000000000000..02b3006c5315 --- /dev/null +++ b/src/test/ui/nll/relate_tys/var-appears-twice.rs @@ -0,0 +1,38 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning always its first argument can be upcast to one +// that returns either first or second argument. + +#![feature(nll)] +#![allow(warnings)] + +use std::cell::Cell; + +type DoubleCell = Cell<(A, A)>; +type DoublePair = (A, A); + +fn make_cell<'b>(x: &'b u32) -> Cell<(&'static u32, &'b u32)> { + panic!() +} + +fn main() { + let a: &'static u32 = &22; + let b = 44; + + // Here we get an error because `DoubleCell<_>` requires the same type + // on both parts of the `Cell`, and we can't have that. + let x: DoubleCell<_> = make_cell(&b); //~ ERROR + + // Here we do not get an error because `DoublePair<_>` permits + // variance on the lifetimes involved. + let y: DoublePair<_> = make_cell(&b).get(); +} diff --git a/src/test/ui/nll/relate_tys/var-appears-twice.stderr b/src/test/ui/nll/relate_tys/var-appears-twice.stderr new file mode 100644 index 000000000000..15c4cc2e100b --- /dev/null +++ b/src/test/ui/nll/relate_tys/var-appears-twice.stderr @@ -0,0 +1,14 @@ +error[E0597]: `b` does not live long enough + --> $DIR/var-appears-twice.rs:33:38 + | +LL | let x: DoubleCell<_> = make_cell(&b); //~ ERROR + | ^^ borrowed value does not live long enough +... +LL | } + | - `b` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/patterns.rs b/src/test/ui/nll/user-annotations/patterns.rs new file mode 100644 index 000000000000..53d97360c869 --- /dev/null +++ b/src/test/ui/nll/user-annotations/patterns.rs @@ -0,0 +1,121 @@ +// Test that various patterns also enforce types. + +#![feature(nll)] + +fn variable_no_initializer() { + let x = 22; + let y: &'static u32; + y = &x; //~ ERROR +} + +fn tuple_no_initializer() { + // FIXME(#47187): We are not propagating ascribed type through tuples. + + let x = 22; + let (y, z): (&'static u32, &'static u32); + y = &x; +} + +fn ref_with_ascribed_static_type() -> u32 { + // Check the behavior in some wacky cases. + let x = 22; + let y = &x; //~ ERROR + let ref z: &'static u32 = y; + **z +} + +fn ref_with_ascribed_any_type() -> u32 { + let x = 22; + let y = &x; + let ref z: &u32 = y; + **z +} + +struct Single { value: T } + +fn struct_no_initializer() { + // FIXME(#47187): We are not propagating ascribed type through patterns. + + let x = 22; + let Single { value: y }: Single<&'static u32>; + y = &x; +} + +fn variable_with_initializer() { + let x = 22; + let y: &'static u32 = &x; //~ ERROR +} + +fn underscore_with_initializer() { + let x = 22; + let _: &'static u32 = &x; //~ ERROR + + let _: Vec<&'static String> = vec![&String::new()]; + //~^ ERROR borrowed value does not live long enough [E0597] + + let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44); + //~^ ERROR borrowed value does not live long enough [E0597] + + let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44); + //~^ ERROR borrowed value does not live long enough [E0597] +} + +fn pair_underscores_with_initializer() { + let x = 22; + let (_, _): (&'static u32, u32) = (&x, 44); //~ ERROR +} + +fn pair_variable_with_initializer() { + let x = 22; + let (y, _): (&'static u32, u32) = (&x, 44); //~ ERROR +} + +fn struct_single_field_variable_with_initializer() { + let x = 22; + let Single { value: y }: Single<&'static u32> = Single { value: &x }; //~ ERROR +} + +fn struct_single_field_underscore_with_initializer() { + let x = 22; + let Single { value: _ }: Single<&'static u32> = Single { value: &x }; //~ ERROR +} + +struct Double { value1: T, value2: T } + +fn struct_double_field_underscore_with_initializer() { + let x = 22; + let Double { value1: _, value2: _ }: Double<&'static u32> = Double { + value1: &x, //~ ERROR + value2: &44, + }; +} + +fn static_to_a_to_static_through_variable<'a>(x: &'a u32) -> &'static u32 { + // The error in this test is inconsistency with + // `static_to_a_to_static_through_tuple`, but "feels right" to + // me. It occurs because we special case the single binding case + // and force the type of `y` to be `&'a u32`, even though the + // right-hand side has type `&'static u32`. + + let y: &'a u32 = &22; + y //~ ERROR +} + +fn static_to_a_to_static_through_tuple<'a>(x: &'a u32) -> &'static u32 { + // FIXME(#47187): The fact that this type-checks is perhaps surprising. + // What happens is that the right-hand side is constrained to have + // type `&'a u32`, which is possible, because it has type + // `&'static u32`. The variable `y` is then forced to have type + // `&'static u32`, but it is constrained only by the right-hand + // side, not the ascribed type, and hence it passes. + + let (y, _z): (&'a u32, u32) = (&22, 44); + y +} + +fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 { + let (y, _z): (&'static u32, u32) = (x, 44); //~ ERROR + y +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/patterns.stderr b/src/test/ui/nll/user-annotations/patterns.stderr new file mode 100644 index 000000000000..563de1a9e02e --- /dev/null +++ b/src/test/ui/nll/user-annotations/patterns.stderr @@ -0,0 +1,143 @@ +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:8:9 + | +LL | y = &x; //~ ERROR + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:22:13 + | +LL | let y = &x; //~ ERROR + | ^^ borrowed value does not live long enough +... +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:46:27 + | +LL | let y: &'static u32 = &x; //~ ERROR + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:51:27 + | +LL | let _: &'static u32 = &x; //~ ERROR + | ^^ borrowed value does not live long enough +... +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: borrowed value does not live long enough + --> $DIR/patterns.rs:53:41 + | +LL | let _: Vec<&'static String> = vec![&String::new()]; + | ^^^^^^^^^^^^^ - temporary value only lives until here + | | + | temporary value does not live long enough + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: borrowed value does not live long enough + --> $DIR/patterns.rs:56:52 + | +LL | let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44); + | ^^^^^^^^^^^^^ - temporary value only lives until here + | | + | temporary value does not live long enough + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: borrowed value does not live long enough + --> $DIR/patterns.rs:59:53 + | +LL | let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44); + | ^^^^^^^^^^^^^ - temporary value only lives until here + | | + | temporary value does not live long enough + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:65:40 + | +LL | let (_, _): (&'static u32, u32) = (&x, 44); //~ ERROR + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:70:40 + | +LL | let (y, _): (&'static u32, u32) = (&x, 44); //~ ERROR + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:75:69 + | +LL | let Single { value: y }: Single<&'static u32> = Single { value: &x }; //~ ERROR + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:80:69 + | +LL | let Single { value: _ }: Single<&'static u32> = Single { value: &x }; //~ ERROR + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:88:17 + | +LL | value1: &x, //~ ERROR + | ^^ borrowed value does not live long enough +... +LL | } + | - `x` dropped here while still borrowed + | + = note: borrowed value must be valid for the static lifetime... + +error: unsatisfied lifetime constraints + --> $DIR/patterns.rs:101:5 + | +LL | fn static_to_a_to_static_through_variable<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | y //~ ERROR + | ^ returning this value requires that `'a` must outlive `'static` + +error: unsatisfied lifetime constraints + --> $DIR/patterns.rs:117:40 + | +LL | fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let (y, _z): (&'static u32, u32) = (x, 44); //~ ERROR + | ^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/try-block/try-block-bad-lifetime.stderr b/src/test/ui/try-block/try-block-bad-lifetime.stderr index 36c89faf5a2c..ebf56dc9835f 100644 --- a/src/test/ui/try-block/try-block-bad-lifetime.stderr +++ b/src/test/ui/try-block/try-block-bad-lifetime.stderr @@ -1,13 +1,14 @@ error[E0597]: `my_string` does not live long enough --> $DIR/try-block-bad-lifetime.rs:25:33 | +LL | let result: Result<(), &str> = try { + | ------ borrow later used here +LL | let my_string = String::from(""); LL | let my_str: & str = & my_string; | ^^^^^^^^^^^ borrowed value does not live long enough ... LL | }; | - `my_string` dropped here while still borrowed -LL | do_something_with(result); - | ------ borrow later used here error[E0506]: cannot assign to `i` because it is borrowed --> $DIR/try-block-bad-lifetime.rs:39:13