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:
commit
d4ecf31efc
|
@ -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")]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -76,7 +76,8 @@ pub enum MirPhase {
|
|||
Build = 0,
|
||||
Const = 1,
|
||||
Validated = 2,
|
||||
Optimized = 3,
|
||||
DropElab = 3,
|
||||
Optimized = 4,
|
||||
}
|
||||
|
||||
impl MirPhase {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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`)
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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`.
|
|
@ -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 {
|
|
@ -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
|
|
@ -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>> = {
|
|
@ -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() {}
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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() {}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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() {}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue