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;
}
}
+/// `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,
/// - `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 {