//!
//! [rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/mir/index.html
-use graphviz::IntoCow;
use hir::def::CtorKind;
use hir::def_id::DefId;
use hir::{self, HirId, InlineAsm};
use syntax::symbol::InternedString;
use syntax_pos::{Span, DUMMY_SP};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
-use ty::subst::{Subst, Substs};
+use ty::subst::{CanonicalUserSubsts, Subst, Substs};
use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, GeneratorSubsts, Region, Ty, TyCtxt};
use util::ppaux;
if idx < stmts.len() {
&stmts[idx].source_info
} else {
- assert!(idx == stmts.len());
+ assert_eq!(idx, stmts.len());
&block.terminator().source_info
}
}
/// Check if `sub` is a sub scope of `sup`
pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool {
- loop {
- if sub == sup {
- return true;
- }
+ while sub != sup {
match self.source_scopes[sub].parent_scope {
None => return false,
Some(p) => sub = p,
}
}
+ true
}
/// Return the return type, it always return first element from `local_decls` array
pub fn allows_two_phase_borrow(&self) -> bool {
match *self {
BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false,
- BorrowKind::Mut {
- allow_two_phase_borrow,
- } => allow_two_phase_borrow,
+ BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow,
}
}
}
}
}
+/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries
+/// created during evaluation of expressions in a block tail
+/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`.
+///
+/// It is used to improve diagnostics when such temporaries are
+/// involved in borrow_check errors, e.g. explanations of where the
+/// temporaries come from, when their destructors are run, and/or how
+/// one might revise the code to satisfy the borrow checker's rules.
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct BlockTailInfo {
+ /// If `true`, then the value resulting from evaluating this tail
+ /// expression is ignored by the block's expression context.
+ ///
+ /// Examples include `{ ...; tail };` and `let _ = { ...; tail };`
+ /// but not e.g. `let _x = { ...; tail };`
+ pub tail_result_is_ignored: bool,
+}
+
+impl_stable_hash_for!(struct BlockTailInfo { tail_result_is_ignored });
+
/// A MIR local.
///
/// This can be a binding declared by the user, a temporary inserted by the compiler, a function
/// generator.
pub internal: bool,
+ /// If this local is a temporary and `is_block_tail` is `Some`,
+ /// then it is a temporary created for evaluation of some
+ /// subexpression of some block's tail expression (with no
+ /// intervening statement context).
+ pub is_block_tail: Option<BlockTailInfo>,
+
/// Type of this local.
pub ty: Ty<'tcx>,
/// 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<CanonicalTy<'tcx>>,
+ pub user_ty: Option<(UserTypeAnnotation<'tcx>, Span)>,
/// Name of the local, used in debuginfo and pretty-printing.
///
Self::new_local(ty, Mutability::Mut, false, span)
}
- /// Create a new immutable `LocalDecl` for a temporary.
+ /// Converts `self` into same `LocalDecl` except tagged as immutable.
#[inline]
- pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
- Self::new_local(ty, Mutability::Not, false, span)
+ pub fn immutable(mut self) -> Self {
+ self.mutability = Mutability::Not;
+ self
+ }
+
+ /// Converts `self` into same `LocalDecl` except tagged as internal temporary.
+ #[inline]
+ pub fn block_tail(mut self, info: BlockTailInfo) -> Self {
+ assert!(self.is_block_tail.is_none());
+ self.is_block_tail = Some(info);
+ self
}
/// Create a new `LocalDecl` for a internal temporary.
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal,
is_user_variable: None,
+ is_block_tail: None,
}
}
},
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal: false,
+ is_block_tail: None,
name: None, // FIXME maybe we do want some name here?
is_user_variable: None,
}
destination: Option<(Place<'tcx>, BasicBlock)>,
/// Cleanups to be done if the call unwinds.
cleanup: Option<BasicBlock>,
+ /// Whether this is from a call in HIR, rather than from an overloaded
+ /// operator. True for overloaded function call.
+ from_hir_call: bool,
},
/// Jump to the target if the condition has the expected value,
};
fmt_const_val(&mut s, &c).unwrap();
s.into()
- }).chain(iter::once(String::from("otherwise").into()))
+ }).chain(iter::once("otherwise".into()))
.collect()
}
Call {
destination: Some(_),
cleanup: Some(_),
..
- } => vec!["return".into_cow(), "unwind".into_cow()],
+ } => vec!["return".into(), "unwind".into()],
Call {
destination: Some(_),
cleanup: None,
..
- } => vec!["return".into_cow()],
+ } => vec!["return".into()],
Call {
destination: None,
cleanup: Some(_),
..
- } => vec!["unwind".into_cow()],
+ } => vec!["unwind".into()],
Call {
destination: None,
cleanup: None,
..
} => vec![],
- Yield { drop: Some(_), .. } => vec!["resume".into_cow(), "drop".into_cow()],
- Yield { drop: None, .. } => vec!["resume".into_cow()],
+ Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
+ Yield { drop: None, .. } => vec!["resume".into()],
DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
- vec!["return".into_cow()]
+ vec!["return".into()]
}
DropAndReplace {
unwind: Some(_), ..
}
| Drop {
unwind: Some(_), ..
- } => vec!["return".into_cow(), "unwind".into_cow()],
+ } => vec!["return".into(), "unwind".into()],
Assert { cleanup: None, .. } => vec!["".into()],
- Assert { .. } => vec!["success".into_cow(), "unwind".into_cow()],
+ Assert { .. } => vec!["success".into(), "unwind".into()],
FalseEdges {
ref imaginary_targets,
..
/// - `Contravariant` -- requires that `T_y :> T`
/// - `Invariant` -- requires that `T_y == T`
/// - `Bivariant` -- no effect
- AscribeUserType(Place<'tcx>, ty::Variance, CanonicalTy<'tcx>),
+ AscribeUserType(Place<'tcx>, ty::Variance, UserTypeAnnotation<'tcx>),
/// No-op. Useful for deleting instructions without affecting statement indices.
Nop,
Place::Projection(Box::new(PlaceProjection { base: self, elem }))
}
- /// Find the innermost `Local` from this `Place`.
+ /// Find the innermost `Local` from this `Place`, *if* it is either a local itself or
+ /// a single deref of a local.
+ ///
+ /// FIXME: can we safely swap the semantics of `fn base_local` below in here instead?
pub fn local(&self) -> Option<Local> {
match self {
Place::Local(local) |
_ => None,
}
}
+
+ /// Find the innermost `Local` from this `Place`.
+ pub fn base_local(&self) -> Option<Local> {
+ match self {
+ Place::Local(local) => Some(*local),
+ Place::Projection(box Projection { base, elem: _ }) => base.base_local(),
+ Place::Promoted(..) | Place::Static(..) => None,
+ }
+ }
}
impl<'tcx> Debug for Place<'tcx> {
&'tcx AdtDef,
usize,
&'tcx Substs<'tcx>,
- Option<CanonicalTy<'tcx>>,
+ Option<UserTypeAnnotation<'tcx>>,
Option<usize>,
),
/// this does not necessarily mean that they are "==" in Rust -- in
/// particular one must be wary of `NaN`!
-#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct Constant<'tcx> {
pub span: Span,
pub ty: Ty<'tcx>,
/// indicate that `Vec<_>` was explicitly specified.
///
/// Needed for NLL to impose user-given type constraints.
- pub user_ty: Option<CanonicalTy<'tcx>>,
+ pub user_ty: Option<UserTypeAnnotation<'tcx>>,
pub literal: &'tcx ty::Const<'tcx>,
}
+/// A user-given type annotation attached to a constant. These arise
+/// from constants that are named via paths, like `Foo::<A>::new` and
+/// so forth.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub enum UserTypeAnnotation<'tcx> {
+ Ty(CanonicalTy<'tcx>),
+ FnDef(DefId, CanonicalUserSubsts<'tcx>),
+ AdtDef(&'tcx AdtDef, CanonicalUserSubsts<'tcx>),
+}
+
+EnumTypeFoldableImpl! {
+ impl<'tcx> TypeFoldable<'tcx> for UserTypeAnnotation<'tcx> {
+ (UserTypeAnnotation::Ty)(ty),
+ (UserTypeAnnotation::FnDef)(def, substs),
+ (UserTypeAnnotation::AdtDef)(def, substs),
+ }
+}
+
newtype_index! {
pub struct Promoted {
DEBUG_FORMAT = "promoted[{}]"
// This region or type ...
pub subject: ClosureOutlivesSubject<'tcx>,
- // .. must outlive this one.
+ // ... must outlive this one.
pub outlived_free_region: ty::RegionVid,
- // If not, report an error here.
+ // If not, report an error here ...
pub blame_span: Span,
+
+ // ... due to this reason.
+ pub category: ConstraintCategory,
+}
+
+/// Outlives constraints can be categorized to determine whether and why they
+/// are interesting (for error reporting). Order of variants indicates sort
+/// order of the category, thereby influencing diagnostic output.
+///
+/// See also [rustc_mir::borrow_check::nll::constraints]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
+pub enum ConstraintCategory {
+ Return,
+ TypeAnnotation,
+ Cast,
+
+ /// A constraint that came from checking the body of a closure.
+ ///
+ /// We try to get the category that the closure used when reporting this.
+ ClosureBounds,
+ CallArgument,
+ CopyBound,
+ SizedBound,
+ Assignment,
+ OpaqueType,
+
+ /// A "boring" constraint (caused by the given location) is one that
+ /// the user probably doesn't want to see described in diagnostics,
+ /// because it is kind of an artifact of the type system setup.
+ /// Example: `x = Foo { field: y }` technically creates
+ /// intermediate regions representing the "type of `Foo { field: y
+ /// }`", and data flows from `y` into those variables, but they
+ /// are not very interesting. The assignment into `x` on the other
+ /// hand might be.
+ Boring,
+ // Boring and applicable everywhere.
+ BoringNoLocation,
+
+ /// A constraint that doesn't correspond to anything the user sees.
+ Internal,
}
/// The subject of a ClosureOutlivesRequirement -- that is, the thing
*/
CloneTypeFoldableAndLiftImpls! {
+ BlockTailInfo,
Mutability,
SourceInfo,
UpvarDecl,
user_ty,
name,
source_info,
+ is_block_tail,
visibility_scope,
}
}
ref args,
ref destination,
cleanup,
+ from_hir_call,
} => {
let dest = destination
.as_ref()
args: args.fold_with(folder),
destination: dest,
cleanup,
+ from_hir_call,
}
}
Assert {