1
Fork 0

Auto merge of #48482 - davidtwco:issue-47184, r=nikomatsakis

NLL should identify and respect the lifetime annotations that the user wrote

Part of #47184.

r? @nikomatsakis
This commit is contained in:
bors 2018-03-24 02:08:22 +00:00
commit ab0ef145ac
36 changed files with 358 additions and 33 deletions

View file

@ -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);

View file

@ -33,6 +33,7 @@
use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin};
use rustc_data_structures::indexed_vec::Idx;
use serialize::UseSpecializedDecodable;
use std::fmt::Debug;
use std::ops::Index;
use syntax::codemap::Span;
@ -49,7 +50,7 @@ use rustc_data_structures::fx::FxHashMap;
/// A "canonicalized" type `V` is one where all free inference
/// variables have been rewriten to "canonical vars". These are
/// numbered starting from 0 in order of first appearance.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
pub struct Canonical<'gcx, V> {
pub variables: CanonicalVarInfos<'gcx>,
pub value: V,
@ -57,6 +58,8 @@ pub struct Canonical<'gcx, V> {
pub type CanonicalVarInfos<'gcx> = &'gcx Slice<CanonicalVarInfo>;
impl<'gcx> UseSpecializedDecodable for CanonicalVarInfos<'gcx> { }
/// A set of values corresponding to the canonical variables from some
/// `Canonical`. You can give these values to
/// `canonical_value.substitute` to substitute them into the canonical
@ -69,7 +72,7 @@ pub type CanonicalVarInfos<'gcx> = &'gcx Slice<CanonicalVarInfo>;
/// You can also use `infcx.fresh_inference_vars_for_canonical_vars`
/// to get back a `CanonicalVarValues` containing fresh inference
/// variables.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
pub struct CanonicalVarValues<'tcx> {
pub var_values: IndexVec<CanonicalVar, Kind<'tcx>>,
}
@ -78,7 +81,7 @@ pub struct CanonicalVarValues<'tcx> {
/// canonical value. This is sufficient information for code to create
/// a copy of the canonical value in some other inference context,
/// with fresh inference variables replacing the canonical values.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
pub struct CanonicalVarInfo {
pub kind: CanonicalVarKind,
}
@ -86,7 +89,7 @@ pub struct CanonicalVarInfo {
/// Describes the "kind" of the canonical variable. This is a "kind"
/// in the type-theory sense of the term -- i.e., a "meta" type system
/// that analyzes type-like values.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
pub enum CanonicalVarKind {
/// Some kind of type inference variable.
Ty(CanonicalTyVarKind),
@ -100,7 +103,7 @@ pub enum CanonicalVarKind {
/// 22.) can only be instantiated with integral/float types (e.g.,
/// usize or f32). In order to faithfully reproduce a type, we need to
/// know what set of types a given type variable can be unified with.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)]
pub enum CanonicalTyVarKind {
/// General type variable `?T` that can be unified with arbitrary types.
General,
@ -855,11 +858,14 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g
}
CloneTypeFoldableAndLiftImpls! {
::infer::canonical::Certainty,
::infer::canonical::CanonicalVarInfo,
::infer::canonical::CanonicalVarKind,
}
CloneTypeFoldableImpls! {
for <'tcx> {
::infer::canonical::Certainty,
::infer::canonical::CanonicalVarInfo,
::infer::canonical::CanonicalVarInfos<'tcx>,
::infer::canonical::CanonicalVarKind,
}
}
@ -870,6 +876,13 @@ BraceStructTypeFoldableImpl! {
} where C: TypeFoldable<'tcx>
}
BraceStructLiftImpl! {
impl<'a, 'tcx, T> Lift<'tcx> for Canonical<'a, T> {
type Lifted = Canonical<'tcx, T::Lifted>;
variables, value
} where T: Lift<'tcx>
}
impl<'tcx> CanonicalVarValues<'tcx> {
fn iter<'a>(&'a self) -> impl Iterator<Item = Kind<'tcx>> + 'a {
self.var_values.iter().cloned()

View file

@ -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<?0>` 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),
}
}

View file

@ -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>,

View file

@ -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<bool> = (None, parse_opt_bool, [TRACKED],

View file

@ -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<CanonicalVarInfos<'tcx>, D::Error>
where D: TyDecoder<'a, 'tcx>,
'tcx: 'a,
{
let len = decoder.read_usize()?;
let interned: Result<Vec<CanonicalVarInfo>, _> = (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<CanonicalVarInfos<'tcx>>
for $DecoderName<$($typaram),*> {
fn specialized_decode(&mut self)
-> Result<CanonicalVarInfos<'tcx>, 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> {

View file

@ -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<Def>,
/// Stores the canonicalized types provided by the user. See also `UserAssertTy` statement in
/// MIR.
user_provided_tys: ItemLocalMap<CanonicalTy<'tcx>>,
/// Stores the types for various nodes in the AST. Note that this table
/// is not guaranteed to be populated until after typeck. See
/// typeck::check::fn_ctxt for details.
@ -420,6 +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<CanonicalTy<'tcx>> {
LocalTableInContext {
local_id_root: self.local_id_root,
data: &self.user_provided_tys
}
}
pub fn user_provided_tys_mut(&mut self) -> LocalTableInContextMut<CanonicalTy<'tcx>> {
LocalTableInContextMut {
local_id_root: self.local_id_root,
data: &mut self.user_provided_tys
}
}
pub fn node_types(&self) -> LocalTableInContext<Ty<'tcx>> {
LocalTableInContext {
local_id_root: self.local_id_root,
@ -685,6 +705,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
let ty::TypeckTables {
local_id_root,
ref type_dependent_defs,
ref user_provided_tys,
ref node_types,
ref node_substs,
ref adjustments,
@ -704,6 +725,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
type_dependent_defs.hash_stable(hcx, hasher);
user_provided_tys.hash_stable(hcx, hasher);
node_types.hash_stable(hcx, hasher);
node_substs.hash_stable(hcx, hasher);
adjustments.hash_stable(hcx, hasher);
@ -1637,6 +1659,24 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice<Predicate<'a>> {
}
}
impl<'a, 'tcx> Lift<'tcx> for &'a Slice<CanonicalVarInfo> {
type Lifted = &'tcx Slice<CanonicalVarInfo>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
if self.len() == 0 {
return Some(Slice::empty());
}
if tcx.interners.arena.in_arena(*self as *const _) {
return Some(unsafe { mem::transmute(*self) });
}
// Also try in the global tcx if we're not that.
if !tcx.is_global() {
self.lift_to_tcx(tcx.global_tcx())
} else {
None
}
}
}
pub mod tls {
use super::{CtxtInterners, GlobalCtxt, TyCtxt};

View file

@ -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.

View file

@ -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),

View file

@ -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> {

View file

@ -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,

View file

@ -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 { .. }

View file

@ -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

View file

@ -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);

View file

@ -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<hir::HirId>,
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()

View file

@ -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 => {}
}

View file

@ -298,6 +298,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
}
StatementKind::EndRegion(_) |
StatementKind::Validate(..) |
StatementKind::UserAssertTy(..) |
StatementKind::Nop => {}
}
}

View file

@ -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),
},

View file

@ -93,10 +93,13 @@ pub enum StmtKind<'tcx> {
/// lifetime of temporaries
init_scope: region::Scope,
/// let <PAT> = ...
/// let <PAT>: ty = ...
pattern: Pattern<'tcx>,
/// let pat = <INIT> ...
/// let pat: <TY> = init ...
ty: Option<hir::HirId>,
/// let pat: ty = <INIT> ...
initializer: Option<ExprRef<'tcx>>,
/// the lint level for this let-statement

View file

@ -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 => {}

View file

@ -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)
}

View file

@ -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);
}

View file

@ -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,

View file

@ -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 => {}
}
});

View file

@ -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

View file

@ -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,

View file

@ -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);

View file

@ -84,6 +84,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
}
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Validate(..) |
mir::StatementKind::UserAssertTy(..) |
mir::StatementKind::Nop => bx,
}
}

View file

@ -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(

View file

@ -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() {

View file

@ -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;

View file

@ -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<std::boxed::Box<u32>> }, _4);
// _4 = std::option::Option<std::boxed::Box<u32>>::None;
// StorageLive(_5);
// StorageLive(_6);

View file

@ -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;
// ...

View file

@ -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)]

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(nll)]
fn main() {
let _vec: Vec<&'static String> = vec![&String::new()];
//~^ ERROR borrowed value does not live long enough [E0597]
}

View file

@ -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`.