Auto merge of #73367 - RalfJung:rollup-4ewvk9b, r=RalfJung

Rollup of 10 pull requests

Successful merges:

 - #71824 (Check for live drops in constants after drop elaboration)
 - #72389 (Explain move errors that occur due to method calls involving `self`)
 - #72556 (Fix trait alias inherent impl resolution)
 - #72584 (Stabilize vec::Drain::as_slice)
 - #72598 (Display information about captured variable in `FnMut` error)
 - #73336 (Group `Pattern::strip_*` method together)
 - #73341 (_match.rs: fix module doc comment)
 - #73342 (Fix iterator copied() documentation example code)
 - #73351 (Update E0446.md)
 - #73353 (structural_match: non-structural-match ty closures)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-06-15 08:09:38 +00:00
commit d4ecf31efc
87 changed files with 1574 additions and 507 deletions

View File

@ -2779,19 +2779,25 @@ impl<'a, T> Drain<'a, T> {
/// # Examples
///
/// ```
/// # #![feature(vec_drain_as_slice)]
/// let mut vec = vec!['a', 'b', 'c'];
/// let mut drain = vec.drain(..);
/// assert_eq!(drain.as_slice(), &['a', 'b', 'c']);
/// let _ = drain.next().unwrap();
/// assert_eq!(drain.as_slice(), &['b', 'c']);
/// ```
#[unstable(feature = "vec_drain_as_slice", reason = "recently added", issue = "58957")]
#[stable(feature = "vec_drain_as_slice", since = "1.46.0")]
pub fn as_slice(&self) -> &[T] {
self.iter.as_slice()
}
}
#[stable(feature = "vec_drain_as_slice", since = "1.46.0")]
impl<'a, T> AsRef<[T]> for Drain<'a, T> {
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
#[stable(feature = "drain", since = "1.6.0")]
unsafe impl<T: Sync> Sync for Drain<'_, T> {}
#[stable(feature = "drain", since = "1.6.0")]

View File

@ -56,6 +56,7 @@ pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
where
T: Generator<ResumeTy, Yield = ()>,
{
#[rustc_diagnostic_item = "gen_future"]
struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);
// We rely on the fact that async/await futures are immovable in order to create

View File

@ -2717,12 +2717,12 @@ pub trait Iterator {
/// ```
/// let a = [1, 2, 3];
///
/// let v_cloned: Vec<_> = a.iter().copied().collect();
/// let v_copied: Vec<_> = a.iter().copied().collect();
///
/// // copied is the same as .map(|&x| x)
/// let v_map: Vec<_> = a.iter().map(|&x| x).collect();
///
/// assert_eq!(v_cloned, vec![1, 2, 3]);
/// assert_eq!(v_copied, vec![1, 2, 3]);
/// assert_eq!(v_map, vec![1, 2, 3]);
/// ```
#[stable(feature = "iter_copied", since = "1.36.0")]

View File

@ -69,7 +69,7 @@ use crate::slice::memchr;
/// |--------------------------|-------------------------------------------|
/// | `&str` | is substring |
/// | `char` | is contained in string |
/// | `&[char] | any char in slice is contained in string |
/// | `&[char]` | any char in slice is contained in string |
/// | `F: FnMut(char) -> bool` | `F` returns `true` for a char in string |
/// | `&&str` | is substring |
/// | `&String` | is substring |
@ -117,6 +117,15 @@ pub trait Pattern<'a>: Sized {
matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _))
}
/// Checks whether the pattern matches at the back of the haystack
#[inline]
fn is_suffix_of(self, haystack: &'a str) -> bool
where
Self::Searcher: ReverseSearcher<'a>,
{
matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j)
}
/// Removes the pattern from the front of haystack, if it matches.
#[inline]
fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> {
@ -133,15 +142,6 @@ pub trait Pattern<'a>: Sized {
}
}
/// Checks whether the pattern matches at the back of the haystack
#[inline]
fn is_suffix_of(self, haystack: &'a str) -> bool
where
Self::Searcher: ReverseSearcher<'a>,
{
matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j)
}
/// Removes the pattern from the back of haystack, if it matches.
#[inline]
fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str>

View File

@ -9,7 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::source_map::{respan, DesugaringKind, ForLoopLoc, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_target::asm;
use std::collections::hash_map::Entry;
@ -25,6 +25,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
let mut span = e.span;
ensure_sufficient_stack(|| {
let kind = match e.kind {
ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)),
@ -53,6 +54,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args, span)
}
ExprKind::Binary(binop, ref lhs, ref rhs) => {
span = self.mark_span_with_reason(DesugaringKind::Operator, e.span, None);
let binop = self.lower_binop(binop);
let lhs = self.lower_expr(lhs);
let rhs = self.lower_expr(rhs);
@ -222,7 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::Expr {
hir_id: self.lower_node_id(e.id),
kind,
span: e.span,
span,
attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::<Vec<_>>().into(),
}
})
@ -237,6 +239,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
fn lower_binop(&mut self, b: BinOp) -> hir::BinOp {
let span = self.mark_span_with_reason(DesugaringKind::Operator, b.span, None);
Spanned {
node: match b.node {
BinOpKind::Add => hir::BinOpKind::Add,
@ -258,7 +261,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
BinOpKind::Ge => hir::BinOpKind::Ge,
BinOpKind::Gt => hir::BinOpKind::Gt,
},
span: b.span,
span,
}
}
@ -1360,9 +1363,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: &Block,
opt_label: Option<Label>,
) -> hir::Expr<'hir> {
let orig_head_span = head.span;
// expand <head>
let mut head = self.lower_expr_mut(head);
let desugared_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None);
let desugared_span = self.mark_span_with_reason(
DesugaringKind::ForLoop(ForLoopLoc::Head),
orig_head_span,
None,
);
head.span = desugared_span;
let iter = Ident::with_dummy_span(sym::iter);
@ -1457,10 +1465,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `mut iter => { ... }`
let iter_arm = self.arm(iter_pat, loop_expr);
let into_iter_span = self.mark_span_with_reason(
DesugaringKind::ForLoop(ForLoopLoc::IntoIter),
orig_head_span,
None,
);
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
let into_iter_path = &[sym::iter, sym::IntoIterator, sym::into_iter];
self.expr_call_std_path(desugared_span, into_iter_path, arena_vec![self; head])
self.expr_call_std_path(into_iter_span, into_iter_path, arena_vec![self; head])
};
let match_expr = self.arena.alloc(self.expr_match(

View File

@ -4,10 +4,10 @@ Erroneous code example:
```compile_fail,E0446
#![deny(private_in_public)]
struct Bar(u32);
mod Foo {
struct Bar(u32);
mod foo {
use crate::Bar;
pub fn bar() -> Bar { // error: private type in public interface
Bar(0)
}
@ -16,15 +16,31 @@ mod Foo {
fn main() {}
```
To solve this error, please ensure that the type is also public. The type
can be made inaccessible if necessary by placing it into a private inner
module, but it still has to be marked with `pub`.
There are two ways to solve this error. The first is to make the public type
signature only public to a module that also has access to the private type.
This is done by using pub(crate) or pub(in crate::my_mod::etc)
Example:
```
mod Foo {
pub struct Bar(u32); // we set the Bar type public
struct Bar(u32);
mod foo {
use crate::Bar;
pub(crate) fn bar() -> Bar { // only public to crate root
Bar(0)
}
}
fn main() {}
```
The other way to solve this error is to make the private type public.
Example:
```
pub struct Bar(u32); // we set the Bar type public
mod foo {
use crate::Bar;
pub fn bar() -> Bar { // ok!
Bar(0)
}

View File

@ -1,5 +1,4 @@
A type with a `Drop` implementation was destructured when trying to initialize
a static item.
A value with a custom `Drop` implementation may be dropped during const-eval.
Erroneous code example:
@ -16,13 +15,14 @@ struct Foo {
field1: DropType,
}
static FOO: Foo = Foo { ..Foo { field1: DropType::A } }; // error!
static FOO: Foo = Foo { field1: (DropType::A, DropType::A).1 }; // error!
```
The problem here is that if the given type or one of its fields implements the
`Drop` trait, this `Drop` implementation cannot be called during the static
type initialization which might cause a memory leak. To prevent this issue,
you need to instantiate all the static type's fields by hand.
`Drop` trait, this `Drop` implementation cannot be called within a const
context since it may run arbitrary, non-const-checked code. To prevent this
issue, ensure all values with custom a custom `Drop` implementation escape the
initializer.
```
enum DropType {

View File

@ -577,6 +577,9 @@ declare_features! (
/// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`.
(active, abi_avr_interrupt, "1.45.0", Some(69664), None),
/// Be more precise when looking for live drops in a const context.
(active, const_precise_live_drops, "1.46.0", Some(73255), None),
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------

View File

@ -455,7 +455,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let msg = if let Some(simple_ident) = pattern.simple_ident() {
match pattern.span.desugaring_kind() {
None => format!("consider giving `{}` {}", simple_ident, suffix),
Some(DesugaringKind::ForLoop) => {
Some(DesugaringKind::ForLoop(_)) => {
"the element type for this iterator is not specified".to_string()
}
_ => format!("this needs {}", suffix),

View File

@ -847,7 +847,11 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
sess.time("MIR_effect_checking", || {
for def_id in tcx.body_owners() {
mir::transform::check_unsafety::check_unsafety(tcx, def_id)
mir::transform::check_unsafety::check_unsafety(tcx, def_id);
if tcx.hir().body_const_context(def_id).is_some() {
tcx.ensure().mir_drops_elaborated_and_const_checked(def_id);
}
}
});

View File

@ -1315,13 +1315,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
}
}
fn get_fn_param_names(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Symbol] {
fn get_fn_param_names(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Ident] {
let param_names = match self.kind(id) {
EntryKind::Fn(data) | EntryKind::ForeignFn(data) => data.decode(self).param_names,
EntryKind::AssocFn(data) => data.decode(self).fn_data.param_names,
_ => Lazy::empty(),
};
tcx.arena.alloc_from_iter(param_names.decode(self))
tcx.arena.alloc_from_iter(param_names.decode((self, tcx)))
}
fn exported_symbols(

View File

@ -30,7 +30,7 @@ use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
use rustc_serialize::{opaque, Encodable, Encoder, SpecializedEncoder};
use rustc_session::config::CrateType;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{self, ExternalSource, FileName, SourceFile, Span};
use rustc_target::abi::VariantIdx;
use std::hash::Hash;
@ -994,18 +994,12 @@ impl EncodeContext<'tcx> {
}
}
fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Symbol]> {
self.tcx.dep_graph.with_ignore(|| {
let body = self.tcx.hir().body(body_id);
self.lazy(body.params.iter().map(|arg| match arg.pat.kind {
hir::PatKind::Binding(_, _, ident, _) => ident.name,
_ => kw::Invalid,
}))
})
fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Ident]> {
self.tcx.dep_graph.with_ignore(|| self.lazy(self.tcx.hir().body_param_names(body_id)))
}
fn encode_fn_param_names(&mut self, param_names: &[Ident]) -> Lazy<[Symbol]> {
self.lazy(param_names.iter().map(|ident| ident.name))
fn encode_fn_param_names(&mut self, param_names: &[Ident]) -> Lazy<[Ident]> {
self.lazy(param_names.iter())
}
fn encode_optimized_mir(&mut self, def_id: LocalDefId) {

View File

@ -19,7 +19,7 @@ use rustc_serialize::opaque::Encoder;
use rustc_session::config::SymbolManglingVersion;
use rustc_session::CrateDisambiguator;
use rustc_span::edition::Edition;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::{self, Span};
use rustc_target::spec::{PanicStrategy, TargetTriple};
@ -326,7 +326,7 @@ struct ModData {
struct FnData {
asyncness: hir::IsAsync,
constness: hir::Constness,
param_names: Lazy<[Symbol]>,
param_names: Lazy<[Ident]>,
}
#[derive(RustcEncodable, RustcDecodable)]

View File

@ -14,7 +14,7 @@ use rustc_hir::*;
use rustc_index::vec::IndexVec;
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
@ -374,6 +374,13 @@ impl<'hir> Map<'hir> {
})
}
pub fn body_param_names(&self, id: BodyId) -> impl Iterator<Item = Ident> + 'hir {
self.body(id).params.iter().map(|arg| match arg.pat.kind {
PatKind::Binding(_, _, ident, _) => ident,
_ => Ident::new(kw::Invalid, rustc_span::DUMMY_SP),
})
}
/// Returns the `BodyOwnerKind` of this `LocalDefId`.
///
/// Panics if `LocalDefId` does not have an associated body.

View File

@ -12,10 +12,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE};
use rustc_hir::Body;
use rustc_hir::HirId;
use rustc_hir::ItemLocalId;
use rustc_hir::Node;
use rustc_hir::*;
use rustc_index::vec::IndexVec;
pub struct Owner<'tcx> {
@ -79,5 +76,20 @@ pub fn provide(providers: &mut Providers<'_>) {
};
providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature;
providers.hir_owner_nodes = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_deref();
providers.fn_arg_names = |tcx, id| {
let hir = tcx.hir();
let hir_id = hir.as_local_hir_id(id.expect_local());
if let Some(body_id) = hir.maybe_body_owned_by(hir_id) {
tcx.arena.alloc_from_iter(hir.body_param_names(body_id))
} else if let Node::TraitItem(&TraitItem {
kind: TraitItemKind::Fn(_, TraitFn::Required(idents)),
..
}) = hir.get(hir_id)
{
tcx.arena.alloc_slice(idents)
} else {
span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id);
}
};
map::provide(providers);
}

View File

@ -339,7 +339,9 @@ pub fn struct_lint_level<'s, 'd>(
pub fn in_external_macro(sess: &Session, span: Span) -> bool {
let expn_data = span.ctxt().outer_expn_data();
match expn_data.kind {
ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => false,
ExpnKind::Root
| ExpnKind::Desugaring(DesugaringKind::ForLoop(_))
| ExpnKind::Desugaring(DesugaringKind::Operator) => false,
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
ExpnKind::Macro(MacroKind::Bang, _) => {
// Dummy span for the `def_site` means it's an external macro.

View File

@ -76,7 +76,8 @@ pub enum MirPhase {
Build = 0,
Const = 1,
Validated = 2,
Optimized = 3,
DropElab = 3,
Optimized = 4,
}
impl MirPhase {

View File

@ -183,7 +183,7 @@ pub struct ClosureOutlivesRequirement<'tcx> {
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[derive(RustcEncodable, RustcDecodable, HashStable)]
pub enum ConstraintCategory {
Return,
Return(ReturnConstraint),
Yield,
UseAsConst,
UseAsStatic,
@ -199,6 +199,7 @@ pub enum ConstraintCategory {
SizedBound,
Assignment,
OpaqueType,
ClosureUpvar(hir::HirId),
/// A "boring" constraint (caused by the given location) is one that
/// the user probably doesn't want to see described in diagnostics,
@ -216,6 +217,13 @@ pub enum ConstraintCategory {
Internal,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[derive(RustcEncodable, RustcDecodable, HashStable)]
pub enum ReturnConstraint {
Normal,
ClosureUpvar(hir::HirId),
}
/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing
/// that must outlive some region.
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]

View File

@ -190,6 +190,12 @@ rustc_queries! {
no_hash
}
query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> Steal<mir::Body<'tcx>> {
storage(ArenaCacheSelector<'tcx>)
no_hash
desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.to_def_id()) }
}
query mir_validated(key: LocalDefId) ->
(
Steal<mir::Body<'tcx>>,
@ -700,7 +706,7 @@ rustc_queries! {
}
Other {
query fn_arg_names(def_id: DefId) -> &'tcx [Symbol] {
query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] {
desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) }
}
/// Gets the rendered value of the specified constant or associated constant.

View File

@ -24,7 +24,8 @@ use crate::borrow_check::{
};
use super::{
explain_borrow::BorrowExplanation, IncludingDowncast, RegionName, RegionNameSource, UseSpans,
explain_borrow::BorrowExplanation, FnSelfUseKind, IncludingDowncast, RegionName,
RegionNameSource, UseSpans,
};
#[derive(Debug)]
@ -150,13 +151,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
format!("value moved{} here, in previous iteration of loop", move_msg),
);
} else {
err.span_label(move_span, format!("value moved{} here", move_msg));
move_spans.var_span_label(
&mut err,
format!("variable moved due to use{}", move_spans.describe()),
);
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } =
move_spans
{
let place_name = self
.describe_place(moved_place.as_ref())
.map(|n| format!("`{}`", n))
.unwrap_or_else(|| "value".to_owned());
match kind {
FnSelfUseKind::FnOnceCall => {
err.span_label(
fn_call_span,
&format!("{} moved due to this call", place_name),
);
err.span_note(
var_span,
"this value implements `FnOnce`, which causes it to be moved when called",
);
}
FnSelfUseKind::Operator { self_arg } => {
err.span_label(
fn_call_span,
&format!("{} moved due to usage in operator", place_name),
);
if self.fn_self_span_reported.insert(fn_span) {
err.span_note(
self_arg.span,
"calling this operator moves the left-hand side",
);
}
}
FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
if implicit_into_iter {
err.span_label(
fn_call_span,
&format!(
"{} moved due to this implicit call to `.into_iter()`",
place_name
),
);
} else {
err.span_label(
fn_call_span,
&format!("{} moved due to this method call", place_name),
);
}
// Avoid pointing to the same function in multiple different
// error messages
if self.fn_self_span_reported.insert(self_arg.span) {
err.span_note(
self_arg.span,
&format!("this function consumes the receiver `self` by taking ownership of it, which moves {}", place_name)
);
}
}
}
} else {
err.span_label(move_span, format!("value moved{} here", move_msg));
move_spans.var_span_label(
&mut err,
format!("variable moved due to use{}", move_spans.describe()),
);
}
}
if Some(DesugaringKind::ForLoop) == move_span.desugaring_kind() {
if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() {
let sess = self.infcx.tcx.sess;
if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) {
err.span_suggestion(
@ -766,7 +824,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
category:
category
@
(ConstraintCategory::Return
(ConstraintCategory::Return(_)
| ConstraintCategory::CallArgument
| ConstraintCategory::OpaqueType),
from_closure: false,
@ -1089,7 +1147,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
opt_place_desc: Option<&String>,
) -> Option<DiagnosticBuilder<'cx>> {
let return_kind = match category {
ConstraintCategory::Return => "return",
ConstraintCategory::Return(_) => "return",
ConstraintCategory::Yield => "yield",
_ => return None,
};
@ -1203,7 +1261,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
let msg = match category {
ConstraintCategory::Return | ConstraintCategory::OpaqueType => {
ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
format!("{} is returned here", kind)
}
ConstraintCategory::CallArgument => {

View File

@ -509,7 +509,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// Used in a closure.
(LaterUseKind::ClosureCapture, var_span)
}
UseSpans::OtherUse(span) => {
UseSpans::OtherUse(span) | UseSpans::FnSelfUse { var_span: span, .. } => {
let block = &self.body.basic_blocks()[location.block];
let kind = if let Some(&Statement {

View File

@ -11,7 +11,11 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
use rustc_span::{symbol::sym, Span};
use rustc_span::{
hygiene::{DesugaringKind, ForLoopLoc},
symbol::sym,
Span,
};
use rustc_target::abi::VariantIdx;
use super::borrow_set::BorrowData;
@ -33,6 +37,7 @@ crate use mutability_errors::AccessKind;
crate use outlives_suggestion::OutlivesSuggestionBuilder;
crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
crate use region_name::{RegionName, RegionNameSource};
use rustc_span::symbol::Ident;
pub(super) struct IncludingDowncast(pub(super) bool);
@ -529,33 +534,58 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
// The span(s) associated to a use of a place.
/// The span(s) associated to a use of a place.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum UseSpans {
// The access is caused by capturing a variable for a closure.
/// The access is caused by capturing a variable for a closure.
ClosureUse {
// This is true if the captured variable was from a generator.
/// This is true if the captured variable was from a generator.
generator_kind: Option<GeneratorKind>,
// The span of the args of the closure, including the `move` keyword if
// it's present.
/// The span of the args of the closure, including the `move` keyword if
/// it's present.
args_span: Span,
// The span of the first use of the captured variable inside the closure.
/// The span of the first use of the captured variable inside the closure.
var_span: Span,
},
/// The access is caused by using a variable as the receiver of a method
/// that takes 'self'
FnSelfUse {
/// The span of the variable being moved
var_span: Span,
/// The span of the method call on the variable
fn_call_span: Span,
/// The definition span of the method being called
fn_span: Span,
kind: FnSelfUseKind,
},
// This access has a single span associated to it: common case.
OtherUse(Span),
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum FnSelfUseKind {
/// A normal method call of the form `receiver.foo(a, b, c)`
Normal { self_arg: Ident, implicit_into_iter: bool },
/// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
FnOnceCall,
/// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
Operator { self_arg: Ident },
}
impl UseSpans {
pub(super) fn args_or_use(self) -> Span {
match self {
UseSpans::ClosureUse { args_span: span, .. } | UseSpans::OtherUse(span) => span,
UseSpans::ClosureUse { args_span: span, .. }
| UseSpans::FnSelfUse { var_span: span, .. }
| UseSpans::OtherUse(span) => span,
}
}
pub(super) fn var_or_use(self) -> Span {
match self {
UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
UseSpans::ClosureUse { var_span: span, .. }
| UseSpans::FnSelfUse { var_span: span, .. }
| UseSpans::OtherUse(span) => span,
}
}
@ -624,6 +654,7 @@ impl UseSpans {
{
match self {
closure @ UseSpans::ClosureUse { .. } => closure,
fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
UseSpans::OtherUse(_) => if_other(),
}
}
@ -727,21 +758,100 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind {
let def_id = match kind {
match kind {
box AggregateKind::Closure(def_id, _)
| box AggregateKind::Generator(def_id, _, _) => def_id,
_ => return OtherUse(stmt.source_info.span),
};
debug!("move_spans: def_id={:?} places={:?}", def_id, places);
if let Some((args_span, generator_kind, var_span)) =
self.closure_span(*def_id, moved_place, places)
{
return ClosureUse { generator_kind, args_span, var_span };
| box AggregateKind::Generator(def_id, _, _) => {
debug!("move_spans: def_id={:?} places={:?}", def_id, places);
if let Some((args_span, generator_kind, var_span)) =
self.closure_span(*def_id, moved_place, places)
{
return ClosureUse { generator_kind, args_span, var_span };
}
}
_ => {}
}
}
OtherUse(stmt.source_info.span)
let normal_ret = OtherUse(stmt.source_info.span);
// We are trying to find MIR of the form:
// ```
// _temp = _moved_val;
// ...
// FnSelfCall(_temp, ...)
// ```
//
// where `_moved_val` is the place we generated the move error for,
// `_temp` is some other local, and `FnSelfCall` is a function
// that has a `self` parameter.
let target_temp = match stmt.kind {
StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => {
temp.as_local().unwrap()
}
_ => return normal_ret,
};
debug!("move_spans: target_temp = {:?}", target_temp);
if let Some(Terminator { kind: TerminatorKind::Call { func, args, fn_span, .. }, .. }) =
&self.body[location.block].terminator
{
let mut method_did = None;
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
if let ty::FnDef(def_id, _) = ty.kind {
debug!("move_spans: fn = {:?}", def_id);
if let Some(ty::AssocItem { fn_has_self_parameter, .. }) =
self.infcx.tcx.opt_associated_item(def_id)
{
if *fn_has_self_parameter {
method_did = Some(def_id);
}
}
}
}
let tcx = self.infcx.tcx;
let method_did = if let Some(did) = method_did { did } else { return normal_ret };
if let [Operand::Move(self_place), ..] = **args {
if self_place.as_local() == Some(target_temp) {
let is_fn_once = tcx.parent(method_did) == tcx.lang_items().fn_once_trait();
let fn_call_span = *fn_span;
let self_arg = tcx.fn_arg_names(method_did)[0];
let kind = if is_fn_once {
FnSelfUseKind::FnOnceCall
} else if fn_call_span.is_desugaring(DesugaringKind::Operator) {
FnSelfUseKind::Operator { self_arg }
} else {
debug!(
"move_spans: method_did={:?}, fn_call_span={:?}",
method_did, fn_call_span
);
let implicit_into_iter = matches!(
fn_call_span.desugaring_kind(),
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
);
FnSelfUseKind::Normal { self_arg, implicit_into_iter }
};
return FnSelfUse {
var_span: stmt.source_info.span,
fn_call_span,
fn_span: self
.infcx
.tcx
.sess
.source_map()
.guess_head_span(self.infcx.tcx.def_span(method_did)),
kind,
};
}
}
}
return normal_ret;
}
/// Finds the span of arguments of a closure (within `maybe_closure_span`)

View File

@ -408,7 +408,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
format!("{}.as_ref()", snippet),
Applicability::MaybeIncorrect,
);
} else if span.is_desugaring(DesugaringKind::ForLoop)
} else if matches!(span.desugaring_kind(), Some(DesugaringKind::ForLoop(_)))
&& self.infcx.tcx.is_diagnostic_item(Symbol::intern("vec_type"), def_id)
{
// FIXME: suggest for anything that implements `IntoIterator`.

View File

@ -365,7 +365,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
opt_assignment_rhs_span.and_then(|span| span.desugaring_kind());
match opt_desugaring_kind {
// on for loops, RHS points to the iterator part
Some(DesugaringKind::ForLoop) => Some((
Some(DesugaringKind::ForLoop(_)) => Some((
false,
opt_assignment_rhs_span.unwrap(),
format!(

View File

@ -5,9 +5,9 @@ use rustc_infer::infer::{
error_reporting::nice_region_error::NiceRegionError,
error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin,
};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
use rustc_middle::ty::{self, RegionVid, Ty};
use rustc_span::symbol::kw;
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use crate::util::borrowck_errors;
@ -26,7 +26,7 @@ impl ConstraintDescription for ConstraintCategory {
// Must end with a space. Allows for empty names to be provided.
match self {
ConstraintCategory::Assignment => "assignment ",
ConstraintCategory::Return => "returning this value ",
ConstraintCategory::Return(_) => "returning this value ",
ConstraintCategory::Yield => "yielding this value ",
ConstraintCategory::UseAsConst => "using this value as a constant ",
ConstraintCategory::UseAsStatic => "using this value as a static ",
@ -37,6 +37,7 @@ impl ConstraintDescription for ConstraintCategory {
ConstraintCategory::SizedBound => "proving this value is `Sized` ",
ConstraintCategory::CopyBound => "copying this value ",
ConstraintCategory::OpaqueType => "opaque type ",
ConstraintCategory::ClosureUpvar(_) => "closure capture ",
ConstraintCategory::Boring
| ConstraintCategory::BoringNoLocation
| ConstraintCategory::Internal => "",
@ -306,8 +307,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
};
let diag = match (category, fr_is_local, outlived_fr_is_local) {
(ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(fr) => {
self.report_fnmut_error(&errci)
(ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => {
self.report_fnmut_error(&errci, kind)
}
(ConstraintCategory::Assignment, true, false)
| (ConstraintCategory::CallArgument, true, false) => {
@ -347,7 +348,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
/// executing...
/// = note: ...therefore, returned references to captured variables will escape the closure
/// ```
fn report_fnmut_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
fn report_fnmut_error(
&self,
errci: &ErrorConstraintInfo,
kind: ReturnConstraint,
) -> DiagnosticBuilder<'tcx> {
let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
let mut diag = self
@ -356,19 +361,39 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
.sess
.struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");
// We should check if the return type of this closure is in fact a closure - in that
// case, we can special case the error further.
let return_type_is_closure =
self.regioncx.universal_regions().unnormalized_output_ty.is_closure();
let message = if return_type_is_closure {
"returns a closure that contains a reference to a captured variable, which then \
escapes the closure body"
} else {
"returns a reference to a captured variable which escapes the closure body"
let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
if let ty::Opaque(def_id, _) = output_ty.kind {
output_ty = self.infcx.tcx.type_of(def_id)
};
debug!("report_fnmut_error: output_ty={:?}", output_ty);
let message = match output_ty.kind {
ty::Closure(_, _) => {
"returns a closure that contains a reference to a captured variable, which then \
escapes the closure body"
}
ty::Adt(def, _) if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did) => {
"returns an `async` block that contains a reference to a captured variable, which then \
escapes the closure body"
}
_ => "returns a reference to a captured variable which escapes the closure body",
};
diag.span_label(*span, message);
if let ReturnConstraint::ClosureUpvar(upvar) = kind {
let def_id = match self.regioncx.universal_regions().defining_ty {
DefiningTy::Closure(def_id, _) => def_id,
ty @ _ => bug!("unexpected DefiningTy {:?}", ty),
};
let upvar_def_span = self.infcx.tcx.hir().span(upvar);
let upvar_span = self.infcx.tcx.upvars_mentioned(def_id).unwrap()[&upvar].span;
diag.span_label(upvar_def_span, "variable defined here");
diag.span_label(upvar_span, "variable captured here");
}
match self.give_region_a_name(*outlived_fr).unwrap().source {
RegionNameSource::NamedEarlyBoundRegion(fr_span)
| RegionNameSource::NamedFreeRegion(fr_span)
@ -506,7 +531,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
outlived_fr_name.highlight_region_name(&mut diag);
match (category, outlived_fr_is_local, fr_is_local) {
(ConstraintCategory::Return, true, _) => {
(ConstraintCategory::Return(_), true, _) => {
diag.span_label(
*span,
format!(

View File

@ -216,6 +216,7 @@ fn do_mir_borrowck<'a, 'tcx>(
&mut flow_inits,
&mdpe.move_data,
&borrow_set,
&upvars,
);
// Dump MIR results into a file, if that is enabled. This let us
@ -277,6 +278,7 @@ fn do_mir_borrowck<'a, 'tcx>(
move_data: &move_data,
location_table: &LocationTable::new(promoted_body),
movable_generator,
fn_self_span_reported: Default::default(),
locals_are_invalidated_at_exit,
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
@ -310,6 +312,7 @@ fn do_mir_borrowck<'a, 'tcx>(
location_table,
movable_generator,
locals_are_invalidated_at_exit,
fn_self_span_reported: Default::default(),
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
reservation_warnings: Default::default(),
@ -486,6 +489,10 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
// but it is currently inconvenient to track down the `BorrowIndex`
// at the time we detect and report a reservation error.
reservation_error_reported: FxHashSet<Place<'tcx>>,
/// This fields keeps track of the `Span`s that we have
/// used to report extra information for `FnSelfUse`, to avoid
/// unnecessarily verbose errors.
fn_self_span_reported: FxHashSet<Span>,
/// Migration warnings to be reported for #56254. We delay reporting these
/// so that we can suppress the warning if there's a corresponding error
/// for the activation of the borrow.
@ -2308,30 +2315,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// be `self` in the current MIR, because that is the only time we directly access the fields
/// of a closure type.
pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<Field> {
let mut place_projection = place_ref.projection;
let mut by_ref = false;
if let [proj_base @ .., ProjectionElem::Deref] = place_projection {
place_projection = proj_base;
by_ref = true;
}
match place_projection {
[base @ .., ProjectionElem::Field(field, _ty)] => {
let tcx = self.infcx.tcx;
let base_ty = Place::ty_from(place_ref.local, base, self.body(), tcx).ty;
if (base_ty.is_closure() || base_ty.is_generator())
&& (!by_ref || self.upvars[field.index()].by_ref)
{
Some(*field)
} else {
None
}
}
_ => None,
}
path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body())
}
}

View File

@ -39,6 +39,7 @@ use crate::borrow_check::{
renumber,
type_check::{self, MirTypeckRegionConstraints, MirTypeckResults},
universal_regions::UniversalRegions,
Upvar,
};
crate type PoloniusOutput = Output<RustcFacts>;
@ -166,6 +167,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
upvars: &[Upvar],
) -> NllOutput<'tcx> {
let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default());
@ -188,6 +190,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
flow_inits,
move_data,
elements,
upvars,
);
if let Some(all_facts) = &mut all_facts {

View File

@ -1,10 +1,11 @@
use crate::borrow_check::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
use crate::borrow_check::places_conflict;
use crate::borrow_check::AccessDepth;
use crate::borrow_check::Upvar;
use crate::dataflow::indexes::BorrowIndex;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_middle::mir::BorrowKind;
use rustc_middle::mir::{BasicBlock, Body, Location, Place};
use rustc_middle::mir::{BasicBlock, Body, Field, Location, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::TyCtxt;
/// Returns `true` if the borrow represented by `kind` is
@ -135,3 +136,38 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
// Any errors will be caught on the initial borrow
!place.is_indirect()
}
/// If `place` is a field projection, and the field is being projected from a closure type,
/// then returns the index of the field being projected. Note that this closure will always
/// be `self` in the current MIR, because that is the only time we directly access the fields
/// of a closure type.
pub(crate) fn is_upvar_field_projection(
tcx: TyCtxt<'tcx>,
upvars: &[Upvar],
place_ref: PlaceRef<'tcx>,
body: &Body<'tcx>,
) -> Option<Field> {
let mut place_projection = place_ref.projection;
let mut by_ref = false;
if let [proj_base @ .., ProjectionElem::Deref] = place_projection {
place_projection = proj_base;
by_ref = true;
}
match place_projection {
[base @ .., ProjectionElem::Field(field, _ty)] => {
let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty;
if (base_ty.is_closure() || base_ty.is_generator())
&& (!by_ref || upvars[field.index()].by_ref)
{
Some(*field)
} else {
None
}
}
_ => None,
}
}

View File

@ -12,7 +12,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}
use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin};
use rustc_middle::mir::{
Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
ConstraintCategory, Local, Location,
ConstraintCategory, Local, Location, ReturnConstraint,
};
use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable};
use rustc_span::Span;
@ -2017,7 +2017,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
| ConstraintCategory::BoringNoLocation
| ConstraintCategory::Internal => false,
ConstraintCategory::TypeAnnotation
| ConstraintCategory::Return
| ConstraintCategory::Return(_)
| ConstraintCategory::Yield => true,
_ => constraint_sup_scc != target_scc,
}
@ -2042,7 +2042,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
if let Some(i) = best_choice {
if let Some(next) = categorized_path.get(i + 1) {
if categorized_path[i].0 == ConstraintCategory::Return
if matches!(categorized_path[i].0, ConstraintCategory::Return(_))
&& next.0 == ConstraintCategory::OpaqueType
{
// The return expression is being influenced by the return type being
@ -2050,6 +2050,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
return *next;
}
}
if categorized_path[i].0 == ConstraintCategory::Return(ReturnConstraint::Normal) {
let field = categorized_path.iter().find_map(|p| {
if let ConstraintCategory::ClosureUpvar(f) = p.0 { Some(f) } else { None }
});
if let Some(field) = field {
categorized_path[i].0 =
ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field));
}
}
return categorized_path[i];
}

View File

@ -55,6 +55,7 @@ use crate::borrow_check::{
location::LocationTable,
member_constraints::MemberConstraintSet,
nll::ToRegionVid,
path_utils,
region_infer::values::{
LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements,
},
@ -62,6 +63,7 @@ use crate::borrow_check::{
renumber,
type_check::free_region_relations::{CreateResult, UniversalRegionRelations},
universal_regions::{DefiningTy, UniversalRegions},
Upvar,
};
macro_rules! span_mirbug {
@ -132,6 +134,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
move_data: &MoveData<'tcx>,
elements: &Rc<RegionValueElements>,
upvars: &[Upvar],
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
let mut constraints = MirTypeckRegionConstraints {
@ -162,6 +165,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
borrow_set,
all_facts,
constraints: &mut constraints,
upvars,
};
let opaque_type_values = type_check_internal(
@ -577,7 +581,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
for constraint in constraints.outlives().iter() {
let mut constraint = *constraint;
constraint.locations = locations;
if let ConstraintCategory::Return
if let ConstraintCategory::Return(_)
| ConstraintCategory::UseAsConst
| ConstraintCategory::UseAsStatic = constraint.category
{
@ -827,6 +831,7 @@ struct BorrowCheckContext<'a, 'tcx> {
all_facts: &'a mut Option<AllFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
upvars: &'a [Upvar],
}
crate struct MirTypeckResults<'tcx> {
@ -1420,7 +1425,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return
ConstraintCategory::Return(ReturnConstraint::Normal)
}
}
Some(l) if !body.local_decls[l].is_user_variable() => {
@ -1703,7 +1708,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return
ConstraintCategory::Return(ReturnConstraint::Normal)
}
}
Some(l) if !body.local_decls[l].is_user_variable() => {
@ -2489,6 +2494,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
let mut cursor = borrowed_place.projection.as_ref();
let tcx = self.infcx.tcx;
let field = path_utils::is_upvar_field_projection(
tcx,
&self.borrowck_context.upvars,
borrowed_place.as_ref(),
body,
);
let category = if let Some(field) = field {
ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id)
} else {
ConstraintCategory::Boring
};
while let [proj_base @ .., elem] = cursor {
cursor = proj_base;
@ -2496,7 +2514,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
match elem {
ProjectionElem::Deref => {
let tcx = self.infcx.tcx;
let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty;
debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
@ -2506,7 +2523,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
sup: ref_region.to_region_vid(),
sub: borrow_region.to_region_vid(),
locations: location.to_locations(),
category: ConstraintCategory::Boring,
category,
});
match mutbl {

View File

@ -12,6 +12,7 @@ use rustc_middle::ty::{self, TyCtxt};
pub use self::qualifs::Qualif;
mod ops;
pub mod post_drop_elaboration;
pub mod qualifs;
mod resolver;
pub mod validation;

View File

@ -10,6 +10,22 @@ use rustc_span::{Span, Symbol};
use super::ConstCx;
/// Emits an error if `op` is not allowed in the given const context.
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
debug!("illegal_op: op={:?}", op);
if op.is_allowed_in_item(ccx) {
return;
}
if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
return;
}
op.emit_error(ccx, span);
}
/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
/// Returns the `Symbol` corresponding to the feature gate that would enable this operation,

View File

@ -0,0 +1,119 @@
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, BasicBlock, Location};
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
use super::ops;
use super::qualifs::{NeedsDrop, Qualif};
use super::validation::Qualifs;
use super::ConstCx;
/// Returns `true` if we should use the more precise live drop checker that runs after drop
/// elaboration.
pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool {
tcx.features().const_precise_live_drops
}
/// Look for live drops in a const context.
///
/// This is separate from the rest of the const checking logic because it must run after drop
/// elaboration.
pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<'tcx>) {
let const_kind = tcx.hir().body_const_context(def_id);
if const_kind.is_none() {
return;
}
if !checking_enabled(tcx) {
return;
}
let ccx = ConstCx {
body,
tcx,
def_id: def_id.to_def_id(),
const_kind,
param_env: tcx.param_env(def_id),
};
let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
visitor.visit_body(body);
}
struct CheckLiveDrops<'mir, 'tcx> {
ccx: &'mir ConstCx<'mir, 'tcx>,
qualifs: Qualifs<'mir, 'tcx>,
}
// So we can access `body` and `tcx`.
impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
type Target = ConstCx<'mir, 'tcx>;
fn deref(&self) -> &Self::Target {
&self.ccx
}
}
impl CheckLiveDrops<'mir, 'tcx> {
fn check_live_drop(&self, span: Span) {
ops::non_const(self.ccx, ops::LiveDrop, span);
}
}
impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) {
trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
// Ignore drop terminators in cleanup blocks.
if block.is_cleanup {
return;
}
self.super_basic_block_data(bb, block);
}
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
match &terminator.kind {
mir::TerminatorKind::Drop { location: dropped_place, .. } => {
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
return;
}
if dropped_place.is_indirect() {
self.check_live_drop(terminator.source_info.span);
return;
}
if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) {
// Use the span where the dropped local was declared for the error.
let span = self.body.local_decls[dropped_place.local].source_info.span;
self.check_live_drop(span);
}
}
mir::TerminatorKind::DropAndReplace { .. } => span_bug!(
terminator.source_info.span,
"`DropAndReplace` should be removed by drop elaboration",
),
mir::TerminatorKind::Abort
| mir::TerminatorKind::Call { .. }
| mir::TerminatorKind::Assert { .. }
| mir::TerminatorKind::FalseEdge { .. }
| mir::TerminatorKind::FalseUnwind { .. }
| mir::TerminatorKind::GeneratorDrop
| mir::TerminatorKind::Goto { .. }
| mir::TerminatorKind::InlineAsm { .. }
| mir::TerminatorKind::Resume
| mir::TerminatorKind::Return
| mir::TerminatorKind::SwitchInt { .. }
| mir::TerminatorKind::Unreachable
| mir::TerminatorKind::Yield { .. } => {}
}
}
}

View File

@ -40,7 +40,7 @@ pub struct Qualifs<'mir, 'tcx> {
}
impl Qualifs<'mir, 'tcx> {
fn indirectly_mutable(
pub fn indirectly_mutable(
&mut self,
ccx: &'mir ConstCx<'mir, 'tcx>,
local: Local,
@ -68,7 +68,7 @@ impl Qualifs<'mir, 'tcx> {
/// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
///
/// Only updates the cursor if absolutely necessary
fn needs_drop(
pub fn needs_drop(
&mut self,
ccx: &'mir ConstCx<'mir, 'tcx>,
local: Local,
@ -95,7 +95,7 @@ impl Qualifs<'mir, 'tcx> {
/// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
///
/// Only updates the cursor if absolutely necessary.
fn has_mut_interior(
pub fn has_mut_interior(
&mut self,
ccx: &'mir ConstCx<'mir, 'tcx>,
local: Local,
@ -232,30 +232,15 @@ impl Validator<'mir, 'tcx> {
self.qualifs.in_return_place(self.ccx)
}
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
/// context.
pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
where
O: NonConstOp,
{
debug!("check_op: op={:?}", op);
if op.is_allowed_in_item(self) {
return;
}
if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
self.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
return;
}
op.emit_error(self, span);
}
/// Emits an error if an expression cannot be evaluated in the current context.
pub fn check_op(&mut self, op: impl NonConstOp) {
let span = self.span;
self.check_op_spanned(op, span)
ops::non_const(self.ccx, op, self.span);
}
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
/// context.
pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) {
ops::non_const(self.ccx, op, span);
}
fn check_static(&mut self, def_id: DefId, span: Span) {
@ -577,6 +562,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
// projections that cannot be `NeedsDrop`.
TerminatorKind::Drop { location: dropped_place, .. }
| TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
// If we are checking live drops after drop-elaboration, don't emit duplicate
// errors here.
if super::post_drop_elaboration::checking_enabled(self.tcx) {
return;
}
let mut err_span = self.span;
// Check to see if the type of this place can ever have a drop impl. If not, this

View File

@ -511,6 +511,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// This is basically `force_bits`.
let r_bits = r_bits.and_then(|r| r.to_bits_or_ptr(right_size, &self.tcx).ok());
if r_bits.map_or(false, |b| b >= left_size_bits as u128) {
debug!("check_binary_op: reporting assert for {:?}", source_info);
self.report_assert_as_lint(
lint::builtin::ARITHMETIC_OVERFLOW,
source_info,

View File

@ -49,6 +49,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) {
mir_const,
mir_const_qualif,
mir_validated,
mir_drops_elaborated_and_const_checked,
optimized_mir,
is_mir_available,
promoted_mir,
@ -294,12 +295,31 @@ fn mir_validated(
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
}
fn run_optimization_passes<'tcx>(
fn mir_drops_elaborated_and_const_checked<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> Steal<Body<'tcx>> {
// (Mir-)Borrowck uses `mir_validated`, so we have to force it to
// execute before we can steal.
tcx.ensure().mir_borrowck(def_id);
let (body, _) = tcx.mir_validated(def_id);
let mut body = body.steal();
run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, None);
check_consts::post_drop_elaboration::check_live_drops(tcx, def_id, &body);
tcx.alloc_steal_mir(body)
}
/// After this series of passes, no lifetime analysis based on borrowing can be done.
fn run_post_borrowck_cleanup_passes<'tcx>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
def_id: LocalDefId,
promoted: Option<Promoted>,
) {
debug!("post_borrowck_cleanup({:?})", def_id);
let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
// Remove all things only needed by analysis
&no_landing_pads::NoLandingPads::new(tcx),
@ -318,9 +338,24 @@ fn run_optimization_passes<'tcx>(
// but before optimizations begin.
&add_retag::AddRetag,
&simplify::SimplifyCfg::new("elaborate-drops"),
// No lifetime analysis based on borrowing can be done from here on out.
];
run_passes(
tcx,
body,
InstanceDef::Item(def_id.to_def_id()),
promoted,
MirPhase::DropElab,
&[post_borrowck_cleanup],
);
}
fn run_optimization_passes<'tcx>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
def_id: LocalDefId,
promoted: Option<Promoted>,
) {
let optimizations: &[&dyn MirPass<'tcx>] = &[
&unreachable_prop::UnreachablePropagation,
&uninhabited_enum_branching::UninhabitedEnumBranching,
@ -368,6 +403,7 @@ fn run_optimization_passes<'tcx>(
let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level;
#[rustfmt::skip]
run_passes(
tcx,
body,
@ -375,7 +411,6 @@ fn run_optimization_passes<'tcx>(
promoted,
MirPhase::Optimized,
&[
post_borrowck_cleanup,
if mir_opt_level > 0 { optimizations } else { no_optimizations },
pre_codegen_cleanup,
],
@ -393,12 +428,7 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
let def_id = def_id.expect_local();
// (Mir-)Borrowck uses `mir_validated`, so we have to force it to
// execute before we can steal.
tcx.ensure().mir_borrowck(def_id);
let (body, _) = tcx.mir_validated(def_id);
let mut body = body.steal();
let mut body = tcx.mir_drops_elaborated_and_const_checked(def_id).steal();
run_optimization_passes(tcx, &mut body, def_id, None);
debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
@ -418,6 +448,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> IndexVec<Promoted, Body<'_>>
let mut promoted = promoted.steal();
for (p, mut body) in promoted.iter_enumerated_mut() {
run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, Some(p));
run_optimization_passes(tcx, &mut body, def_id, Some(p));
}

View File

@ -1,274 +1,274 @@
/// Note: most of the tests relevant to this file can be found (at the time of writing) in
/// src/tests/ui/pattern/usefulness.
///
/// This file includes the logic for exhaustiveness and usefulness checking for
/// pattern-matching. Specifically, given a list of patterns for a type, we can
/// tell whether:
/// (a) the patterns cover every possible constructor for the type [exhaustiveness]
/// (b) each pattern is necessary [usefulness]
///
/// The algorithm implemented here is a modified version of the one described in:
/// http://moscova.inria.fr/~maranget/papers/warn/index.html
/// However, to save future implementors from reading the original paper, we
/// summarise the algorithm here to hopefully save time and be a little clearer
/// (without being so rigorous).
///
/// # Premise
///
/// The core of the algorithm revolves about a "usefulness" check. In particular, we
/// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
/// a matrix). `U(P, p)` represents whether, given an existing list of patterns
/// `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously-
/// uncovered values of the type).
///
/// If we have this predicate, then we can easily compute both exhaustiveness of an
/// entire set of patterns and the individual usefulness of each one.
/// (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard
/// match doesn't increase the number of values we're matching)
/// (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a
/// pattern to those that have come before it doesn't increase the number of values
/// we're matching).
///
/// # Core concept
///
/// The idea that powers everything that is done in this file is the following: a value is made
/// from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)`
/// (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the
/// constructor for the number `2`). Fields are just a (possibly empty) list of values.
///
/// Some of the constructors listed above might feel weird: `None` and `2` don't take any
/// arguments. This is part of what makes constructors so general: we will consider plain values
/// like numbers and string literals to be constructors that take no arguments, also called "0-ary
/// constructors"; they are the simplest case of constructors. This allows us to see any value as
/// made up from a tree of constructors, each having a given number of children. For example:
/// `(None, Ok(0))` is made from 4 different constructors.
///
/// This idea can be extended to patterns: a pattern captures a set of possible values, and we can
/// describe this set using constructors. For example, `Err(_)` captures all values of the type
/// `Result<T, E>` that start with the `Err` constructor (for some choice of `T` and `E`). The
/// wildcard `_` captures all values of the given type starting with any of the constructors for
/// that type.
///
/// We use this to compute whether different patterns might capture a same value. Do the patterns
/// `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern
/// captures only values starting with the `Ok` constructor and the second only values starting
/// with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might,
/// since they both capture values starting with `Some`. To be certain, we need to dig under the
/// `Some` constructor and continue asking the question. This is the main idea behind the
/// exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently
/// figure out if some new pattern might capture a value that hadn't been captured by previous
/// patterns.
///
/// Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum.
/// Most of the complexity of this file resides in transforming between patterns and
/// (`Constructor`, `Fields`) pairs, handling all the special cases correctly.
///
/// Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example
/// a value of type `Rc<u64>` doesn't fit this idea very well, nor do various other things.
/// However, this idea covers most of the cases that are relevant to exhaustiveness checking.
///
///
/// # Algorithm
///
/// Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`,
/// adding a new pattern `p` will cover previously-uncovered values of the type.
/// During the course of the algorithm, the rows of the matrix won't just be individual patterns,
/// but rather partially-deconstructed patterns in the form of a list of fields. The paper
/// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
/// new pattern `p`.
///
/// For example, say we have the following:
/// ```
/// // x: (Option<bool>, Result<()>)
/// match x {
/// (Some(true), _) => {}
/// (None, Err(())) => {}
/// (None, Err(_)) => {}
/// }
/// ```
/// Here, the matrix `P` starts as:
/// [
/// [(Some(true), _)],
/// [(None, Err(()))],
/// [(None, Err(_))],
/// ]
/// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
/// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
/// all the values it covers are already covered by row 2.
///
/// A list of patterns can be thought of as a stack, because we are mainly interested in the top of
/// the stack at any given point, and we can pop or apply constructors to get new pattern-stacks.
/// To match the paper, the top of the stack is at the beginning / on the left.
///
/// There are two important operations on pattern-stacks necessary to understand the algorithm:
/// 1. We can pop a given constructor off the top of a stack. This operation is called
/// `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
/// `None`) and `p` a pattern-stack.
/// If the pattern on top of the stack can cover `c`, this removes the constructor and
/// pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
/// Otherwise the pattern-stack is discarded.
/// This essentially filters those pattern-stacks whose top covers the constructor `c` and
/// discards the others.
///
/// For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
/// pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
/// `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
/// nothing back.
///
/// This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
/// on top of the stack, and we have four cases:
/// 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
/// push onto the stack the arguments of this constructor, and return the result:
/// r_1, .., r_a, p_2, .., p_n
/// 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
/// return nothing.
/// 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
/// arguments (its arity), and return the resulting stack:
/// _, .., _, p_2, .., p_n
/// 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
/// stack:
/// S(c, (r_1, p_2, .., p_n))
/// S(c, (r_2, p_2, .., p_n))
///
/// 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
/// a pattern-stack.
/// This is used when we know there are missing constructor cases, but there might be
/// existing wildcard patterns, so to check the usefulness of the matrix, we have to check
/// all its *other* components.
///
/// It is computed as follows. We look at the pattern `p_1` on top of the stack,
/// and we have three cases:
/// 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
/// 1.2. `p_1 = _`. We return the rest of the stack:
/// p_2, .., p_n
/// 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
/// stack.
/// D((r_1, p_2, .., p_n))
/// D((r_2, p_2, .., p_n))
///
/// Note that the OR-patterns are not always used directly in Rust, but are used to derive the
/// exhaustive integer matching rules, so they're written here for posterity.
///
/// Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
/// working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
/// the given constructor, and popping a wildcard keeps those rows that start with a wildcard.
///
///
/// The algorithm for computing `U`
/// -------------------------------
/// The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
/// That means we're going to check the components from left-to-right, so the algorithm
/// operates principally on the first component of the matrix and new pattern-stack `p`.
/// This algorithm is realised in the `is_useful` function.
///
/// Base case. (`n = 0`, i.e., an empty tuple pattern)
/// - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`),
/// then `U(P, p)` is false.
/// - Otherwise, `P` must be empty, so `U(P, p)` is true.
///
/// Inductive step. (`n > 0`, i.e., whether there's at least one column
/// [which may then be expanded into further columns later])
/// We're going to match on the top of the new pattern-stack, `p_1`.
/// - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
/// Then, the usefulness of `p_1` can be reduced to whether it is useful when
/// we ignore all the patterns in the first column of `P` that involve other constructors.
/// This is where `S(c, P)` comes in:
/// `U(P, p) := U(S(c, P), S(c, p))`
/// This special case is handled in `is_useful_specialized`.
///
/// For example, if `P` is:
/// [
/// [Some(true), _],
/// [None, 0],
/// ]
/// and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
/// matches values that row 2 doesn't. For row 1 however, we need to dig into the
/// arguments of `Some` to know whether some new value is covered. So we compute
/// `U([[true, _]], [false, 0])`.
///
/// - If `p_1 == _`, then we look at the list of constructors that appear in the first
/// component of the rows of `P`:
/// + If there are some constructors that aren't present, then we might think that the
/// wildcard `_` is useful, since it covers those constructors that weren't covered
/// before.
/// That's almost correct, but only works if there were no wildcards in those first
/// components. So we need to check that `p` is useful with respect to the rows that
/// start with a wildcard, if there are any. This is where `D` comes in:
/// `U(P, p) := U(D(P), D(p))`
///
/// For example, if `P` is:
/// [
/// [_, true, _],
/// [None, false, 1],
/// ]
/// and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we
/// only had row 2, we'd know that `p` is useful. However row 1 starts with a
/// wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
///
/// + Otherwise, all possible constructors (for the relevant type) are present. In this
/// case we must check whether the wildcard pattern covers any unmatched value. For
/// that, we can think of the `_` pattern as a big OR-pattern that covers all
/// possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
/// example. The wildcard pattern is useful in this case if it is useful when
/// specialized to one of the possible constructors. So we compute:
/// `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
///
/// For example, if `P` is:
/// [
/// [Some(true), _],
/// [None, false],
/// ]
/// and `p` is [_, false], both `None` and `Some` constructors appear in the first
/// components of `P`. We will therefore try popping both constructors in turn: we
/// compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]],
/// [false]) for the `None` constructor. The first case returns true, so we know that
/// `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
/// before.
///
/// - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
/// `U(P, p) := U(P, (r_1, p_2, .., p_n))
/// || U(P, (r_2, p_2, .., p_n))`
///
/// Modifications to the algorithm
/// ------------------------------
/// The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for
/// example uninhabited types and variable-length slice patterns. These are drawn attention to
/// throughout the code below. I'll make a quick note here about how exhaustive integer matching is
/// accounted for, though.
///
/// Exhaustive integer matching
/// ---------------------------
/// An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ...
/// So to support exhaustive integer matching, we can make use of the logic in the paper for
/// OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because
/// they are likely gigantic. So we instead treat ranges as constructors of the integers. This means
/// that we have a constructor *of* constructors (the integers themselves). We then need to work
/// through all the inductive step rules above, deriving how the ranges would be treated as
/// OR-patterns, and making sure that they're treated in the same way even when they're ranges.
/// There are really only four special cases here:
/// - When we match on a constructor that's actually a range, we have to treat it as if we would
/// an OR-pattern.
/// + It turns out that we can simply extend the case for single-value patterns in
/// `specialize` to either be *equal* to a value constructor, or *contained within* a range
/// constructor.
/// + When the pattern itself is a range, you just want to tell whether any of the values in
/// the pattern range coincide with values in the constructor range, which is precisely
/// intersection.
/// Since when encountering a range pattern for a value constructor, we also use inclusion, it
/// means that whenever the constructor is a value/range and the pattern is also a value/range,
/// we can simply use intersection to test usefulness.
/// - When we're testing for usefulness of a pattern and the pattern's first component is a
/// wildcard.
/// + If all the constructors appear in the matrix, we have a slight complication. By default,
/// the behaviour (i.e., a disjunction over specialised matrices for each constructor) is
/// invalid, because we want a disjunction over every *integer* in each range, not just a
/// disjunction over every range. This is a bit more tricky to deal with: essentially we need
/// to form equivalence classes of subranges of the constructor range for which the behaviour
/// of the matrix `P` and new pattern `p` are the same. This is described in more
/// detail in `split_grouped_constructors`.
/// + If some constructors are missing from the matrix, it turns out we don't need to do
/// anything special (because we know none of the integers are actually wildcards: i.e., we
/// can't span wildcards using ranges).
//! Note: most of the tests relevant to this file can be found (at the time of writing) in
//! src/tests/ui/pattern/usefulness.
//!
//! This file includes the logic for exhaustiveness and usefulness checking for
//! pattern-matching. Specifically, given a list of patterns for a type, we can
//! tell whether:
//! (a) the patterns cover every possible constructor for the type [exhaustiveness]
//! (b) each pattern is necessary [usefulness]
//!
//! The algorithm implemented here is a modified version of the one described in:
//! http://moscova.inria.fr/~maranget/papers/warn/index.html
//! However, to save future implementors from reading the original paper, we
//! summarise the algorithm here to hopefully save time and be a little clearer
//! (without being so rigorous).
//!
//! # Premise
//!
//! The core of the algorithm revolves about a "usefulness" check. In particular, we
//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
//! a matrix). `U(P, p)` represents whether, given an existing list of patterns
//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously-
//! uncovered values of the type).
//!
//! If we have this predicate, then we can easily compute both exhaustiveness of an
//! entire set of patterns and the individual usefulness of each one.
//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard
//! match doesn't increase the number of values we're matching)
//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a
//! pattern to those that have come before it doesn't increase the number of values
//! we're matching).
//!
//! # Core concept
//!
//! The idea that powers everything that is done in this file is the following: a value is made
//! from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)`
//! (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the
//! constructor for the number `2`). Fields are just a (possibly empty) list of values.
//!
//! Some of the constructors listed above might feel weird: `None` and `2` don't take any
//! arguments. This is part of what makes constructors so general: we will consider plain values
//! like numbers and string literals to be constructors that take no arguments, also called "0-ary
//! constructors"; they are the simplest case of constructors. This allows us to see any value as
//! made up from a tree of constructors, each having a given number of children. For example:
//! `(None, Ok(0))` is made from 4 different constructors.
//!
//! This idea can be extended to patterns: a pattern captures a set of possible values, and we can
//! describe this set using constructors. For example, `Err(_)` captures all values of the type
//! `Result<T, E>` that start with the `Err` constructor (for some choice of `T` and `E`). The
//! wildcard `_` captures all values of the given type starting with any of the constructors for
//! that type.
//!
//! We use this to compute whether different patterns might capture a same value. Do the patterns
//! `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern
//! captures only values starting with the `Ok` constructor and the second only values starting
//! with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might,
//! since they both capture values starting with `Some`. To be certain, we need to dig under the
//! `Some` constructor and continue asking the question. This is the main idea behind the
//! exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently
//! figure out if some new pattern might capture a value that hadn't been captured by previous
//! patterns.
//!
//! Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum.
//! Most of the complexity of this file resides in transforming between patterns and
//! (`Constructor`, `Fields`) pairs, handling all the special cases correctly.
//!
//! Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example
//! a value of type `Rc<u64>` doesn't fit this idea very well, nor do various other things.
//! However, this idea covers most of the cases that are relevant to exhaustiveness checking.
//!
//!
//! # Algorithm
//!
//! Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`,
//! adding a new pattern `p` will cover previously-uncovered values of the type.
//! During the course of the algorithm, the rows of the matrix won't just be individual patterns,
//! but rather partially-deconstructed patterns in the form of a list of fields. The paper
//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
//! new pattern `p`.
//!
//! For example, say we have the following:
//! ```
//! // x: (Option<bool>, Result<()>)
//! match x {
//! (Some(true), _) => {}
//! (None, Err(())) => {}
//! (None, Err(_)) => {}
//! }
//! ```
//! Here, the matrix `P` starts as:
//! [
//! [(Some(true), _)],
//! [(None, Err(()))],
//! [(None, Err(_))],
//! ]
//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
//! all the values it covers are already covered by row 2.
//!
//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of
//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks.
//! To match the paper, the top of the stack is at the beginning / on the left.
//!
//! There are two important operations on pattern-stacks necessary to understand the algorithm:
//! 1. We can pop a given constructor off the top of a stack. This operation is called
//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
//! `None`) and `p` a pattern-stack.
//! If the pattern on top of the stack can cover `c`, this removes the constructor and
//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
//! Otherwise the pattern-stack is discarded.
//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
//! discards the others.
//!
//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
//! nothing back.
//!
//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
//! on top of the stack, and we have four cases:
//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
//! push onto the stack the arguments of this constructor, and return the result:
//! r_1, .., r_a, p_2, .., p_n
//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
//! return nothing.
//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
//! arguments (its arity), and return the resulting stack:
//! _, .., _, p_2, .., p_n
//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
//! stack:
//! S(c, (r_1, p_2, .., p_n))
//! S(c, (r_2, p_2, .., p_n))
//!
//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
//! a pattern-stack.
//! This is used when we know there are missing constructor cases, but there might be
//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
//! all its *other* components.
//!
//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
//! and we have three cases:
//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
//! 1.2. `p_1 = _`. We return the rest of the stack:
//! p_2, .., p_n
//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
//! stack.
//! D((r_1, p_2, .., p_n))
//! D((r_2, p_2, .., p_n))
//!
//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
//! exhaustive integer matching rules, so they're written here for posterity.
//!
//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard.
//!
//!
//! The algorithm for computing `U`
//! -------------------------------
//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
//! That means we're going to check the components from left-to-right, so the algorithm
//! operates principally on the first component of the matrix and new pattern-stack `p`.
//! This algorithm is realised in the `is_useful` function.
//!
//! Base case. (`n = 0`, i.e., an empty tuple pattern)
//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`),
//! then `U(P, p)` is false.
//! - Otherwise, `P` must be empty, so `U(P, p)` is true.
//!
//! Inductive step. (`n > 0`, i.e., whether there's at least one column
//! [which may then be expanded into further columns later])
//! We're going to match on the top of the new pattern-stack, `p_1`.
//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
//! we ignore all the patterns in the first column of `P` that involve other constructors.
//! This is where `S(c, P)` comes in:
//! `U(P, p) := U(S(c, P), S(c, p))`
//! This special case is handled in `is_useful_specialized`.
//!
//! For example, if `P` is:
//! [
//! [Some(true), _],
//! [None, 0],
//! ]
//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
//! arguments of `Some` to know whether some new value is covered. So we compute
//! `U([[true, _]], [false, 0])`.
//!
//! - If `p_1 == _`, then we look at the list of constructors that appear in the first
//! component of the rows of `P`:
//! + If there are some constructors that aren't present, then we might think that the
//! wildcard `_` is useful, since it covers those constructors that weren't covered
//! before.
//! That's almost correct, but only works if there were no wildcards in those first
//! components. So we need to check that `p` is useful with respect to the rows that
//! start with a wildcard, if there are any. This is where `D` comes in:
//! `U(P, p) := U(D(P), D(p))`
//!
//! For example, if `P` is:
//! [
//! [_, true, _],
//! [None, false, 1],
//! ]
//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we
//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
//!
//! + Otherwise, all possible constructors (for the relevant type) are present. In this
//! case we must check whether the wildcard pattern covers any unmatched value. For
//! that, we can think of the `_` pattern as a big OR-pattern that covers all
//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
//! example. The wildcard pattern is useful in this case if it is useful when
//! specialized to one of the possible constructors. So we compute:
//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
//!
//! For example, if `P` is:
//! [
//! [Some(true), _],
//! [None, false],
//! ]
//! and `p` is [_, false], both `None` and `Some` constructors appear in the first
//! components of `P`. We will therefore try popping both constructors in turn: we
//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]],
//! [false]) for the `None` constructor. The first case returns true, so we know that
//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
//! before.
//!
//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
//! `U(P, p) := U(P, (r_1, p_2, .., p_n))
//! || U(P, (r_2, p_2, .., p_n))`
//!
//! Modifications to the algorithm
//! ------------------------------
//! The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for
//! example uninhabited types and variable-length slice patterns. These are drawn attention to
//! throughout the code below. I'll make a quick note here about how exhaustive integer matching is
//! accounted for, though.
//!
//! Exhaustive integer matching
//! ---------------------------
//! An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ...
//! So to support exhaustive integer matching, we can make use of the logic in the paper for
//! OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because
//! they are likely gigantic. So we instead treat ranges as constructors of the integers. This means
//! that we have a constructor *of* constructors (the integers themselves). We then need to work
//! through all the inductive step rules above, deriving how the ranges would be treated as
//! OR-patterns, and making sure that they're treated in the same way even when they're ranges.
//! There are really only four special cases here:
//! - When we match on a constructor that's actually a range, we have to treat it as if we would
//! an OR-pattern.
//! + It turns out that we can simply extend the case for single-value patterns in
//! `specialize` to either be *equal* to a value constructor, or *contained within* a range
//! constructor.
//! + When the pattern itself is a range, you just want to tell whether any of the values in
//! the pattern range coincide with values in the constructor range, which is precisely
//! intersection.
//! Since when encountering a range pattern for a value constructor, we also use inclusion, it
//! means that whenever the constructor is a value/range and the pattern is also a value/range,
//! we can simply use intersection to test usefulness.
//! - When we're testing for usefulness of a pattern and the pattern's first component is a
//! wildcard.
//! + If all the constructors appear in the matrix, we have a slight complication. By default,
//! the behaviour (i.e., a disjunction over specialised matrices for each constructor) is
//! invalid, because we want a disjunction over every *integer* in each range, not just a
//! disjunction over every range. This is a bit more tricky to deal with: essentially we need
//! to form equivalence classes of subranges of the constructor range for which the behaviour
//! of the matrix `P` and new pattern `p` are the same. This is described in more
//! detail in `split_grouped_constructors`.
//! + If some constructors are missing from the matrix, it turns out we don't need to do
//! anything special (because we know none of the integers are actually wildcards: i.e., we
//! can't span wildcards using ranges).
use self::Constructor::*;
use self::SliceKind::*;
use self::Usefulness::*;

View File

@ -130,6 +130,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
traits::NonStructuralMatchTy::Generator => {
"generators cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTy::Closure => {
"closures cannot be used in patterns".to_string()
}
traits::NonStructuralMatchTy::Param => {
bug!("use of a constant whose type is a parameter inside a pattern")
}

View File

@ -822,7 +822,15 @@ pub enum DesugaringKind {
OpaqueTy,
Async,
Await,
ForLoop,
ForLoop(ForLoopLoc),
Operator,
}
/// A location in the desugaring of a `for` loop
#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)]
pub enum ForLoopLoc {
Head,
IntoIter,
}
impl DesugaringKind {
@ -835,7 +843,8 @@ impl DesugaringKind {
DesugaringKind::QuestionMark => "operator `?`",
DesugaringKind::TryBlock => "`try` block",
DesugaringKind::OpaqueTy => "`impl Trait`",
DesugaringKind::ForLoop => "`for` loop",
DesugaringKind::ForLoop(_) => "`for` loop",
DesugaringKind::Operator => "operator",
}
}
}

View File

@ -31,7 +31,9 @@ pub mod edition;
use edition::Edition;
pub mod hygiene;
use hygiene::Transparency;
pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, MacroKind, SyntaxContext};
pub use hygiene::{
DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind, SyntaxContext,
};
pub mod def_id;
use def_id::{CrateNum, DefId, LOCAL_CRATE};
mod span_encoding;

View File

@ -227,6 +227,7 @@ symbols! {
const_loop,
const_mut_refs,
const_panic,
const_precise_live_drops,
const_raw_ptr_deref,
const_raw_ptr_to_usize_cast,
const_transmute,

View File

@ -18,6 +18,7 @@ pub enum NonStructuralMatchTy<'tcx> {
Opaque,
Generator,
Projection,
Closure,
}
/// This method traverses the structure of `ty`, trying to find an
@ -162,6 +163,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
self.found = Some(NonStructuralMatchTy::Generator);
return true; // Stop visiting.
}
ty::Closure(..) => {
self.found = Some(NonStructuralMatchTy::Closure);
return true; // Stop visiting.
}
ty::RawPtr(..) => {
// structural-match ignores substructure of
// `*const _`/`*mut _`, so skip `super_visit_with`.
@ -211,7 +216,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
ty.super_visit_with(self);
return false;
}
ty::Closure(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
bug!("unexpected type during structural-match checking: {:?}", ty);
}
ty::Error => {

View File

@ -795,6 +795,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) {
// FIXME: do we want to commit to this behavior for param bounds?
debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty);
let bounds =
self.param_env.caller_bounds.iter().filter_map(|predicate| match predicate.kind() {
@ -952,7 +953,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
import_ids: import_ids.clone(),
kind: TraitCandidate(new_trait_ref),
},
true,
false,
);
});
} else {

View File

@ -0,0 +1,22 @@
// Regression test for issue #69446 - we should display
// which variable is captured
// edition:2018
use core::future::Future;
struct Foo;
impl Foo {
fn foo(&mut self) {}
}
async fn bar<T>(_: impl FnMut() -> T)
where
T: Future<Output = ()>,
{}
fn main() {
let mut x = Foo;
bar(move || async { //~ ERROR captured
x.foo();
});
}

View File

@ -0,0 +1,19 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/issue-69446-fnmut-capture.rs:19:17
|
LL | let mut x = Foo;
| ----- variable defined here
LL | bar(move || async {
| _______________-_^
| | |
| | inferred to be a `FnMut` closure
LL | | x.foo();
| | - variable captured here
LL | | });
| |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
error: aborting due to previous error

View File

@ -4,10 +4,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn add<A: Add<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs + rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn add(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn add<A: Add<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -35,10 +40,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn sub<A: Sub<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs - rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn sub(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn sub<A: Sub<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -66,10 +76,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn mul<A: Mul<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs * rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn mul(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn mul<A: Mul<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -97,10 +112,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn div<A: Div<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs / rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn div(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn div<A: Div<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -128,10 +148,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn rem<A: Rem<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs % rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn rem(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn rem<A: Rem<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -159,10 +184,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn bitand<A: BitAnd<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs & rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/bit.rs:LL:COL
|
LL | fn bitand(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn bitand<A: BitAnd<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -190,10 +220,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn bitor<A: BitOr<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs | rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/bit.rs:LL:COL
|
LL | fn bitor(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn bitor<A: BitOr<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -221,10 +256,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn bitxor<A: BitXor<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs ^ rhs;
| --- value moved here
| --------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/bit.rs:LL:COL
|
LL | fn bitxor(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn bitxor<A: BitXor<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -252,10 +292,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn shl<A: Shl<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs << rhs;
| --- value moved here
| ---------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/bit.rs:LL:COL
|
LL | fn shl(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn shl<A: Shl<B, Output=()> + Copy, B>(lhs: A, rhs: B) {
@ -283,10 +328,15 @@ error[E0382]: use of moved value: `lhs`
LL | fn shr<A: Shr<B, Output=()>, B>(lhs: A, rhs: B) {
| --- move occurs because `lhs` has type `A`, which does not implement the `Copy` trait
LL | lhs >> rhs;
| --- value moved here
| ---------- `lhs` moved due to usage in operator
LL | drop(lhs);
| ^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/bit.rs:LL:COL
|
LL | fn shr(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn shr<A: Shr<B, Output=()> + Copy, B>(lhs: A, rhs: B) {

View File

@ -1,14 +1,21 @@
error[E0382]: use of moved value: `x`
--> $DIR/binop-move-semantics.rs:8:5
|
LL | fn double_move<T: Add<Output=()>>(x: T) {
| - move occurs because `x` has type `T`, which does not implement the `Copy` trait
LL | x
| - value moved here
LL | +
LL | x;
| ^ value used here after move
LL | fn double_move<T: Add<Output=()>>(x: T) {
| - move occurs because `x` has type `T`, which does not implement the `Copy` trait
LL | / x
LL | | +
LL | | x;
| | ^
| | |
| |_____value used here after move
| `x` moved due to usage in operator
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn add(self, rhs: Rhs) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn double_move<T: Add<Output=()> + Copy>(x: T) {

View File

@ -21,10 +21,13 @@ LL | *y = 1;
error: captured variable cannot escape `FnMut` closure body
--> $DIR/borrowck-describe-lvalue.rs:264:16
|
LL | let mut x = 0;
| ----- variable defined here
LL | || {
| - inferred to be a `FnMut` closure
LL | / || {
LL | | let y = &mut x;
| | - variable captured here
LL | | &mut x;
LL | | *y = 1;
LL | | drop(y);

View File

@ -22,10 +22,15 @@ error[E0382]: use of moved value: `f`
LL | fn c<F:FnOnce(isize, isize) -> isize>(f: F) {
| - move occurs because `f` has type `F`, which does not implement the `Copy` trait
LL | f(1, 2);
| - value moved here
| ------- `f` moved due to this call
LL | f(1, 2);
| ^ value used here after move
|
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/borrowck-unboxed-closures.rs:11:5
|
LL | f(1, 2);
| ^
help: consider further restricting this bound
|
LL | fn c<F:FnOnce(isize, isize) -> isize + Copy>(f: F) {

View File

@ -2,7 +2,7 @@ error[E0382]: use of moved value: `debug_dump_dict`
--> $DIR/issue-42065.rs:11:5
|
LL | debug_dump_dict();
| --------------- value moved here
| ----------------- `debug_dump_dict` moved due to this call
LL | debug_dump_dict();
| ^^^^^^^^^^^^^^^ value used here after move
|
@ -11,6 +11,11 @@ note: closure cannot be invoked more than once because it moves the variable `di
|
LL | for (key, value) in dict {
| ^^^^
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/issue-42065.rs:10:5
|
LL | debug_dump_dict();
| ^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -4,10 +4,16 @@ error[E0382]: borrow of moved value: `some_vec`
LL | let some_vec = vec!["hi"];
| -------- move occurs because `some_vec` has type `std::vec::Vec<&str>`, which does not implement the `Copy` trait
LL | some_vec.into_iter();
| -------- value moved here
| ----------- `some_vec` moved due to this method call
LL | {
LL | println!("{:?}", some_vec);
| ^^^^^^^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `some_vec`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
error: aborting due to previous error

View File

@ -0,0 +1,15 @@
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop-fail.rs:10:9
|
LL | let x = Some(Vec::new());
| ^ constants cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop-fail.rs:41:9
|
LL | let mut tmp = None;
| ^^^^^^^ constants cannot evaluate destructors
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0493`.

View File

@ -1,11 +1,14 @@
// revisions: stock precise
#![feature(const_if_match)]
#![feature(const_loop)]
#![cfg_attr(precise, feature(const_precise_live_drops))]
// `x` is *not* always moved into the final value may be dropped inside the initializer.
// `x` is *not* always moved into the final value and may be dropped inside the initializer.
const _: Option<Vec<i32>> = {
let y: Option<Vec<i32>> = None;
let x = Some(Vec::new());
//~^ ERROR destructors cannot be evaluated at compile-time
//[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
if true {
x
@ -18,7 +21,7 @@ const _: Option<Vec<i32>> = {
// existing analysis.
const _: Vec<i32> = {
let vec_tuple = (Vec::new(),);
//~^ ERROR destructors cannot be evaluated at compile-time
//[stock]~^ ERROR destructors cannot be evaluated at compile-time
vec_tuple.0
};
@ -26,7 +29,7 @@ const _: Vec<i32> = {
// This applies to single-field enum variants as well.
const _: Vec<i32> = {
let x: Result<_, Vec<i32>> = Ok(Vec::new());
//~^ ERROR destructors cannot be evaluated at compile-time
//[stock]~^ ERROR destructors cannot be evaluated at compile-time
match x {
Ok(x) | Err(x) => x,
@ -36,7 +39,7 @@ const _: Vec<i32> = {
const _: Option<Vec<i32>> = {
let mut some = Some(Vec::new());
let mut tmp = None;
//~^ ERROR destructors cannot be evaluated at compile-time
//[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
let mut i = 0;
while i < 10 {

View File

@ -1,23 +1,23 @@
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop-failure.rs:7:9
--> $DIR/drop-fail.rs:10:9
|
LL | let x = Some(Vec::new());
| ^ constants cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop-failure.rs:20:9
--> $DIR/drop-fail.rs:23:9
|
LL | let vec_tuple = (Vec::new(),);
| ^^^^^^^^^ constants cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop-failure.rs:28:9
--> $DIR/drop-fail.rs:31:9
|
LL | let x: Result<_, Vec<i32>> = Ok(Vec::new());
| ^ constants cannot evaluate destructors
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop-failure.rs:38:9
--> $DIR/drop-fail.rs:41:9
|
LL | let mut tmp = None;
| ^^^^^^^ constants cannot evaluate destructors

View File

@ -1,7 +1,9 @@
// run-pass
// revisions: stock precise
#![feature(const_if_match)]
#![feature(const_loop)]
#![cfg_attr(precise, feature(const_precise_live_drops))]
// `x` is always moved into the final value and is not dropped inside the initializer.
const _: Option<Vec<i32>> = {

View File

@ -0,0 +1,20 @@
// run-pass
// gate-test-const_precise_live_drops
#![feature(const_if_match)]
#![feature(const_loop)]
#![feature(const_precise_live_drops)]
const _: Vec<i32> = {
let vec_tuple = (Vec::new(),);
vec_tuple.0
};
const _: Vec<i32> = {
let x: Result<_, Vec<i32>> = Ok(Vec::new());
match x {
Ok(x) | Err(x) => x,
}
};
fn main() {}

View File

@ -6,14 +6,15 @@
static CMP: () = {
let x = &0 as *const _;
let _v = x == x;
let _v = x == x; //~ NOTE in this
//~^ ERROR could not evaluate static initializer
//~| NOTE pointer arithmetic or comparison
//~| NOTE in this
};
static INT_PTR_ARITH: () = unsafe {
let x: usize = std::mem::transmute(&0);
let _v = x + 0;
let _v = x + 0; //~ NOTE in this
//~^ ERROR could not evaluate static initializer
//~| NOTE pointer-to-integer cast
};

View File

@ -5,7 +5,7 @@ LL | let _v = x == x;
| ^^^^^^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
error[E0080]: could not evaluate static initializer
--> $DIR/ptr_arith.rs:16:14
--> $DIR/ptr_arith.rs:17:14
|
LL | let _v = x + 0;
| ^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants
@ -18,7 +18,7 @@ help: skipping check for `const_compare_raw_pointers` feature
LL | let _v = x == x;
| ^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/ptr_arith.rs:15:20
--> $DIR/ptr_arith.rs:16:20
|
LL | let x: usize = std::mem::transmute(&0);
| ^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -18,8 +18,12 @@ fn y /* 0#0 */() { }
Expansions:
0: parent: ExpnId(0), call_site_ctxt: #0, kind: Root
1: parent: ExpnId(0), call_site_ctxt: #0, kind: Macro(Bang, "foo")
2: parent: ExpnId(0), call_site_ctxt: #1, kind: Desugaring(Operator)
3: parent: ExpnId(0), call_site_ctxt: #1, kind: Desugaring(Operator)
SyntaxContexts:
#0: parent: #0, outer_mark: (ExpnId(0), Opaque)
#1: parent: #0, outer_mark: (ExpnId(1), SemiTransparent)
#2: parent: #1, outer_mark: (ExpnId(2), Transparent)
#3: parent: #1, outer_mark: (ExpnId(3), Transparent)
*/

View File

@ -0,0 +1,8 @@
// check-pass
#![feature(impl_trait_in_bindings)]
//~^ WARN the feature `impl_trait_in_bindings` is incomplete
const _: impl Fn() = ||();
fn main() {}

View File

@ -0,0 +1,11 @@
warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/impl-trait-in-bindings-issue-73003.rs:3:12
|
LL | #![feature(impl_trait_in_bindings)]
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information
warning: 1 warning emitted

View File

@ -2,10 +2,15 @@ error[E0382]: use of moved value: `f`
--> $DIR/issue-12127.rs:11:9
|
LL | f();
| - value moved here
| --- `f` moved due to this call
LL | f();
| ^ value used here after move
|
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/issue-12127.rs:10:9
|
LL | f();
| ^
= note: move occurs because `f` has type `[closure@$DIR/issue-12127.rs:8:24: 8:41 x:std::boxed::Box<isize>]`, which does not implement the `Copy` trait
error: aborting due to previous error

View File

@ -3,4 +3,5 @@ use std::collections::HashMap;
fn main() {
for _ in HashMap::new().iter().cloned() {} //~ ERROR type mismatch
//~^ ERROR type mismatch
//~| ERROR type mismatch
}

View File

@ -17,6 +17,16 @@ LL | for _ in HashMap::new().iter().cloned() {}
found reference `&_`
= note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned<std::collections::hash_map::Iter<'_, _, _>>`
error: aborting due to 2 previous errors
error[E0271]: type mismatch resolving `<std::collections::hash_map::Iter<'_, _, _> as std::iter::Iterator>::Item == &_`
--> $DIR/issue-33941.rs:4:14
|
LL | for _ in HashMap::new().iter().cloned() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference
|
= note: expected tuple `(&_, &_)`
found reference `&_`
= note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned<std::collections::hash_map::Iter<'_, _, _>>`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0271`.

View File

@ -5,14 +5,19 @@ LL | pub fn baz<T: Foo>(x: T) -> T {
| - move occurs because `x` has type `T`, which does not implement the `Copy` trait
LL | if 0 == 1 {
LL | bar::bar(x.zero())
| - value moved here
| ------ `x` moved due to this method call
LL | } else {
LL | x.zero()
| - value moved here
| ------ `x` moved due to this method call
LL | };
LL | x.zero()
| ^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
--> $DIR/issue-34721.rs:4:13
|
LL | fn zero(self) -> Self;
| ^^^^
help: consider further restricting this bound
|
LL | pub fn baz<T: Foo + Copy>(x: T) -> T {

View File

@ -1,10 +1,16 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/issue-40510-1.rs:7:9
|
LL | let mut x: Box<()> = Box::new(());
| ----- variable defined here
LL |
LL | || {
| - inferred to be a `FnMut` closure
LL | &mut x
| ^^^^^^ returns a reference to a captured variable which escapes the closure body
| ^^^^^-
| | |
| | variable captured here
| returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape

View File

@ -1,10 +1,14 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/issue-40510-3.rs:7:9
|
LL | let mut x: Vec<()> = Vec::new();
| ----- variable defined here
LL |
LL | || {
| - inferred to be a `FnMut` closure
LL | / || {
LL | | x.push(())
| | - variable captured here
LL | | }
| |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body
|

View File

@ -1,11 +1,14 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/issue-49824.rs:4:9
|
LL | let mut x = 0;
| ----- variable defined here
LL | || {
| - inferred to be a `FnMut` closure
LL | / || {
LL | |
LL | | let _y = &mut x;
| | - variable captured here
LL | | }
| |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body
|

View File

@ -6,11 +6,17 @@ LL | let mut bad_letters = vec!['e', 't', 'o', 'i'];
LL | for l in bad_letters {
| -----------
| |
| value moved here
| `bad_letters` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&bad_letters`
...
LL | bad_letters.push('s');
| ^^^^^^^^^^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `bad_letters`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
error: aborting due to previous error

View File

@ -6,12 +6,18 @@ LL | let orig = vec![true];
LL | for _val in orig {}
| ----
| |
| value moved here
| `orig` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&orig`
LL | let _closure = || orig;
| ^^ ---- use occurs due to use in closure
| |
| value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `orig`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
error: aborting due to previous error

View File

@ -0,0 +1,74 @@
use std::pin::Pin;
use std::rc::Rc;
use std::ops::Add;
struct Foo;
impl Add for Foo {
type Output = ();
fn add(self, _rhs: Self) -> () {}
}
impl Foo {
fn use_self(self) {}
fn use_box_self(self: Box<Self>) {}
fn use_pin_box_self(self: Pin<Box<Self>>) {}
fn use_rc_self(self: Rc<Self>) {}
fn use_mut_self(&mut self) -> &mut Self { self }
}
struct Container(Vec<bool>);
impl Container {
fn custom_into_iter(self) -> impl Iterator<Item = bool> {
self.0.into_iter()
}
}
fn move_out(val: Container) {
val.0.into_iter().next();
val.0; //~ ERROR use of moved
let foo = Foo;
foo.use_self();
foo; //~ ERROR use of moved
let second_foo = Foo;
second_foo.use_self();
second_foo; //~ ERROR use of moved
let boxed_foo = Box::new(Foo);
boxed_foo.use_box_self();
boxed_foo; //~ ERROR use of moved
let pin_box_foo = Box::pin(Foo);
pin_box_foo.use_pin_box_self();
pin_box_foo; //~ ERROR use of moved
let mut mut_foo = Foo;
let ret = mut_foo.use_mut_self();
mut_foo; //~ ERROR cannot move out
ret;
let rc_foo = Rc::new(Foo);
rc_foo.use_rc_self();
rc_foo; //~ ERROR use of moved
let foo_add = Foo;
foo_add + Foo;
foo_add; //~ ERROR use of moved
let implicit_into_iter = vec![true];
for _val in implicit_into_iter {}
implicit_into_iter; //~ ERROR use of moved
let explicit_into_iter = vec![true];
for _val in explicit_into_iter.into_iter() {}
explicit_into_iter; //~ ERROR use of moved
let container = Container(vec![]);
for _val in container.custom_into_iter() {}
container; //~ ERROR use of moved
}
fn main() {}

View File

@ -0,0 +1,158 @@
error[E0382]: use of moved value: `val.0`
--> $DIR/move-fn-self-receiver.rs:30:5
|
LL | val.0.into_iter().next();
| ----------- `val.0` moved due to this method call
LL | val.0;
| ^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `val.0`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
= note: move occurs because `val.0` has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `foo`
--> $DIR/move-fn-self-receiver.rs:34:5
|
LL | let foo = Foo;
| --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
LL | foo.use_self();
| ---------- `foo` moved due to this method call
LL | foo;
| ^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `foo`
--> $DIR/move-fn-self-receiver.rs:13:17
|
LL | fn use_self(self) {}
| ^^^^
error[E0382]: use of moved value: `second_foo`
--> $DIR/move-fn-self-receiver.rs:38:5
|
LL | let second_foo = Foo;
| ---------- move occurs because `second_foo` has type `Foo`, which does not implement the `Copy` trait
LL | second_foo.use_self();
| ---------- `second_foo` moved due to this method call
LL | second_foo;
| ^^^^^^^^^^ value used here after move
error[E0382]: use of moved value: `boxed_foo`
--> $DIR/move-fn-self-receiver.rs:42:5
|
LL | let boxed_foo = Box::new(Foo);
| --------- move occurs because `boxed_foo` has type `std::boxed::Box<Foo>`, which does not implement the `Copy` trait
LL | boxed_foo.use_box_self();
| -------------- `boxed_foo` moved due to this method call
LL | boxed_foo;
| ^^^^^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `boxed_foo`
--> $DIR/move-fn-self-receiver.rs:14:21
|
LL | fn use_box_self(self: Box<Self>) {}
| ^^^^
error[E0382]: use of moved value: `pin_box_foo`
--> $DIR/move-fn-self-receiver.rs:46:5
|
LL | let pin_box_foo = Box::pin(Foo);
| ----------- move occurs because `pin_box_foo` has type `std::pin::Pin<std::boxed::Box<Foo>>`, which does not implement the `Copy` trait
LL | pin_box_foo.use_pin_box_self();
| ------------------ `pin_box_foo` moved due to this method call
LL | pin_box_foo;
| ^^^^^^^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `pin_box_foo`
--> $DIR/move-fn-self-receiver.rs:15:25
|
LL | fn use_pin_box_self(self: Pin<Box<Self>>) {}
| ^^^^
error[E0505]: cannot move out of `mut_foo` because it is borrowed
--> $DIR/move-fn-self-receiver.rs:50:5
|
LL | let ret = mut_foo.use_mut_self();
| ------- borrow of `mut_foo` occurs here
LL | mut_foo;
| ^^^^^^^ move out of `mut_foo` occurs here
LL | ret;
| --- borrow later used here
error[E0382]: use of moved value: `rc_foo`
--> $DIR/move-fn-self-receiver.rs:55:5
|
LL | let rc_foo = Rc::new(Foo);
| ------ move occurs because `rc_foo` has type `std::rc::Rc<Foo>`, which does not implement the `Copy` trait
LL | rc_foo.use_rc_self();
| ------------- `rc_foo` moved due to this method call
LL | rc_foo;
| ^^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `rc_foo`
--> $DIR/move-fn-self-receiver.rs:16:20
|
LL | fn use_rc_self(self: Rc<Self>) {}
| ^^^^
error[E0382]: use of moved value: `foo_add`
--> $DIR/move-fn-self-receiver.rs:59:5
|
LL | let foo_add = Foo;
| ------- move occurs because `foo_add` has type `Foo`, which does not implement the `Copy` trait
LL | foo_add + Foo;
| ------------- `foo_add` moved due to usage in operator
LL | foo_add;
| ^^^^^^^ value used here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/libcore/ops/arith.rs:LL:COL
|
LL | fn add(self, rhs: Rhs) -> Self::Output;
| ^^^^
error[E0382]: use of moved value: `implicit_into_iter`
--> $DIR/move-fn-self-receiver.rs:63:5
|
LL | let implicit_into_iter = vec![true];
| ------------------ move occurs because `implicit_into_iter` has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
LL | for _val in implicit_into_iter {}
| ------------------
| |
| `implicit_into_iter` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&implicit_into_iter`
LL | implicit_into_iter;
| ^^^^^^^^^^^^^^^^^^ value used here after move
error[E0382]: use of moved value: `explicit_into_iter`
--> $DIR/move-fn-self-receiver.rs:67:5
|
LL | let explicit_into_iter = vec![true];
| ------------------ move occurs because `explicit_into_iter` has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
LL | for _val in explicit_into_iter.into_iter() {}
| ----------- `explicit_into_iter` moved due to this method call
LL | explicit_into_iter;
| ^^^^^^^^^^^^^^^^^^ value used here after move
error[E0382]: use of moved value: `container`
--> $DIR/move-fn-self-receiver.rs:71:5
|
LL | let container = Container(vec![]);
| --------- move occurs because `container` has type `Container`, which does not implement the `Copy` trait
LL | for _val in container.custom_into_iter() {}
| ------------------ `container` moved due to this method call
LL | container;
| ^^^^^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `container`
--> $DIR/move-fn-self-receiver.rs:23:25
|
LL | fn custom_into_iter(self) -> impl Iterator<Item = bool> {
| ^^^^
error: aborting due to 11 previous errors
Some errors have detailed explanations: E0382, E0505.
For more information about an error, try `rustc --explain E0382`.

View File

@ -4,9 +4,15 @@ error[E0382]: borrow of moved value: `x`
LL | let x = vec!["hi".to_string()];
| - move occurs because `x` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait
LL | consume(x.into_iter().next().unwrap());
| - value moved here
| ----------- `x` moved due to this method call
LL | touch(&x[0]);
| ^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
error: aborting due to previous error

View File

@ -104,9 +104,15 @@ error[E0382]: borrow of moved value: `x`
LL | let x = vec!["hi".to_string()];
| - move occurs because `x` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait
LL | let _y = x.into_iter().next().unwrap();
| - value moved here
| ----------- `x` moved due to this method call
LL | touch(&x);
| ^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
error[E0382]: borrow of moved value: `x`
--> $DIR/moves-based-on-type-exprs.rs:83:11
@ -114,9 +120,15 @@ error[E0382]: borrow of moved value: `x`
LL | let x = vec!["hi".to_string()];
| - move occurs because `x` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait
LL | let _y = [x.into_iter().next().unwrap(); 1];
| - value moved here
| ----------- `x` moved due to this method call
LL | touch(&x);
| ^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
--> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL
|
LL | fn into_iter(self) -> Self::IntoIter;
| ^^^^
error: aborting due to 11 previous errors

View File

@ -1,9 +1,13 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/issue-53040.rs:3:8
|
LL | let mut v: Vec<()> = Vec::new();
| ----- variable defined here
LL | || &mut v;
| - ^^^^^^ returns a reference to a captured variable which escapes the closure body
| |
| - ^^^^^-
| | | |
| | | variable captured here
| | returns a reference to a captured variable which escapes the closure body
| inferred to be a `FnMut` closure
|
= note: `FnMut` closures only have access to their captured variables while they are executing...

View File

@ -4,10 +4,15 @@ error[E0382]: use of moved value: `blk`
LL | fn foo<F:FnOnce()>(blk: F) {
| --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait
LL | blk();
| --- value moved here
| ----- `blk` moved due to this call
LL | blk();
| ^^^ value used here after move
|
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/once-cant-call-twice-on-heap.rs:8:5
|
LL | blk();
| ^^^
help: consider further restricting this bound
|
LL | fn foo<F:FnOnce() + Copy>(blk: F) {

View File

@ -1,9 +1,13 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/regions-return-ref-to-upvar-issue-17403.rs:7:24
|
LL | let mut x = 0;
| ----- variable defined here
LL | let mut f = || &mut x;
| - ^^^^^^ returns a reference to a captured variable which escapes the closure body
| |
| - ^^^^^-
| | | |
| | | variable captured here
| | returns a reference to a captured variable which escapes the closure body
| inferred to be a `FnMut` closure
|
= note: `FnMut` closures only have access to their captured variables while they are executing...

View File

@ -0,0 +1,19 @@
// check-pass
#![feature(trait_alias)]
trait SomeTrait {
fn map(&self) {}
}
impl<T> SomeTrait for Option<T> {}
trait SomeAlias = SomeTrait;
fn main() {
let x = Some(123);
// This should resolve to the trait impl for Option
Option::map(x, |z| z);
// This should resolve to the trait impl for SomeTrait
SomeTrait::map(&x);
}

View File

@ -0,0 +1,14 @@
// check-pass
#![feature(trait_alias)]
trait Bounded { const MAX: Self; }
impl Bounded for u32 {
// This should correctly resolve to the associated const in the inherent impl of u32.
const MAX: Self = u32::MAX;
}
trait Num = Bounded + Copy;
fn main() {}

View File

@ -2,7 +2,7 @@ error[E0382]: use of moved value: `tick`
--> $DIR/unboxed-closures-infer-fnonce-call-twice.rs:10:5
|
LL | tick();
| ---- value moved here
| ------ `tick` moved due to this call
LL | tick();
| ^^^^ value used here after move
|
@ -11,6 +11,11 @@ note: closure cannot be invoked more than once because it moves the variable `co
|
LL | let tick = || mem::drop(counter);
| ^^^^^^^
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/unboxed-closures-infer-fnonce-call-twice.rs:9:5
|
LL | tick();
| ^^^^
error: aborting due to previous error

View File

@ -2,7 +2,7 @@ error[E0382]: use of moved value: `tick`
--> $DIR/unboxed-closures-infer-fnonce-move-call-twice.rs:10:5
|
LL | tick();
| ---- value moved here
| ------ `tick` moved due to this call
LL | tick();
| ^^^^ value used here after move
|
@ -11,6 +11,11 @@ note: closure cannot be invoked more than once because it moves the variable `co
|
LL | let tick = move || mem::drop(counter);
| ^^^^^^^
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/unboxed-closures-infer-fnonce-move-call-twice.rs:9:5
|
LL | tick();
| ^^^^
error: aborting due to previous error

View File

@ -4,11 +4,16 @@ error[E0382]: borrow of moved value: `x`
LL | fn move_then_borrow<T: Not<Output=T> + Clone>(x: T) {
| - move occurs because `x` has type `T`, which does not implement the `Copy` trait
LL | !x;
| - value moved here
| -- `x` moved due to this method call
LL |
LL | x.clone();
| ^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `x`
--> $SRC_DIR/libcore/ops/bit.rs:LL:COL
|
LL | fn not(self) -> Self::Output;
| ^^^^
help: consider further restricting this bound
|
LL | fn move_then_borrow<T: Not<Output=T> + Clone + Copy>(x: T) {

View File

@ -37,10 +37,16 @@ error[E0382]: borrow of moved value: `y`
LL | let y = *x;
| - move occurs because `y` has type `str`, which does not implement the `Copy` trait
LL | y.foo();
| - value moved here
| ----- `y` moved due to this method call
...
LL | println!("{}", &y);
| ^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `y`
--> $DIR/borrow-after-move.rs:4:12
|
LL | fn foo(self) -> String;
| ^^^^
error[E0382]: borrow of moved value: `x`
--> $DIR/borrow-after-move.rs:39:24

View File

@ -34,9 +34,15 @@ error[E0382]: use of moved value: `y`
LL | let y = *x;
| - move occurs because `y` has type `str`, which does not implement the `Copy` trait
LL | y.foo();
| - value moved here
| ----- `y` moved due to this method call
LL | y.foo();
| ^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `y`
--> $DIR/double-move.rs:4:12
|
LL | fn foo(self) -> String;
| ^^^^
error[E0382]: use of moved value: `x`
--> $DIR/double-move.rs:45:9

View File

@ -4,9 +4,15 @@ error[E0382]: use of moved value: `self`
LL | pub fn foo(self) -> isize {
| ---- move occurs because `self` has type `S`, which does not implement the `Copy` trait
LL | self.bar();
| ---- value moved here
| ----- `self` moved due to this method call
LL | return self.x;
| ^^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `self`
--> $DIR/use-after-move-self-based-on-type.rs:15:16
|
LL | pub fn bar(self) {}
| ^^^^
error: aborting due to previous error

View File

@ -4,9 +4,15 @@ error[E0382]: use of moved value: `self`
LL | pub fn foo(self) -> isize {
| ---- move occurs because `self` has type `S`, which does not implement the `Copy` trait
LL | self.bar();
| ---- value moved here
| ----- `self` moved due to this method call
LL | return *self.x;
| ^^^^^^^ value used here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `self`
--> $DIR/use-after-move-self.rs:13:16
|
LL | pub fn bar(self) {}
| ^^^^
error: aborting due to previous error

View File

@ -4,9 +4,15 @@ error[E0382]: borrow of moved value: `start`
LL | let start = Mine{test:"Foo".to_string(), other_val:0};
| ----- move occurs because `start` has type `Mine`, which does not implement the `Copy` trait
LL | let end = Mine{other_val:1, ..start.make_string_bar()};
| ----- value moved here
| ----------------- `start` moved due to this method call
LL | println!("{}", start.test);
| ^^^^^^^^^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `start`
--> $DIR/walk-struct-literal-with.rs:7:28
|
LL | fn make_string_bar(mut self) -> Mine{
| ^^^^
error: aborting due to previous error