diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 1e6dadae36371..c73f171806e42 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -277,6 +277,10 @@ for mir::StatementKind<'gcx> { op.hash_stable(hcx, hasher); places.hash_stable(hcx, hasher); } + mir::StatementKind::UserAssertTy(ref c_ty, ref local) => { + c_ty.hash_stable(hcx, hasher); + local.hash_stable(hcx, hasher); + } mir::StatementKind::Nop => {} mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { asm.hash_stable(hcx, hasher); diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs index 22526c7751d50..debddd708ea22 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc/infer/canonical.rs @@ -33,6 +33,7 @@ use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::Idx; +use serialize::UseSpecializedDecodable; use std::fmt::Debug; use std::ops::Index; use syntax::codemap::Span; @@ -49,7 +50,7 @@ use rustc_data_structures::fx::FxHashMap; /// A "canonicalized" type `V` is one where all free inference /// variables have been rewriten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct Canonical<'gcx, V> { pub variables: CanonicalVarInfos<'gcx>, pub value: V, @@ -57,6 +58,8 @@ pub struct Canonical<'gcx, V> { pub type CanonicalVarInfos<'gcx> = &'gcx Slice; +impl<'gcx> UseSpecializedDecodable for CanonicalVarInfos<'gcx> { } + /// A set of values corresponding to the canonical variables from some /// `Canonical`. You can give these values to /// `canonical_value.substitute` to substitute them into the canonical @@ -69,7 +72,7 @@ pub type CanonicalVarInfos<'gcx> = &'gcx Slice; /// You can also use `infcx.fresh_inference_vars_for_canonical_vars` /// to get back a `CanonicalVarValues` containing fresh inference /// variables. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct CanonicalVarValues<'tcx> { pub var_values: IndexVec>, } @@ -78,7 +81,7 @@ pub struct CanonicalVarValues<'tcx> { /// canonical value. This is sufficient information for code to create /// a copy of the canonical value in some other inference context, /// with fresh inference variables replacing the canonical values. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub struct CanonicalVarInfo { pub kind: CanonicalVarKind, } @@ -86,7 +89,7 @@ pub struct CanonicalVarInfo { /// Describes the "kind" of the canonical variable. This is a "kind" /// in the type-theory sense of the term -- i.e., a "meta" type system /// that analyzes type-like values. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub enum CanonicalVarKind { /// Some kind of type inference variable. Ty(CanonicalTyVarKind), @@ -100,7 +103,7 @@ pub enum CanonicalVarKind { /// 22.) can only be instantiated with integral/float types (e.g., /// usize or f32). In order to faithfully reproduce a type, we need to /// know what set of types a given type variable can be unified with. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] pub enum CanonicalTyVarKind { /// General type variable `?T` that can be unified with arbitrary types. General, @@ -855,11 +858,14 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g } CloneTypeFoldableAndLiftImpls! { + ::infer::canonical::Certainty, + ::infer::canonical::CanonicalVarInfo, + ::infer::canonical::CanonicalVarKind, +} + +CloneTypeFoldableImpls! { for <'tcx> { - ::infer::canonical::Certainty, - ::infer::canonical::CanonicalVarInfo, ::infer::canonical::CanonicalVarInfos<'tcx>, - ::infer::canonical::CanonicalVarKind, } } @@ -870,6 +876,13 @@ BraceStructTypeFoldableImpl! { } where C: TypeFoldable<'tcx> } +BraceStructLiftImpl! { + impl<'a, 'tcx, T> Lift<'tcx> for Canonical<'a, T> { + type Lifted = Canonical<'tcx, T::Lifted>; + variables, value + } where T: Lift<'tcx> +} + impl<'tcx> CanonicalVarValues<'tcx> { fn iter<'a>(&'a self) -> impl Iterator> + 'a { self.var_values.iter().cloned() diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 64a1729982a31..9ed4e6a8e00ae 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -27,7 +27,7 @@ use hir::def_id::DefId; use mir::visit::MirVisitable; use mir::interpret::{Value, PrimVal}; use ty::subst::{Subst, Substs}; -use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; +use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::TypeAndMut; use util::ppaux; @@ -1253,6 +1253,23 @@ 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. + /// + /// `CanonicalTy` is used to capture "inference variables" from the user's types. For example: + /// + /// let x: Vec<_> = ...; + /// let y: &u32 = ...; + /// + /// would result in `Vec` and `&'?0 u32` respectively (where `?0` is a canonicalized + /// variable). + UserAssertTy(CanonicalTy<'tcx>, Local), + /// No-op. Useful for deleting instructions without affecting statement indices. Nop, } @@ -1324,6 +1341,8 @@ impl<'tcx> Debug for Statement<'tcx> { InlineAsm { ref asm, ref outputs, ref inputs } => { write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs) }, + UserAssertTy(ref c_ty, ref local) => write!(fmt, "UserAssertTy({:?}, {:?})", + c_ty, local), Nop => write!(fmt, "nop"), } } @@ -2184,6 +2203,7 @@ EnumTypeFoldableImpl! { (StatementKind::InlineAsm) { asm, outputs, inputs }, (StatementKind::Validate)(a, b), (StatementKind::EndRegion)(a), + (StatementKind::UserAssertTy)(a, b), (StatementKind::Nop), } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 650af8dc4d903..a3fdb6f73abb0 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -10,7 +10,7 @@ use hir::def_id::DefId; use ty::subst::Substs; -use ty::{ClosureSubsts, Region, Ty, GeneratorInterior}; +use ty::{CanonicalTy, ClosureSubsts, Region, Ty, GeneratorInterior}; use mir::*; use syntax_pos::Span; @@ -144,6 +144,13 @@ 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_place(&mut self, place: & $($mutability)* Place<'tcx>, context: PlaceContext<'tcx>, @@ -376,6 +383,10 @@ 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::Nop => {} } } @@ -619,6 +630,13 @@ macro_rules! make_mir_visitor { } } + fn super_user_assert_ty(&mut self, + _c_ty: & $($mutability)* CanonicalTy<'tcx>, + local: & $($mutability)* Local, + location: Location) { + self.visit_local(local, PlaceContext::Validate, location); + } + fn super_place(&mut self, place: & $($mutability)* Place<'tcx>, context: PlaceContext<'tcx>, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 0d91074e946bd..c47a351e9d39d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1251,6 +1251,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "choose which RELRO level to use"), nll: bool = (false, parse_bool, [UNTRACKED], "run the non-lexical lifetimes MIR pass"), + disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED], + "disable user provided type assertion in NLL"), trans_time_graph: bool = (false, parse_bool, [UNTRACKED], "generate a graphical HTML report of time spent in trans and LLVM"), thinlto: Option = (None, parse_opt_bool, [TRACKED], diff --git a/src/librustc/ty/codec.rs b/src/librustc/ty/codec.rs index f98bc95356098..4e15f0711a5aa 100644 --- a/src/librustc/ty/codec.rs +++ b/src/librustc/ty/codec.rs @@ -17,6 +17,7 @@ // persisting to incr. comp. caches. use hir::def_id::{DefId, CrateNum}; +use infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decodable, Decoder, Encoder, Encodable, opaque}; use std::hash::Hash; @@ -239,6 +240,19 @@ pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D) .mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) } +#[inline] +pub fn decode_canonical_var_infos<'a, 'tcx, D>(decoder: &mut D) + -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + let interned: Result, _> = (0..len).map(|_| Decodable::decode(decoder)) + .collect(); + Ok(decoder.tcx() + .intern_canonical_var_infos(interned?.as_slice())) +} + #[inline] pub fn decode_const<'a, 'tcx, D>(decoder: &mut D) -> Result<&'tcx ty::Const<'tcx>, D::Error> @@ -262,6 +276,7 @@ macro_rules! implement_ty_decoder { ($DecoderName:ident <$($typaram:tt),*>) => { mod __ty_decoder_impl { use super::$DecoderName; + use $crate::infer::canonical::CanonicalVarInfos; use $crate::ty; use $crate::ty::codec::*; use $crate::ty::subst::Substs; @@ -364,6 +379,14 @@ macro_rules! implement_ty_decoder { } } + impl<$($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) + -> Result, Self::Error> { + decode_canonical_var_infos(self) + } + } + impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::ty::Const<'tcx>> for $DecoderName<$($typaram),*> { fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index fd3465f59ebf2..55e695bb18c83 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -48,6 +48,7 @@ use ty::layout::{LayoutDetails, TargetDataLayout}; use ty::maps; use ty::steal::Steal; use ty::BindingMode; +use ty::CanonicalTy; use util::nodemap::{NodeMap, DefIdSet, ItemLocalMap}; use util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::accumulate_vec::AccumulateVec; @@ -344,6 +345,10 @@ pub struct TypeckTables<'tcx> { /// method calls, including those of overloaded operators. type_dependent_defs: ItemLocalMap, + /// Stores the canonicalized types provided by the user. See also `UserAssertTy` statement in + /// MIR. + user_provided_tys: ItemLocalMap>, + /// Stores the types for various nodes in the AST. Note that this table /// is not guaranteed to be populated until after typeck. See /// typeck::check::fn_ctxt for details. @@ -420,6 +425,7 @@ impl<'tcx> TypeckTables<'tcx> { TypeckTables { local_id_root, type_dependent_defs: ItemLocalMap(), + user_provided_tys: ItemLocalMap(), node_types: ItemLocalMap(), node_substs: ItemLocalMap(), adjustments: ItemLocalMap(), @@ -461,6 +467,20 @@ impl<'tcx> TypeckTables<'tcx> { } } + pub fn user_provided_tys(&self) -> LocalTableInContext> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.user_provided_tys + } + } + + pub fn user_provided_tys_mut(&mut self) -> LocalTableInContextMut> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.user_provided_tys + } + } + pub fn node_types(&self) -> LocalTableInContext> { LocalTableInContext { local_id_root: self.local_id_root, @@ -685,6 +705,7 @@ impl<'a, 'gcx> HashStable> for TypeckTables<'gcx> { let ty::TypeckTables { local_id_root, ref type_dependent_defs, + ref user_provided_tys, ref node_types, ref node_substs, ref adjustments, @@ -704,6 +725,7 @@ impl<'a, 'gcx> HashStable> for TypeckTables<'gcx> { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { type_dependent_defs.hash_stable(hcx, hasher); + user_provided_tys.hash_stable(hcx, hasher); node_types.hash_stable(hcx, hasher); node_substs.hash_stable(hcx, hasher); adjustments.hash_stable(hcx, hasher); @@ -1635,6 +1657,24 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice> { } } +impl<'a, 'tcx> Lift<'tcx> for &'a Slice { + type Lifted = &'tcx Slice; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { + if self.len() == 0 { + return Some(Slice::empty()); + } + if tcx.interners.arena.in_arena(*self as *const _) { + return Some(unsafe { mem::transmute(*self) }); + } + // Also try in the global tcx if we're not that. + if !tcx.is_global() { + self.lift_to_tcx(tcx.global_tcx()) + } else { + None + } + } +} + pub mod tls { use super::{CtxtInterners, GlobalCtxt, TyCtxt}; diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 95c5cd377d71f..68bc244971138 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -21,6 +21,7 @@ use hir::map::DefPathData; use hir::svh::Svh; use ich::Fingerprint; use ich::StableHashingContext; +use infer::canonical::{Canonical, Canonicalize}; use middle::const_val::ConstVal; use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; use middle::privacy::AccessLevels; @@ -554,6 +555,17 @@ pub type Ty<'tcx> = &'tcx TyS<'tcx>; impl<'tcx> serialize::UseSpecializedEncodable for Ty<'tcx> {} impl<'tcx> serialize::UseSpecializedDecodable for Ty<'tcx> {} +pub type CanonicalTy<'gcx> = Canonical<'gcx, Ty<'gcx>>; + +impl <'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for Ty<'tcx> { + type Canonicalized = CanonicalTy<'gcx>; + + fn intern(_gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>) -> Self::Canonicalized { + value + } +} + /// A wrapper for slices with the additional invariant /// that the slice is interned and no other slice with /// the same contents can exist in the same context. diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 6e1a798910dc6..305df37466d02 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -392,11 +392,13 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // ignored when consuming results (update to // flow_state already handled). } - StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { - // `Nop`, `Validate`, and `StorageLive` are irrelevant + StatementKind::Nop | + StatementKind::UserAssertTy(..) | + StatementKind::Validate(..) | + StatementKind::StorageLive(..) => { + // `Nop`, `UserAssertTy`, `Validate`, and `StorageLive` are irrelevant // to borrow check. } - StatementKind::StorageDead(local) => { self.access_place( ContextKind::StorageDead.new(location), diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 3a39eb5c908de..afaedecdf0abe 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -12,10 +12,10 @@ use rustc::hir; use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Place::Projection; -use rustc::mir::{PlaceProjection, ProjectionElem}; +use rustc::mir::{Local, PlaceProjection, ProjectionElem}; use rustc::mir::visit::TyContext; use rustc::infer::InferCtxt; -use rustc::ty::{self, ClosureSubsts}; +use rustc::ty::{self, CanonicalTy, ClosureSubsts}; use rustc::ty::subst::Substs; use rustc::ty::fold::TypeFoldable; @@ -106,6 +106,9 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx self.super_rvalue(rvalue, location); } + + fn visit_user_assert_ty(&mut self, _c_ty: &CanonicalTy<'tcx>, + _local: &Local, _location: Location) { } } impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index c54acda8a6252..04c206b5c0c40 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -9,8 +9,8 @@ // except according to those terms. use rustc::ty::subst::Substs; -use rustc::ty::{self, ClosureSubsts, GeneratorInterior, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind}; +use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorInterior, Ty, TypeFoldable}; +use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; @@ -118,6 +118,14 @@ 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) { + // 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. + debug!("visit_user_assert_ty: skipping renumber"); + } + fn visit_statement( &mut self, block: BasicBlock, 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 022831b5a9259..80a439b183058 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -761,6 +761,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); }; } + StatementKind::UserAssertTy(ref c_ty, ref local) => { + let local_ty = mir.local_decls()[*local].ty; + let (ty, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars( + stmt.source_info.span, c_ty); + debug!("check_stmt: user_assert_ty ty={:?} local_ty={:?}", ty, local_ty); + if let Err(terr) = self.eq_types(ty, local_ty, location.at_self()) { + span_mirbug!( + self, + stmt, + "bad type assert ({:?} = {:?}): {:?}", + ty, + local_ty, + terr + ); + } + } StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::InlineAsm { .. } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index afd338581392d..39dc29ba18b64 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -777,6 +777,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + debug!( + "replace_bound_regions_with_nll_infer_vars(value={:?}, all_outlive_scope={:?})", + value, + all_outlive_scope, + ); let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| { let liberated_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion { scope: all_outlive_scope, @@ -784,6 +789,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> { })); let region_vid = self.next_nll_region_var(origin); indices.insert_late_bound_region(liberated_region, region_vid.to_region_vid()); + debug!("liberated_region={:?} => {:?}", liberated_region, region_vid); region_vid }); value diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 7281fb5966388..94702927d2600 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -102,6 +102,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { remainder_scope, init_scope, pattern, + ty, initializer, lint_level } => { @@ -120,10 +121,19 @@ 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, pattern, init) + this.expr_into_pattern(block, ty, pattern, init) }) })); } else { + // 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.storage_live_binding(block, node, span); this.schedule_drop_for_binding(node, span); diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 229e33dcd7862..7eb52a3cdee93 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -145,8 +145,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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_indices[&var]; + 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<()> { @@ -156,6 +174,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { var, subpattern: None, .. } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span); + + if let Some(ty) = ty { + self.user_assert_ty(block, ty, var, irrefutable_pat.span); + } + unpack!(block = self.into(&place, block, initializer)); self.schedule_drop_for_binding(var, irrefutable_pat.span); block.unit() diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 499ed55fad41f..fb3042014df6a 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -678,6 +678,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::Nop => {} } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index d6f419f6cfb41..cbf4c822769c6 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -298,6 +298,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } StatementKind::EndRegion(_) | StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => {} } } diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 6f6258f52f794..14aa307f0ae1f 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -76,12 +76,14 @@ 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); 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 5f60a134fb130..fe82b8158f76d 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -93,10 +93,13 @@ pub enum StmtKind<'tcx> { /// lifetime of temporaries init_scope: region::Scope, - /// let = ... + /// let : ty = ... pattern: Pattern<'tcx>, - /// let pat = ... + /// let pat: = init ... + ty: Option, + + /// let pat: ty = ... initializer: Option>, /// the lint level for this let-statement diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 4e1750caf26ba..f1d58ff5e884e 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -89,6 +89,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { M::end_region(self, Some(ce))?; } + UserAssertTy(..) => {} + // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 86d08dec2b9c3..02c703996643a 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -105,6 +105,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { StatementKind::StorageDead(..) | StatementKind::EndRegion(..) | StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => { // safe (at least as emitted during MIR construction) } diff --git a/src/librustc_mir/transform/clean_end_regions.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs similarity index 65% rename from src/librustc_mir/transform/clean_end_regions.rs rename to src/librustc_mir/transform/cleanup_post_borrowck.rs index 6e8985d99d287..256b1fd66e9a7 100644 --- a/src/librustc_mir/transform/clean_end_regions.rs +++ b/src/librustc_mir/transform/cleanup_post_borrowck.rs @@ -8,16 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! This module provides one pass, `CleanEndRegions`, that reduces the -//! set of `EndRegion` statements in the MIR. +//! This module provides two passes: //! -//! The "pass" is actually implemented as two traversals (aka visits) -//! of the input MIR. The first traversal, `GatherBorrowedRegions`, -//! finds all of the regions in the MIR that are involved in a borrow. +//! - `CleanEndRegions`, that reduces the set of `EndRegion` statements +//! in the MIR. +//! - `CleanUserAssertTy`, that replaces all `UserAssertTy` statements +//! with `Nop`. +//! +//! The `CleanEndRegions` "pass" is actually implemented as two +//! traversals (aka visits) of the input MIR. The first traversal, +//! `GatherBorrowedRegions`, finds all of the regions in the MIR +//! that are involved in a borrow. //! //! The second traversal, `DeleteTrivialEndRegions`, walks over the //! 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` +//! pass runs after the MIR borrowck so that the NLL type checker can +//! perform the type assertion when it encounters the `UserAssertTy` +//! statements. use rustc_data_structures::fx::FxHashSet; @@ -93,7 +104,33 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> { } if delete_it { - statement.kind = StatementKind::Nop; + statement.make_nop(); + } + self.super_statement(block, statement, location); + } +} + +pub struct CleanUserAssertTy; + +pub struct DeleteUserAssertTy; + +impl MirPass for CleanUserAssertTy { + fn run_pass<'a, 'tcx>(&self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _source: MirSource, + mir: &mut Mir<'tcx>) { + let mut delete = DeleteUserAssertTy; + delete.visit_mir(mir); + } +} + +impl<'tcx> MutVisitor<'tcx> for DeleteUserAssertTy { + fn visit_statement(&mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location) { + if let StatementKind::UserAssertTy(..) = statement.kind { + statement.make_nop(); } self.super_statement(block, statement, location); } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 81b740c917b5e..63ca35aa0e7b2 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -25,7 +25,7 @@ use syntax_pos::Span; pub mod add_validation; pub mod add_moves_for_packed_drops; -pub mod clean_end_regions; +pub mod cleanup_post_borrowck; pub mod check_unsafety; pub mod simplify_branches; pub mod simplify; @@ -193,7 +193,7 @@ fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Stea let mut mir = tcx.mir_built(def_id).steal(); run_passes![tcx, mir, def_id, 0; // Remove all `EndRegion` statements that are not involved in borrows. - clean_end_regions::CleanEndRegions, + cleanup_post_borrowck::CleanEndRegions, // What we need to do constant evaluation. simplify::SimplifyCfg::new("initial"), @@ -234,6 +234,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, // 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 59a872a23b060..8e2f98d276966 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1099,6 +1099,7 @@ This does not pose a problem by itself because they can't be accessed directly." StatementKind::InlineAsm {..} | StatementKind::EndRegion(_) | StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | StatementKind::Nop => {} } }); diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index cd80d25c410f1..6d365012525f6 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -50,6 +50,7 @@ impl RemoveNoopLandingPads { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::EndRegion(_) | + StatementKind::UserAssertTy(..) | 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 76283edac7284..45e7a0d3f4c5a 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -163,6 +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::Nop => continue, mir::StatementKind::SetDiscriminant{ .. } => span_bug!(stmt.source_info.span, diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 4a4ce63cc1d4e..a4e056c6b589e 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -90,6 +90,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::Nop => "StatementKind::Nop", }, &statement.kind); self.super_statement(block, statement, location); diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index b5b7484940192..579b07929a2f2 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -84,6 +84,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { } mir::StatementKind::EndRegion(_) | mir::StatementKind::Validate(..) | + mir::StatementKind::UserAssertTy(..) | mir::StatementKind::Nop => bx, } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 69879bbe85d6e..10dab6afd004c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -960,10 +960,19 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> { // Add explicitly-declared locals. fn visit_local(&mut self, local: &'gcx hir::Local) { let o_ty = match local.ty { - Some(ref ty) => Some(self.fcx.to_ty(&ty)), - None => None + Some(ref ty) => { + let o_ty = self.fcx.to_ty(&ty); + + let (c_ty, _orig_values) = self.fcx.inh.infcx.canonicalize_response(&o_ty); + debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty); + self.fcx.tables.borrow_mut().user_provided_tys_mut().insert(ty.hir_id, c_ty); + + Some(o_ty) + }, + None => None, }; self.assign(local.span, local.id, o_ty); + debug!("Local variable {:?} is assigned type {}", local.pat, self.fcx.ty_to_string( diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 862b15743c701..bbd04e0b19ae1 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -46,6 +46,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { wbcx.visit_anon_types(); wbcx.visit_cast_types(); wbcx.visit_free_region_map(); + wbcx.visit_user_provided_tys(); let used_trait_imports = mem::replace( &mut self.tables.borrow_mut().used_trait_imports, @@ -341,6 +342,33 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { self.tables.free_region_map = free_region_map; } + fn visit_user_provided_tys(&mut self) { + let fcx_tables = self.fcx.tables.borrow(); + debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + let common_local_id_root = fcx_tables.local_id_root.unwrap(); + + for (&local_id, c_ty) in fcx_tables.user_provided_tys().iter() { + let hir_id = hir::HirId { + owner: common_local_id_root.index, + local_id, + }; + + let c_ty = if let Some(c_ty) = self.tcx().lift_to_global(c_ty) { + c_ty + } else { + span_bug!( + hir_id.to_span(&self.fcx.tcx), + "writeback: `{:?}` missing from the global type context", + c_ty + ); + }; + + self.tables + .user_provided_tys_mut() + .insert(hir_id, c_ty.clone()); + } + } + fn visit_anon_types(&mut self) { let gcx = self.tcx().global_tcx(); for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 851b13c705516..2f49500c3910b 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")] + except="HirBody,TypeckTables,MirValidated")] #[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 321c05c490356..3c236ddcf0409 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -23,6 +23,8 @@ // 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 + fn main() { let nodrop_x = false; let nodrop_y; @@ -46,6 +48,7 @@ fn main() { // _2 = move _3; // StorageDead(_3); // StorageLive(_4); +// UserAssertTy(Canonical { variables: Slice([]), value: std::option::Option> }, _4); // _4 = std::option::Option>::None; // StorageLive(_5); // StorageLive(_6); diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index f69c51c3562dc..92e42a73bbb6a 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,9 +28,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#7r | {bb0[6..=14]} +// | '_#7r | {bb0[4], bb0[8..=17]} // ... -// | '_#9r | {bb0[11..=14]} +// | '_#9r | {bb0[10], bb0[14..=17]} // ... // let _2: &'_#7r mut i32; // ... diff --git a/src/test/run-pass/generator/yield-subtype.rs b/src/test/run-pass/generator/yield-subtype.rs index 7e8a0d1e2b925..c41341690441f 100644 --- a/src/test/run-pass/generator/yield-subtype.rs +++ b/src/test/run-pass/generator/yield-subtype.rs @@ -9,6 +9,7 @@ // except according to those terms. // revisions:lexical nll +//[nll]compile-flags: -Z disable-nll-user-type-assert #![cfg_attr(nll, feature(nll))] #![feature(generators)] diff --git a/src/test/ui/issue-47184.rs b/src/test/ui/issue-47184.rs new file mode 100644 index 0000000000000..0831b7e0af8e2 --- /dev/null +++ b/src/test/ui/issue-47184.rs @@ -0,0 +1,16 @@ +// Copyright 2015 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. + +#![feature(nll)] + +fn main() { + let _vec: Vec<&'static String> = vec![&String::new()]; + //~^ ERROR borrowed value does not live long enough [E0597] +} diff --git a/src/test/ui/issue-47184.stderr b/src/test/ui/issue-47184.stderr new file mode 100644 index 0000000000000..a9eb33f01e3e0 --- /dev/null +++ b/src/test/ui/issue-47184.stderr @@ -0,0 +1,14 @@ +error[E0597]: borrowed value does not live long enough + --> $DIR/issue-47184.rs:14:44 + | +LL | let _vec: Vec<&'static String> = vec![&String::new()]; + | ^^^^^^^^^^^^^ temporary value does not live long enough +LL | //~^ ERROR borrowed value does not live long enough [E0597] +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`.