diff options
87 files changed, 1574 insertions, 507 deletions
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2226737757b..06462fd96d9 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -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")] diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs index 6f6009b47e6..9dbc23f5c04 100644 --- a/src/libcore/future/mod.rs +++ b/src/libcore/future/mod.rs @@ -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 diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index a10b34d931d..fbfcdc3c1a9 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -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")] diff --git a/src/libcore/str/pattern.rs b/src/libcore/str/pattern.rs index 263d6b5efdf..14f1f293d40 100644 --- a/src/libcore/str/pattern.rs +++ b/src/libcore/str/pattern.rs @@ -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> diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs index b7894eb145b..e59cacfffc9 100644 --- a/src/librustc_ast_lowering/expr.rs +++ b/src/librustc_ast_lowering/expr.rs @@ -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( diff --git a/src/librustc_error_codes/error_codes/E0446.md b/src/librustc_error_codes/error_codes/E0446.md index 77a1834ece4..6ec47c4962c 100644 --- a/src/librustc_error_codes/error_codes/E0446.md +++ b/src/librustc_error_codes/error_codes/E0446.md @@ -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) } diff --git a/src/librustc_error_codes/error_codes/E0493.md b/src/librustc_error_codes/error_codes/E0493.md index 90a0cbce623..0dcc3b62b4b 100644 --- a/src/librustc_error_codes/error_codes/E0493.md +++ b/src/librustc_error_codes/error_codes/E0493.md @@ -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 { diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index b4935236b6a..d186f35a12b 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -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 // ------------------------------------------------------------------------- diff --git a/src/librustc_infer/infer/error_reporting/need_type_info.rs b/src/librustc_infer/infer/error_reporting/need_type_info.rs index ceaec2ba5db..1361d5bede6 100644 --- a/src/librustc_infer/infer/error_reporting/need_type_info.rs +++ b/src/librustc_infer/infer/error_reporting/need_type_info.rs @@ -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), diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 1a9bf4e1e8f..1ed9bc3f1f5 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -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); + } } }); diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 44944a9fb26..edde82c40fb 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -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( diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 1dc22c10c8e..721b4254c85 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -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) { diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 626a436b400..ec80a2b6171 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -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)] diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs index d1cfc4867a2..e3e0856ffc5 100644 --- a/src/librustc_middle/hir/map/mod.rs +++ b/src/librustc_middle/hir/map/mod.rs @@ -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. diff --git a/src/librustc_middle/hir/mod.rs b/src/librustc_middle/hir/mod.rs index 1e3676496ce..e152d11c081 100644 --- a/src/librustc_middle/hir/mod.rs +++ b/src/librustc_middle/hir/mod.rs @@ -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); } diff --git a/src/librustc_middle/lint.rs b/src/librustc_middle/lint.rs index 27239b4ad2e..923119e359c 100644 --- a/src/librustc_middle/lint.rs +++ b/src/librustc_middle/lint.rs @@ -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. diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 129f9691ea5..21f5d9e7dd4 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -76,7 +76,8 @@ pub enum MirPhase { Build = 0, Const = 1, Validated = 2, - Optimized = 3, + DropElab = 3, + Optimized = 4, } impl MirPhase { diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index 99bfb74c243..d82faf3e5fb 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -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)] diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index be15e6c576f..3b6d54a1bc1 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -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. diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index d0050f801fc..2e897647a3b 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -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 => { diff --git a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs index 5253acbba7f..d04059ff0fc 100644 --- a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs +++ b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs @@ -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 { diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs index ca8e54ea286..04f48cd6582 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mod.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs @@ -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`) diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs index b49e4187fb8..4883b08e424 100644 --- a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs @@ -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`. diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs index 4d4b6fb9386..b4bc89e827d 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs @@ -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!( diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index 727c4d0605e..f1923b9e81c 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -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!( diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index b703237a18e..d099f48adc5 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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()) } } diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index 375b3210e8c..ea68364be37 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -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 { diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index f5238e7b7be..934729553a7 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -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, + } +} diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index fe113843800..3e459bd52f7 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -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]; } diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index e2255d170f9..168612f9bee 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -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 { diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index 7c439f80ef6..e4aa88e3c20 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -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; diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index 92bd740e27a..d5059c98c95 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -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, diff --git a/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs new file mode 100644 index 00000000000..226e0e2049e --- /dev/null +++ b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs @@ -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 { .. } => {} + } + } +} diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index ab87d70da7d..428a74bcdcb 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -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 diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 0ff60cbd55d..72db35de408 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -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, diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 26725a2ac02..4240b528a61 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -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)); } diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index dc7d8098e55..4a4de6c420b 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -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::*; diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index 46b687d76e5..087c2c064cf 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -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") } diff --git a/src/librustc_span/hygiene.rs b/src/librustc_span/hygiene.rs index c0fb84e741f..f2c9f8055b9 100644 --- a/src/librustc_span/hygiene.rs +++ b/src/librustc_span/hygiene.rs @@ -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", } } } diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs index 96240066834..af9b5a264e3 100644 --- a/src/librustc_span/lib.rs +++ b/src/librustc_span/lib.rs @@ -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; diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index fea5880f01e..fdeb58b7b7a 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -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, diff --git a/src/librustc_trait_selection/traits/structural_match.rs b/src/librustc_trait_selection/traits/structural_match.rs index e59fbd313c8..c4deb639140 100644 --- a/src/librustc_trait_selection/traits/structural_match.rs +++ b/src/librustc_trait_selection/traits/structural_match.rs @@ -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 => { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index a2e6c8793cb..37652330108 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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 { diff --git a/src/test/ui/async-await/issue-69446-fnmut-capture.rs b/src/test/ui/async-await/issue-69446-fnmut-capture.rs new file mode 100644 index 00000000000..842115538c9 --- /dev/null +++ b/src/test/ui/async-await/issue-69446-fnmut-capture.rs @@ -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(); + }); +} diff --git a/src/test/ui/async-await/issue-69446-fnmut-capture.stderr b/src/test/ui/async-await/issue-69446-fnmut-capture.stderr new file mode 100644 index 00000000000..3d2b0402bc5 --- /dev/null +++ b/src/test/ui/async-await/issue-69446-fnmut-capture.stderr @@ -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 + diff --git a/src/test/ui/binop/binop-consume-args.stderr b/src/test/ui/binop/binop-consume-args.stderr index acdc03e3726..addc8a0efe1 100644 --- a/src/test/ui/binop/binop-consume-args.stderr +++ b/src/test/ui/binop/binop-consume-args.stderr @@ -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) { diff --git a/src/test/ui/binop/binop-move-semantics.stderr b/src/test/ui/binop/binop-move-semantics.stderr index 6d5ac9cab30..97b70efe20e 100644 --- a/src/test/ui/binop/binop-move-semantics.stderr +++ b/src/test/ui/binop/binop-move-semantics.stderr @@ -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) { diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr index 075e0e2e451..4144d70cc16 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr @@ -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); diff --git a/src/test/ui/borrowck/borrowck-unboxed-closures.stderr b/src/test/ui/borrowck/borrowck-unboxed-closures.stderr index a51cda548ef..bc1721944fb 100644 --- a/src/test/ui/borrowck/borrowck-unboxed-closures.stderr +++ b/src/test/ui/borrowck/borrowck-unboxed-closures.stderr @@ -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) { diff --git a/src/test/ui/closure_context/issue-42065.stderr b/src/test/ui/closure_context/issue-42065.stderr index 69d98654048..896bb6dc6be 100644 --- a/src/test/ui/closure_context/issue-42065.stderr +++ b/src/test/ui/closure_context/issue-42065.stderr @@ -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 diff --git a/src/test/ui/codemap_tests/tab_3.stderr b/src/test/ui/codemap_tests/tab_3.stderr index 97816a76004..614e69e89f6 100644 --- a/src/test/ui/codemap_tests/tab_3.stderr +++ b/src/test/ui/codemap_tests/tab_3.stderr @@ -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 diff --git a/src/test/ui/consts/control-flow/drop-fail.precise.stderr b/src/test/ui/consts/control-flow/drop-fail.precise.stderr new file mode 100644 index 00000000000..b4b6be8a1e5 --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-fail.precise.stderr @@ -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`. diff --git a/src/test/ui/consts/control-flow/drop-failure.rs b/src/test/ui/consts/control-flow/drop-fail.rs index 9da5546976c..7bd36726cea 100644 --- a/src/test/ui/consts/control-flow/drop-failure.rs +++ b/src/test/ui/consts/control-flow/drop-fail.rs @@ -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 { diff --git a/src/test/ui/consts/control-flow/drop-failure.stderr b/src/test/ui/consts/control-flow/drop-fail.stock.stderr index 3eec3a929a0..77cded5c438 100644 --- a/src/test/ui/consts/control-flow/drop-failure.stderr +++ b/src/test/ui/consts/control-flow/drop-fail.stock.stderr @@ -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 diff --git a/src/test/ui/consts/control-flow/drop-success.rs b/src/test/ui/consts/control-flow/drop-pass.rs index 185d6b63996..b0afd76c4e6 100644 --- a/src/test/ui/consts/control-flow/drop-success.rs +++ b/src/test/ui/consts/control-flow/drop-pass.rs @@ -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>> = { diff --git a/src/test/ui/consts/control-flow/drop-precise.rs b/src/test/ui/consts/control-flow/drop-precise.rs new file mode 100644 index 00000000000..95df76d9905 --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-precise.rs @@ -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() {} diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs index 65fc49c0b27..064dc6c262c 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.rs +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -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 }; diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr index 805ba9c6b03..4b3fe995700 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -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); | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/hygiene/unpretty-debug.stdout b/src/test/ui/hygiene/unpretty-debug.stdout index acd852103ca..fc5ed724e3c 100644 --- a/src/test/ui/hygiene/unpretty-debug.stdout +++ b/src/test/ui/hygiene/unpretty-debug.stdout @@ -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) */ diff --git a/src/test/ui/impl-trait-in-bindings-issue-73003.rs b/src/test/ui/impl-trait-in-bindings-issue-73003.rs new file mode 100644 index 00000000000..fd8fe5f48df --- /dev/null +++ b/src/test/ui/impl-trait-in-bindings-issue-73003.rs @@ -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() {} diff --git a/src/test/ui/impl-trait-in-bindings-issue-73003.stderr b/src/test/ui/impl-trait-in-bindings-issue-73003.stderr new file mode 100644 index 00000000000..715671c8add --- /dev/null +++ b/src/test/ui/impl-trait-in-bindings-issue-73003.stderr @@ -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 + diff --git a/src/test/ui/issues/issue-12127.stderr b/src/test/ui/issues/issue-12127.stderr index 2283b1275d0..b759aa45e3e 100644 --- a/src/test/ui/issues/issue-12127.stderr +++ b/src/test/ui/issues/issue-12127.stderr @@ -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 diff --git a/src/test/ui/issues/issue-33941.rs b/src/test/ui/issues/issue-33941.rs index ccaa6334856..4fb805b37e0 100644 --- a/src/test/ui/issues/issue-33941.rs +++ b/src/test/ui/issues/issue-33941.rs @@ -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 } diff --git a/src/test/ui/issues/issue-33941.stderr b/src/test/ui/issues/issue-33941.stderr index 734ae78f362..20335d2cdd6 100644 --- a/src/test/ui/issues/issue-33941.stderr +++ b/src/test/ui/issues/issue-33941.stderr @@ -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`. diff --git a/src/test/ui/issues/issue-34721.stderr b/src/test/ui/issues/issue-34721.stderr index 6cfed20f43a..b4cc1a0aa7e 100644 --- a/src/test/ui/issues/issue-34721.stderr +++ b/src/test/ui/issues/issue-34721.stderr @@ -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 { diff --git a/src/test/ui/issues/issue-40510-1.stderr b/src/test/ui/issues/issue-40510-1.stderr index f4fda0abc20..54df40b6e3d 100644 --- a/src/test/ui/issues/issue-40510-1.stderr +++ b/src/test/ui/issues/issue-40510-1.stderr @@ -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 diff --git a/src/test/ui/issues/issue-40510-3.stderr b/src/test/ui/issues/issue-40510-3.stderr index 4bc7d0f5dea..cb885ec7d95 100644 --- a/src/test/ui/issues/issue-40510-3.stderr +++ b/src/test/ui/issues/issue-40510-3.stderr @@ -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 | diff --git a/src/test/ui/issues/issue-49824.stderr b/src/test/ui/issues/issue-49824.stderr index 6b486aafcdf..2fec482543d 100644 --- a/src/test/ui/issues/issue-49824.stderr +++ b/src/test/ui/issues/issue-49824.stderr @@ -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 | diff --git a/src/test/ui/issues/issue-61108.stderr b/src/test/ui/issues/issue-61108.stderr index 8523a6f6548..ba43f2d33ee 100644 --- a/src/test/ui/issues/issue-61108.stderr +++ b/src/test/ui/issues/issue-61108.stderr @@ -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 diff --git a/src/test/ui/issues/issue-64559.stderr b/src/test/ui/issues/issue-64559.stderr index 3c685dc8d08..2c337bae130 100644 --- a/src/test/ui/issues/issue-64559.stderr +++ b/src/test/ui/issues/issue-64559.stderr @@ -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 diff --git a/src/test/ui/moves/move-fn-self-receiver.rs b/src/test/ui/moves/move-fn-self-receiver.rs new file mode 100644 index 00000000000..6107f53fa19 --- /dev/null +++ b/src/test/ui/moves/move-fn-self-receiver.rs @@ -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() {} diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr new file mode 100644 index 00000000000..4333e8a23e8 --- /dev/null +++ b/src/test/ui/moves/move-fn-self-receiver.stderr @@ -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`. diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr index 71a3c4506ea..142feb280d1 100644 --- a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr +++ b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr @@ -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 diff --git a/src/test/ui/moves/moves-based-on-type-exprs.stderr b/src/test/ui/moves/moves-based-on-type-exprs.stderr index 67fae606c4e..ff98aab50c9 100644 --- a/src/test/ui/moves/moves-based-on-type-exprs.stderr +++ b/src/test/ui/moves/moves-based-on-type-exprs.stderr @@ -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 diff --git a/src/test/ui/nll/issue-53040.stderr b/src/test/ui/nll/issue-53040.stderr index 7cba32c6743..87ffe9b1abf 100644 --- a/src/test/ui/nll/issue-53040.stderr +++ b/src/test/ui/nll/issue-53040.stderr @@ -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... diff --git a/src/test/ui/once-cant-call-twice-on-heap.stderr b/src/test/ui/once-cant-call-twice-on-heap.stderr index 7133a32431a..8761b5261d5 100644 --- a/src/test/ui/once-cant-call-twice-on-heap.stderr +++ b/src/test/ui/once-cant-call-twice-on-heap.stderr @@ -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) { diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr index 4c275b19492..b087e03b464 100644 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr +++ b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr @@ -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... diff --git a/src/test/ui/traits/trait-alias/issue-60021-assoc-method-resolve.rs b/src/test/ui/traits/trait-alias/issue-60021-assoc-method-resolve.rs new file mode 100644 index 00000000000..5e27ed3c646 --- /dev/null +++ b/src/test/ui/traits/trait-alias/issue-60021-assoc-method-resolve.rs @@ -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); +} diff --git a/src/test/ui/traits/trait-alias/issue-72415-assoc-const-resolve.rs b/src/test/ui/traits/trait-alias/issue-72415-assoc-const-resolve.rs new file mode 100644 index 00000000000..e49125d1024 --- /dev/null +++ b/src/test/ui/traits/trait-alias/issue-72415-assoc-const-resolve.rs @@ -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() {} diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr index 0b9aa61a765..ab6f0651846 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr @@ -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 diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr index 20773d561f9..8d70a2b1760 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr @@ -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 diff --git a/src/test/ui/unop-move-semantics.stderr b/src/test/ui/unop-move-semantics.stderr index e0499cfe95c..7e9c8559a4b 100644 --- a/src/test/ui/unop-move-semantics.stderr +++ b/src/test/ui/unop-move-semantics.stderr @@ -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) { diff --git a/src/test/ui/unsized-locals/borrow-after-move.stderr b/src/test/ui/unsized-locals/borrow-after-move.stderr index 110edab69be..906b543e421 100644 --- a/src/test/ui/unsized-locals/borrow-after-move.stderr +++ b/src/test/ui/unsized-locals/borrow-after-move.stderr @@ -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 diff --git a/src/test/ui/unsized-locals/double-move.stderr b/src/test/ui/unsized-locals/double-move.stderr index 5b936fb6447..49b2031c6b9 100644 --- a/src/test/ui/unsized-locals/double-move.stderr +++ b/src/test/ui/unsized-locals/double-move.stderr @@ -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 diff --git a/src/test/ui/use/use-after-move-self-based-on-type.stderr b/src/test/ui/use/use-after-move-self-based-on-type.stderr index 9bf1175430c..b9440f4de07 100644 --- a/src/test/ui/use/use-after-move-self-based-on-type.stderr +++ b/src/test/ui/use/use-after-move-self-based-on-type.stderr @@ -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 diff --git a/src/test/ui/use/use-after-move-self.stderr b/src/test/ui/use/use-after-move-self.stderr index 3be0a65550b..3da53b024db 100644 --- a/src/test/ui/use/use-after-move-self.stderr +++ b/src/test/ui/use/use-after-move-self.stderr @@ -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 diff --git a/src/test/ui/walk-struct-literal-with.stderr b/src/test/ui/walk-struct-literal-with.stderr index eeb594a21f3..ece63a2b819 100644 --- a/src/test/ui/walk-struct-literal-with.stderr +++ b/src/test/ui/walk-struct-literal-with.stderr @@ -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 |
