diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 1067f1e40ef66..af71db6948329 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -9,7 +9,8 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_span::def_id::DefId; +use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -507,12 +508,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let closure_span = tcx.def_span(def_id); + self.cannot_move_out_of(span, &place_description) .with_span_label(upvar_span, "captured outer variable") .with_span_label( closure_span, format!("captured by this `{closure_kind}` closure"), ) + .with_span_help( + self.get_closure_bound_clause_span(*def_id), + "`Fn` and `FnMut` closures require captured values to be able to be \ + consumed multiple times, but an `FnOnce` consume them only once", + ) } _ => { let source = self.borrowed_content_source(deref_base); @@ -561,6 +568,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err } + fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span { + let tcx = self.infcx.tcx; + let typeck_result = tcx.typeck(self.mir_def_id()); + // Check whether the closure is an argument to a call, if so, + // get the instantiated where-bounds of that call. + let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local()); + let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP }; + + let predicates = match parent.kind { + hir::ExprKind::Call(callee, _) => { + let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP }; + let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP }; + tcx.predicates_of(fn_def_id).instantiate(tcx, args) + } + hir::ExprKind::MethodCall(..) => { + let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else { + return DUMMY_SP; + }; + let args = typeck_result.node_args(parent.hir_id); + tcx.predicates_of(method).instantiate(tcx, args) + } + _ => return DUMMY_SP, + }; + + // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`. + for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) { + if let Some(clause) = pred.as_trait_clause() + && let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind() + && *clause_closure_def_id == def_id + && (tcx.lang_items().fn_mut_trait() == Some(clause.def_id()) + || tcx.lang_items().fn_trait() == Some(clause.def_id())) + { + // Found `` + // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which + // could be changed to `FnOnce()` to avoid the move error. + return *span; + } + } + DUMMY_SP + } + fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { match error { GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5a5563c7bb2c8..98be37fd84b59 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -847,17 +847,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + with_fn! { with_span_help, /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] - pub fn span_help>( + pub fn span_help( &mut self, - sp: S, + sp: impl Into, msg: impl Into, ) -> &mut Self { self.sub(Level::Help, msg, sp.into()); self - } + } } /// Disallow attaching suggestions to this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index df95ed602cd36..11fef3be5d09b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -395,6 +395,7 @@ provide! { tcx, def_id, other, cdata, crate_extern_paths => { cdata.source().paths().cloned().collect() } expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) } + default_field => { cdata.get_default_field(def_id.index) } is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) } doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) } doc_link_traits_in_scope => { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a45895765945f..f4d0120a2e7b9 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1864,6 +1864,12 @@ rustc_queries! { feedable } + /// Returns whether the field corresponding to the `DefId` has a default field value. + query default_field(def_id: DefId) -> Option { + desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } return_result_from_ensure_ok diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2ad8543bf8ca4..d18d0fc16a8e4 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -420,14 +420,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // The fields are not expanded yet. return; } - let fields = fields + let field_name = |i, field: &ast::FieldDef| { + field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span)) + }; + let field_names: Vec<_> = + fields.iter().enumerate().map(|(i, field)| field_name(i, field)).collect(); + let defaults = fields .iter() .enumerate() - .map(|(i, field)| { - field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span)) - }) + .filter_map(|(i, field)| field.default.as_ref().map(|_| field_name(i, field).name)) .collect(); - self.r.field_names.insert(def_id, fields); + self.r.field_names.insert(def_id, field_names); + self.r.field_defaults.insert(def_id, defaults); } fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 66f79585d92f6..210ab72678c56 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1943,8 +1943,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { - let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } = - *privacy_error; + let PrivacyError { + ident, + binding, + outermost_res, + parent_scope, + single_nested, + dedup_span, + ref source, + } = *privacy_error; let res = binding.res(); let ctor_fields_span = self.ctor_fields_span(binding); @@ -1960,6 +1967,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut err = self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident }); + self.mention_default_field_values(source, ident, &mut err); + let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { let import_suggestions = self.lookup_import_candidates( @@ -2141,6 +2150,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err.emit(); } + /// When a private field is being set that has a default field value, we suggest using `..` and + /// setting the value of that field implicitly with its default. + /// + /// If we encounter code like + /// ```text + /// struct Priv; + /// pub struct S { + /// pub field: Priv = Priv, + /// } + /// ``` + /// which is used from a place where `Priv` isn't accessible + /// ```text + /// let _ = S { field: m::Priv1 {} }; + /// // ^^^^^ private struct + /// ``` + /// we will suggest instead using the `default_field_values` syntax instead: + /// ```text + /// let _ = S { .. }; + /// ``` + fn mention_default_field_values( + &self, + source: &Option, + ident: Ident, + err: &mut Diag<'_>, + ) { + let Some(expr) = source else { return }; + let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; + // We don't have to handle type-relative paths because they're forbidden in ADT + // expressions, but that would change with `#[feature(more_qualified_paths)]`. + let Some(Res::Def(_, def_id)) = + self.partial_res_map[&struct_expr.path.segments.iter().last().unwrap().id].full_res() + else { + return; + }; + let Some(default_fields) = self.field_defaults(def_id) else { return }; + if struct_expr.fields.is_empty() { + return; + } + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion_verbose( + format!( + "the type `{ident}` of field `{}` is private, but you can construct \ + the default value defined for it in `{}` using `..` in the struct \ + initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } + pub(crate) fn find_similarly_named_module_or_crate( &self, ident: Symbol, diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 092bb6fc4f989..9efcef695b7b4 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1029,6 +1029,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding, dedup_span: path_span, outermost_res: None, + source: None, parent_scope: *parent_scope, single_nested: path_span != root_span, }); @@ -1435,7 +1436,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, ignore_import: Option>, ) -> PathResult<'ra> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import) + self.resolve_path_with_ribs( + path, + opt_ns, + parent_scope, + None, + None, + None, + None, + ignore_import, + ) } #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_path<'r>( @@ -1451,6 +1461,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path, opt_ns, parent_scope, + None, finalize, None, ignore_binding, @@ -1463,6 +1474,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import parent_scope: &ParentScope<'ra>, + source: Option>, finalize: Option, ribs: Option<&PerNS>>>, ignore_binding: Option>, @@ -1645,6 +1657,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if finalize.is_some() { for error in &mut self.get_mut().privacy_errors[privacy_errors_len..] { error.outermost_res = Some((res, ident)); + error.source = match source { + Some(PathSource::Struct(Some(expr))) + | Some(PathSource::Expr(Some(expr))) => Some(expr.clone()), + _ => None, + }; } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 8f0c08c3998b3..953b72fd72b8f 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -424,7 +424,7 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> { /// Paths in path patterns `Path`. Pat, /// Paths in struct expressions and patterns `Path { .. }`. - Struct, + Struct(Option<&'a Expr>), /// Paths in tuple struct patterns `Path(..)`. TupleStruct(Span, &'ra [Span]), /// `m::A::B` in `::B::C`. @@ -447,7 +447,7 @@ impl PathSource<'_, '_, '_> { match self { PathSource::Type | PathSource::Trait(_) - | PathSource::Struct + | PathSource::Struct(_) | PathSource::DefineOpaques => TypeNS, PathSource::Expr(..) | PathSource::Pat @@ -464,7 +464,7 @@ impl PathSource<'_, '_, '_> { PathSource::Type | PathSource::Expr(..) | PathSource::Pat - | PathSource::Struct + | PathSource::Struct(_) | PathSource::TupleStruct(..) | PathSource::ReturnTypeNotation => true, PathSource::Trait(_) @@ -481,7 +481,7 @@ impl PathSource<'_, '_, '_> { PathSource::Type => "type", PathSource::Trait(_) => "trait", PathSource::Pat => "unit struct, unit variant or constant", - PathSource::Struct => "struct, variant or union type", + PathSource::Struct(_) => "struct, variant or union type", PathSource::TraitItem(ValueNS, PathSource::TupleStruct(..)) | PathSource::TupleStruct(..) => "tuple struct or tuple variant", PathSource::TraitItem(ns, _) => match ns { @@ -576,7 +576,7 @@ impl PathSource<'_, '_, '_> { || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) } PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), - PathSource::Struct => matches!( + PathSource::Struct(_) => matches!( res, Res::Def( DefKind::Struct @@ -616,8 +616,8 @@ impl PathSource<'_, '_, '_> { (PathSource::Trait(_), false) => E0405, (PathSource::Type | PathSource::DefineOpaques, true) => E0573, (PathSource::Type | PathSource::DefineOpaques, false) => E0412, - (PathSource::Struct, true) => E0574, - (PathSource::Struct, false) => E0422, + (PathSource::Struct(_), true) => E0574, + (PathSource::Struct(_), false) => E0422, (PathSource::Expr(..), true) | (PathSource::Delegation, true) => E0423, (PathSource::Expr(..), false) | (PathSource::Delegation, false) => E0425, (PathSource::Pat | PathSource::TupleStruct(..), true) => E0532, @@ -1482,11 +1482,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import finalize: Option, + source: PathSource<'_, 'ast, 'ra>, ) -> PathResult<'ra> { self.r.cm().resolve_path_with_ribs( path, opt_ns, &self.parent_scope, + Some(source), finalize, Some(&self.ribs), None, @@ -1966,7 +1968,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, partial_res: PartialRes, path: &[Segment], - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, path_span: Span, ) { let proj_start = path.len() - partial_res.unresolved_segments(); @@ -2019,7 +2021,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { | PathSource::ReturnTypeNotation => false, PathSource::Expr(..) | PathSource::Pat - | PathSource::Struct + | PathSource::Struct(_) | PathSource::TupleStruct(..) | PathSource::DefineOpaques | PathSource::Delegation => true, @@ -3866,7 +3868,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.smart_resolve_path(pat.id, qself, path, PathSource::Pat); } PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => { - self.smart_resolve_path(pat.id, qself, path, PathSource::Struct); + self.smart_resolve_path(pat.id, qself, path, PathSource::Struct(None)); self.record_patterns_with_skipped_bindings(pat, rest); } PatKind::Or(ref ps) => { @@ -4110,7 +4112,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { id: NodeId, qself: &Option>, path: &Path, - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, ) { self.smart_resolve_path_fragment( qself, @@ -4127,7 +4129,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, qself: &Option>, path: &[Segment], - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, finalize: Finalize, record_partial_res: RecordPartialRes, parent_qself: Option<&QSelf>, @@ -4365,7 +4367,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std))); std_path.extend(path); if let PathResult::Module(_) | PathResult::NonModule(_) = - self.resolve_path(&std_path, Some(ns), None) + self.resolve_path(&std_path, Some(ns), None, source) { // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8` let item_span = @@ -4439,7 +4441,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { span: Span, defer_to_typeck: bool, finalize: Finalize, - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, ) -> Result, Spanned>> { let mut fin_res = None; @@ -4488,7 +4490,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path: &[Segment], ns: Namespace, finalize: Finalize, - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, ) -> Result, Spanned>> { debug!( "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})", @@ -4551,7 +4553,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ))); } - let result = match self.resolve_path(path, Some(ns), Some(finalize)) { + let result = match self.resolve_path(path, Some(ns), Some(finalize), source) { PathResult::NonModule(path_res) => path_res, PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => { PartialRes::new(module.res().unwrap()) @@ -4774,7 +4776,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ExprKind::Struct(ref se) => { - self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct); + self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct(parent)); // This is the same as `visit::walk_expr(self, expr);`, but we want to pass the // parent in for accurate suggestions when encountering `Foo { bar }` that should // have been `Foo { bar: self.bar }`. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 0807142a7c35c..aca251da71d39 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -174,7 +174,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, path: &[Segment], span: Span, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, res: Option, ) -> BaseError { // Make the base error. @@ -318,7 +318,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None) } else { let mod_path = &path[..path.len() - 1]; - let mod_res = self.resolve_path(mod_path, Some(TypeNS), None); + let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source); let mod_prefix = match mod_res { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, @@ -419,7 +419,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], following_seg: Option<&Segment>, span: Span, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, res: Option, qself: Option<&QSelf>, ) -> (Diag<'tcx>, Vec) { @@ -1014,7 +1014,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_typo( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -1333,7 +1333,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, res: Option, span: Span, ) { @@ -1341,7 +1341,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { self.diag_metadata.currently_processing_impl_trait.clone() && let TyKind::Path(_, self_ty_path) = &self_ty.kind && let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None) + self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source) && let ModuleKind::Def(DefKind::Trait, ..) = module.kind && trait_ref.path.span == span && let PathSource::Trait(_) = source @@ -1449,13 +1449,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn get_single_associated_item( &mut self, path: &[Segment], - source: &PathSource<'_, '_, '_>, + source: &PathSource<'_, 'ast, 'ra>, filter_fn: &impl Fn(Res) -> bool, ) -> Option { if let crate::PathSource::TraitItem(_, _) = source { let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, None, None) + self.resolve_path(mod_path, None, None, *source) { let targets: Vec<_> = self .r @@ -1854,7 +1854,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { PathSource::Expr(Some(Expr { kind: ExprKind::Index(..) | ExprKind::Call(..), .. })) - | PathSource::Struct, + | PathSource::Struct(_), ) => { // Don't suggest macro if it's unstable. let suggestable = def_id.is_local() @@ -2502,7 +2502,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Search in module. let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, Some(TypeNS), None) + self.resolve_path(mod_path, Some(TypeNS), None, PathSource::Type) { self.r.add_module_candidates(module, &mut names, &filter_fn, None); } @@ -3673,7 +3673,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if let TyKind::Path(None, path) = &ty.kind { // Check if the path being borrowed is likely to be owned. let path: Vec<_> = Segment::from_path(path); - match self.resolve_path(&path, Some(TypeNS), None) { + match self.resolve_path( + &path, + Some(TypeNS), + None, + PathSource::Type, + ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { match module.res() { Some(Res::PrimTy(PrimTy::Str)) => { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 23f44ff16583e..b43f71913d9ba 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -822,6 +822,7 @@ struct PrivacyError<'ra> { parent_scope: ParentScope<'ra>, /// Is the format `use a::{b,c}`? single_nested: bool, + source: Option, } #[derive(Debug)] @@ -1064,6 +1065,7 @@ pub struct Resolver<'ra, 'tcx> { /// N.B., this is used only for better diagnostics, not name resolution itself. field_names: LocalDefIdMap>, + field_defaults: LocalDefIdMap>, /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. /// Used for hints during error reporting. @@ -1538,6 +1540,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { extern_prelude, field_names: Default::default(), + field_defaults: Default::default(), field_visibility_spans: FxHashMap::default(), determined_imports: Vec::new(), @@ -2318,6 +2321,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } + fn field_defaults(&self, def_id: DefId) -> Option> { + match def_id.as_local() { + Some(def_id) => self.field_defaults.get(&def_id).cloned(), + None => Some( + self.tcx + .associated_item_def_ids(def_id) + .iter() + .filter_map(|&def_id| { + self.tcx.default_field(def_id).map(|_| self.tcx.item_name(def_id)) + }) + .collect(), + ), + } + } + /// Checks if an expression refers to a function marked with /// `#[rustc_legacy_const_generics]` and returns the argument index list /// from the attribute. diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 79244827059da..16941a32bb16e 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -196,7 +196,10 @@ pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshn /// /// This checks the build triple platform to confirm we're usable at all, and if LLVM /// with/without assertions is available. -pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) -> bool { +pub(crate) fn is_ci_llvm_available_for_target( + host_target: &TargetSelection, + asserts: bool, +) -> bool { // This is currently all tier 1 targets and tier 2 targets with host tools // (since others may not have CI artifacts) // https://doc.rust-lang.org/rustc/platform-support.html#tier-1 @@ -235,8 +238,8 @@ pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) -> ("x86_64-unknown-netbsd", false), ]; - if !supported_platforms.contains(&(&*config.host_target.triple, asserts)) - && (asserts || !supported_platforms.contains(&(&*config.host_target.triple, true))) + if !supported_platforms.contains(&(&*host_target.triple, asserts)) + && (asserts || !supported_platforms.contains(&(&*host_target.triple, true))) { return false; } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index eb1ac8c637ffe..3eadb1083f400 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -893,21 +893,25 @@ impl Config { let default = config.channel == "dev"; config.omit_git_hash = rust_omit_git_hash.unwrap_or(default); - config.rust_info = config.git_info(config.omit_git_hash, &config.src); + config.rust_info = git_info(&config.exec_ctx, config.omit_git_hash, &config.src); config.cargo_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/cargo")); - config.rust_analyzer_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/rust-analyzer")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/cargo")); + config.rust_analyzer_info = git_info( + &config.exec_ctx, + config.omit_git_hash, + &config.src.join("src/tools/rust-analyzer"), + ); config.clippy_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/clippy")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/clippy")); config.miri_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/miri")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/miri")); config.rustfmt_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/rustfmt")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/rustfmt")); config.enzyme_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/enzyme")); - config.in_tree_llvm_info = config.git_info(false, &config.src.join("src/llvm-project")); - config.in_tree_gcc_info = config.git_info(false, &config.src.join("src/gcc")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/enzyme")); + config.in_tree_llvm_info = + git_info(&config.exec_ctx, false, &config.src.join("src/llvm-project")); + config.in_tree_gcc_info = git_info(&config.exec_ctx, false, &config.src.join("src/gcc")); config.vendor = build_vendor.unwrap_or( config.rust_info.is_from_tarball() @@ -947,11 +951,18 @@ impl Config { ); } - config.download_rustc_commit = config.download_ci_rustc_commit( - rust_download_rustc, - debug_assertions_requested, - config.llvm_assertions, - ); + let dwn_ctx = DownloadContext::from(&config); + config.download_rustc_commit = + download_ci_rustc_commit(dwn_ctx, rust_download_rustc, config.llvm_assertions); + + if debug_assertions_requested { + eprintln!( + "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ + rustc is not currently built with debug assertions." + ); + // We need to put this later down_ci_rustc_commit. + config.download_rustc_commit = None; + } if let Some(t) = toml.target { for (triple, cfg) in t { @@ -1157,8 +1168,9 @@ impl Config { "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." ); + let dwn_ctx = DownloadContext::from(&config); let channel = - config.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned(); + read_file_by_commit(dwn_ctx, Path::new("src/ci/channel"), commit).trim().to_owned(); config.channel = channel; } @@ -1193,8 +1205,9 @@ impl Config { config.llvm_enable_warnings = llvm_enable_warnings.unwrap_or(false); config.llvm_build_config = llvm_build_config.clone().unwrap_or(Default::default()); + let dwn_ctx = DownloadContext::from(&config); config.llvm_from_ci = - config.parse_download_ci_llvm(llvm_download_ci_llvm, config.llvm_assertions); + parse_download_ci_llvm(dwn_ctx, llvm_download_ci_llvm, config.llvm_assertions); if config.llvm_from_ci { let warn = |option: &str| { @@ -1256,7 +1269,8 @@ impl Config { if config.llvm_from_ci { let triple = &config.host_target.triple; - let ci_llvm_bin = config.ci_llvm_root().join("bin"); + let dwn_ctx = DownloadContext::from(&config); + let ci_llvm_bin = ci_llvm_root(dwn_ctx).join("bin"); let build_target = config .target_config .entry(config.host_target) @@ -1297,7 +1311,8 @@ impl Config { ); } - if config.lld_enabled && config.is_system_llvm(config.host_target) { + let dwn_ctx = DownloadContext::from(&config); + if config.lld_enabled && is_system_llvm(dwn_ctx, config.host_target) { panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config."); } @@ -1432,14 +1447,8 @@ impl Config { /// Returns the content of the given file at a specific commit. pub(crate) fn read_file_by_commit(&self, file: &Path, commit: &str) -> String { - assert!( - self.rust_info.is_managed_git_subrepository(), - "`Config::read_file_by_commit` is not supported in non-git sources." - ); - - let mut git = helpers::git(Some(&self.src)); - git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); - git.run_capture_stdout(self).stdout() + let dwn_ctx = DownloadContext::from(self); + read_file_by_commit(dwn_ctx, file, commit) } /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. @@ -1510,8 +1519,8 @@ impl Config { /// The absolute path to the downloaded LLVM artifacts. pub(crate) fn ci_llvm_root(&self) -> PathBuf { - assert!(self.llvm_from_ci); - self.out.join(self.host_target).join("ci-llvm") + let dwn_ctx = DownloadContext::from(self); + ci_llvm_root(dwn_ctx) } /// Directory where the extracted `rustc-dev` component is stored. @@ -1674,261 +1683,14 @@ impl Config { ), )] pub(crate) fn update_submodule(&self, relative_path: &str) { - if self.rust_info.is_from_tarball() || !self.submodules() { - return; - } - - let absolute_path = self.src.join(relative_path); - - // NOTE: This check is required because `jj git clone` doesn't create directories for - // submodules, they are completely ignored. The code below assumes this directory exists, - // so create it here. - if !absolute_path.exists() { - t!(fs::create_dir_all(&absolute_path)); - } - - // NOTE: The check for the empty directory is here because when running x.py the first time, - // the submodule won't be checked out. Check it out now so we can build it. - if !self.git_info(false, &absolute_path).is_managed_git_subrepository() - && !helpers::dir_is_empty(&absolute_path) - { - return; - } - - // Submodule updating actually happens during in the dry run mode. We need to make sure that - // all the git commands below are actually executed, because some follow-up code - // in bootstrap might depend on the submodules being checked out. Furthermore, not all - // the command executions below work with an empty output (produced during dry run). - // Therefore, all commands below are marked with `run_in_dry_run()`, so that they also run in - // dry run mode. - let submodule_git = || { - let mut cmd = helpers::git(Some(&absolute_path)); - cmd.run_in_dry_run(); - cmd - }; - - // Determine commit checked out in submodule. - let checked_out_hash = - submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(self).stdout(); - let checked_out_hash = checked_out_hash.trim_end(); - // Determine commit that the submodule *should* have. - let recorded = helpers::git(Some(&self.src)) - .run_in_dry_run() - .args(["ls-tree", "HEAD"]) - .arg(relative_path) - .run_capture_stdout(self) - .stdout(); - - let actual_hash = recorded - .split_whitespace() - .nth(2) - .unwrap_or_else(|| panic!("unexpected output `{recorded}`")); - - if actual_hash == checked_out_hash { - // already checked out - return; - } - - println!("Updating submodule {relative_path}"); - - helpers::git(Some(&self.src)) - .allow_failure() - .run_in_dry_run() - .args(["submodule", "-q", "sync"]) - .arg(relative_path) - .run(self); - - // Try passing `--progress` to start, then run git again without if that fails. - let update = |progress: bool| { - // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, - // even though that has no relation to the upstream for the submodule. - let current_branch = helpers::git(Some(&self.src)) - .allow_failure() - .run_in_dry_run() - .args(["symbolic-ref", "--short", "HEAD"]) - .run_capture(self); - - let mut git = helpers::git(Some(&self.src)).allow_failure(); - git.run_in_dry_run(); - if current_branch.is_success() { - // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. - // This syntax isn't accepted by `branch.{branch}`. Strip it. - let branch = current_branch.stdout(); - let branch = branch.trim(); - let branch = branch.strip_prefix("heads/").unwrap_or(branch); - git.arg("-c").arg(format!("branch.{branch}.remote=origin")); - } - git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]); - if progress { - git.arg("--progress"); - } - git.arg(relative_path); - git - }; - if !update(true).allow_failure().run(self) { - update(false).allow_failure().run(self); - } - - // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). - // diff-index reports the modifications through the exit status - let has_local_modifications = - !submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(self); - if has_local_modifications { - submodule_git().allow_failure().args(["stash", "push"]).run(self); - } - - submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(self); - submodule_git().allow_failure().args(["clean", "-qdfx"]).run(self); - - if has_local_modifications { - submodule_git().allow_failure().args(["stash", "pop"]).run(self); - } - } - - /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. - pub fn download_ci_rustc_commit( - &self, - download_rustc: Option, - debug_assertions_requested: bool, - llvm_assertions: bool, - ) -> Option { - if !is_download_ci_available(&self.host_target.triple, llvm_assertions) { - return None; - } - - // If `download-rustc` is not set, default to rebuilding. - let if_unchanged = match download_rustc { - // Globally default `download-rustc` to `false`, because some contributors don't use - // profiles for reasons such as: - // - They need to seamlessly switch between compiler/library work. - // - They don't want to use compiler profile because they need to override too many - // things and it's easier to not use a profile. - None | Some(StringOrBool::Bool(false)) => return None, - Some(StringOrBool::Bool(true)) => false, - Some(StringOrBool::String(s)) if s == "if-unchanged" => { - if !self.rust_info.is_managed_git_subrepository() { - println!( - "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources." - ); - crate::exit!(1); - } - - true - } - Some(StringOrBool::String(other)) => { - panic!("unrecognized option for download-rustc: {other}") - } - }; - - let commit = if self.rust_info.is_managed_git_subrepository() { - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let freshness = self.check_path_modifications(RUSTC_IF_UNCHANGED_ALLOWED_PATHS); - self.verbose(|| { - eprintln!("rustc freshness: {freshness:?}"); - }); - match freshness { - PathFreshness::LastModifiedUpstream { upstream } => upstream, - PathFreshness::HasLocalModifications { upstream } => { - if if_unchanged { - return None; - } - - if self.is_running_on_ci { - eprintln!("CI rustc commit matches with HEAD and we are in CI."); - eprintln!( - "`rustc.download-ci` functionality will be skipped as artifacts are not available." - ); - return None; - } - - upstream - } - PathFreshness::MissingUpstream => { - eprintln!("No upstream commit found"); - return None; - } - } - } else { - channel::read_commit_info_file(&self.src) - .map(|info| info.sha.trim().to_owned()) - .expect("git-commit-info is missing in the project root") - }; - - if debug_assertions_requested { - eprintln!( - "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ - rustc is not currently built with debug assertions." - ); - return None; - } - - Some(commit) - } - - pub fn parse_download_ci_llvm( - &self, - download_ci_llvm: Option, - asserts: bool, - ) -> bool { - // We don't ever want to use `true` on CI, as we should not - // download upstream artifacts if there are any local modifications. - let default = if self.is_running_on_ci { - StringOrBool::String("if-unchanged".to_string()) - } else { - StringOrBool::Bool(true) - }; - let download_ci_llvm = download_ci_llvm.unwrap_or(default); - - let if_unchanged = || { - if self.rust_info.is_from_tarball() { - // Git is needed for running "if-unchanged" logic. - println!("ERROR: 'if-unchanged' is only compatible with Git managed sources."); - crate::exit!(1); - } - - // Fetching the LLVM submodule is unnecessary for self-tests. - #[cfg(not(test))] - self.update_submodule("src/llvm-project"); - - // Check for untracked changes in `src/llvm-project` and other important places. - let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); - - // Return false if there are untracked changes, otherwise check if CI LLVM is available. - if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) } - }; - - match download_ci_llvm { - StringOrBool::Bool(b) => { - if !b && self.download_rustc_commit.is_some() { - panic!( - "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." - ); - } - - if b && self.is_running_on_ci { - // On CI, we must always rebuild LLVM if there were any modifications to it - panic!( - "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." - ); - } - - // If download-ci-llvm=true we also want to check that CI llvm is available - b && llvm::is_ci_llvm_available_for_target(self, asserts) - } - StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), - StringOrBool::String(other) => { - panic!("unrecognized option for download-ci-llvm: {other:?}") - } - } + let dwn_ctx = DownloadContext::from(self); + update_submodule(dwn_ctx, relative_path); } /// Returns true if any of the `paths` have been modified locally. pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool { - match self.check_path_modifications(paths) { - PathFreshness::LastModifiedUpstream { .. } => false, - PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, - } + let dwn_ctx = DownloadContext::from(self); + has_changes_from_upstream(dwn_ctx, paths) } /// Checks whether any of the given paths have been modified w.r.t. upstream. @@ -1949,10 +1711,6 @@ impl Config { .clone() } - pub fn ci_env(&self) -> CiEnv { - if self.is_running_on_ci { CiEnv::GitHubActions } else { CiEnv::None } - } - pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool { self.target_config.get(&target).and_then(|t| t.sanitizers).unwrap_or(self.sanitizers) } @@ -2041,15 +1799,8 @@ impl Config { /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. pub fn is_system_llvm(&self, target: TargetSelection) -> bool { - match self.target_config.get(&target) { - Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = self.llvm_from_ci && self.is_host_target(target); - !ci_llvm - } - // We're building from the in-tree src/llvm-project sources. - Some(Target { llvm_config: None, .. }) => false, - None => false, - } + let dwn_ctx = DownloadContext::from(self); + is_system_llvm(dwn_ctx, target) } /// Returns `true` if this is our custom, patched, version of LLVM. @@ -2340,3 +2091,365 @@ pub fn check_stage0_version( )); } } + +pub fn download_ci_rustc_commit<'a>( + dwn_ctx: impl AsRef>, + download_rustc: Option, + llvm_assertions: bool, +) -> Option { + let dwn_ctx = dwn_ctx.as_ref(); + + if !is_download_ci_available(&dwn_ctx.host_target.triple, llvm_assertions) { + return None; + } + + // If `download-rustc` is not set, default to rebuilding. + let if_unchanged = match download_rustc { + // Globally default `download-rustc` to `false`, because some contributors don't use + // profiles for reasons such as: + // - They need to seamlessly switch between compiler/library work. + // - They don't want to use compiler profile because they need to override too many + // things and it's easier to not use a profile. + None | Some(StringOrBool::Bool(false)) => return None, + Some(StringOrBool::Bool(true)) => false, + Some(StringOrBool::String(s)) if s == "if-unchanged" => { + if !dwn_ctx.rust_info.is_managed_git_subrepository() { + println!( + "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources." + ); + crate::exit!(1); + } + + true + } + Some(StringOrBool::String(other)) => { + panic!("unrecognized option for download-rustc: {other}") + } + }; + + let commit = if dwn_ctx.rust_info.is_managed_git_subrepository() { + // Look for a version to compare to based on the current commit. + // Only commits merged by bors will have CI artifacts. + let freshness = check_path_modifications_(dwn_ctx, RUSTC_IF_UNCHANGED_ALLOWED_PATHS); + dwn_ctx.exec_ctx.verbose(|| { + eprintln!("rustc freshness: {freshness:?}"); + }); + match freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream } => { + if if_unchanged { + return None; + } + + if dwn_ctx.is_running_on_ci { + eprintln!("CI rustc commit matches with HEAD and we are in CI."); + eprintln!( + "`rustc.download-ci` functionality will be skipped as artifacts are not available." + ); + return None; + } + + upstream + } + PathFreshness::MissingUpstream => { + eprintln!("No upstream commit found"); + return None; + } + } + } else { + channel::read_commit_info_file(dwn_ctx.src) + .map(|info| info.sha.trim().to_owned()) + .expect("git-commit-info is missing in the project root") + }; + + Some(commit) +} + +pub fn check_path_modifications_<'a>( + dwn_ctx: impl AsRef>, + paths: &[&'static str], +) -> PathFreshness { + let dwn_ctx = dwn_ctx.as_ref(); + // Checking path modifications through git can be relatively expensive (>100ms). + // We do not assume that the sources would change during bootstrap's execution, + // so we can cache the results here. + // Note that we do not use a static variable for the cache, because it would cause problems + // in tests that create separate `Config` instsances. + dwn_ctx + .path_modification_cache + .lock() + .unwrap() + .entry(paths.to_vec()) + .or_insert_with(|| { + check_path_modifications( + dwn_ctx.src, + &git_config(dwn_ctx.stage0_metadata), + paths, + CiEnv::current(), + ) + .unwrap() + }) + .clone() +} + +pub fn git_config(stage0_metadata: &build_helper::stage0_parser::Stage0) -> GitConfig<'_> { + GitConfig { + nightly_branch: &stage0_metadata.config.nightly_branch, + git_merge_commit_email: &stage0_metadata.config.git_merge_commit_email, + } +} + +pub fn parse_download_ci_llvm<'a>( + dwn_ctx: impl AsRef>, + download_ci_llvm: Option, + asserts: bool, +) -> bool { + let dwn_ctx = dwn_ctx.as_ref(); + + // We don't ever want to use `true` on CI, as we should not + // download upstream artifacts if there are any local modifications. + let default = if dwn_ctx.is_running_on_ci { + StringOrBool::String("if-unchanged".to_string()) + } else { + StringOrBool::Bool(true) + }; + let download_ci_llvm = download_ci_llvm.unwrap_or(default); + + let if_unchanged = || { + if dwn_ctx.rust_info.is_from_tarball() { + // Git is needed for running "if-unchanged" logic. + println!("ERROR: 'if-unchanged' is only compatible with Git managed sources."); + crate::exit!(1); + } + + // Fetching the LLVM submodule is unnecessary for self-tests. + #[cfg(not(test))] + update_submodule(dwn_ctx, "src/llvm-project"); + + // Check for untracked changes in `src/llvm-project` and other important places. + let has_changes = has_changes_from_upstream(dwn_ctx, LLVM_INVALIDATION_PATHS); + + // Return false if there are untracked changes, otherwise check if CI LLVM is available. + if has_changes { + false + } else { + llvm::is_ci_llvm_available_for_target(&dwn_ctx.host_target, asserts) + } + }; + + match download_ci_llvm { + StringOrBool::Bool(b) => { + if !b && dwn_ctx.download_rustc_commit.is_some() { + panic!( + "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." + ); + } + + if b && dwn_ctx.is_running_on_ci { + // On CI, we must always rebuild LLVM if there were any modifications to it + panic!( + "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." + ); + } + + // If download-ci-llvm=true we also want to check that CI llvm is available + b && llvm::is_ci_llvm_available_for_target(&dwn_ctx.host_target, asserts) + } + StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), + StringOrBool::String(other) => { + panic!("unrecognized option for download-ci-llvm: {other:?}") + } + } +} + +pub fn has_changes_from_upstream<'a>( + dwn_ctx: impl AsRef>, + paths: &[&'static str], +) -> bool { + let dwn_ctx = dwn_ctx.as_ref(); + match check_path_modifications_(dwn_ctx, paths) { + PathFreshness::LastModifiedUpstream { .. } => false, + PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, + } +} + +#[cfg_attr( + feature = "tracing", + instrument( + level = "trace", + name = "Config::update_submodule", + skip_all, + fields(relative_path = ?relative_path), + ), +)] +pub(crate) fn update_submodule<'a>(dwn_ctx: impl AsRef>, relative_path: &str) { + let dwn_ctx = dwn_ctx.as_ref(); + if dwn_ctx.rust_info.is_from_tarball() || !submodules_(dwn_ctx.submodules, dwn_ctx.rust_info) { + return; + } + + let absolute_path = dwn_ctx.src.join(relative_path); + + // NOTE: This check is required because `jj git clone` doesn't create directories for + // submodules, they are completely ignored. The code below assumes this directory exists, + // so create it here. + if !absolute_path.exists() { + t!(fs::create_dir_all(&absolute_path)); + } + + // NOTE: The check for the empty directory is here because when running x.py the first time, + // the submodule won't be checked out. Check it out now so we can build it. + if !git_info(dwn_ctx.exec_ctx, false, &absolute_path).is_managed_git_subrepository() + && !helpers::dir_is_empty(&absolute_path) + { + return; + } + + // Submodule updating actually happens during in the dry run mode. We need to make sure that + // all the git commands below are actually executed, because some follow-up code + // in bootstrap might depend on the submodules being checked out. Furthermore, not all + // the command executions below work with an empty output (produced during dry run). + // Therefore, all commands below are marked with `run_in_dry_run()`, so that they also run in + // dry run mode. + let submodule_git = || { + let mut cmd = helpers::git(Some(&absolute_path)); + cmd.run_in_dry_run(); + cmd + }; + + // Determine commit checked out in submodule. + let checked_out_hash = + submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(dwn_ctx.exec_ctx).stdout(); + let checked_out_hash = checked_out_hash.trim_end(); + // Determine commit that the submodule *should* have. + let recorded = helpers::git(Some(dwn_ctx.src)) + .run_in_dry_run() + .args(["ls-tree", "HEAD"]) + .arg(relative_path) + .run_capture_stdout(dwn_ctx.exec_ctx) + .stdout(); + + let actual_hash = recorded + .split_whitespace() + .nth(2) + .unwrap_or_else(|| panic!("unexpected output `{recorded}`")); + + if actual_hash == checked_out_hash { + // already checked out + return; + } + + println!("Updating submodule {relative_path}"); + + helpers::git(Some(dwn_ctx.src)) + .allow_failure() + .run_in_dry_run() + .args(["submodule", "-q", "sync"]) + .arg(relative_path) + .run(dwn_ctx.exec_ctx); + + // Try passing `--progress` to start, then run git again without if that fails. + let update = |progress: bool| { + // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, + // even though that has no relation to the upstream for the submodule. + let current_branch = helpers::git(Some(dwn_ctx.src)) + .allow_failure() + .run_in_dry_run() + .args(["symbolic-ref", "--short", "HEAD"]) + .run_capture(dwn_ctx.exec_ctx); + + let mut git = helpers::git(Some(dwn_ctx.src)).allow_failure(); + git.run_in_dry_run(); + if current_branch.is_success() { + // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. + // This syntax isn't accepted by `branch.{branch}`. Strip it. + let branch = current_branch.stdout(); + let branch = branch.trim(); + let branch = branch.strip_prefix("heads/").unwrap_or(branch); + git.arg("-c").arg(format!("branch.{branch}.remote=origin")); + } + git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]); + if progress { + git.arg("--progress"); + } + git.arg(relative_path); + git + }; + if !update(true).allow_failure().run(dwn_ctx.exec_ctx) { + update(false).allow_failure().run(dwn_ctx.exec_ctx); + } + + // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). + // diff-index reports the modifications through the exit status + let has_local_modifications = !submodule_git() + .allow_failure() + .args(["diff-index", "--quiet", "HEAD"]) + .run(dwn_ctx.exec_ctx); + if has_local_modifications { + submodule_git().allow_failure().args(["stash", "push"]).run(dwn_ctx.exec_ctx); + } + + submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(dwn_ctx.exec_ctx); + submodule_git().allow_failure().args(["clean", "-qdfx"]).run(dwn_ctx.exec_ctx); + + if has_local_modifications { + submodule_git().allow_failure().args(["stash", "pop"]).run(dwn_ctx.exec_ctx); + } +} + +pub fn git_info(exec_ctx: &ExecutionContext, omit_git_hash: bool, dir: &Path) -> GitInfo { + GitInfo::new(omit_git_hash, dir, exec_ctx) +} + +pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> bool { + // If not specified in config, the default is to only manage + // submodules if we're currently inside a git repository. + submodules.unwrap_or(rust_info.is_managed_git_subrepository()) +} + +/// Returns `true` if this is an external version of LLVM not managed by bootstrap. +/// In particular, we expect llvm sources to be available when this is false. +/// +/// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. +pub fn is_system_llvm<'a>( + dwn_ctx: impl AsRef>, + target: TargetSelection, +) -> bool { + let dwn_ctx = dwn_ctx.as_ref(); + match dwn_ctx.target_config.get(&target) { + Some(Target { llvm_config: Some(_), .. }) => { + let ci_llvm = dwn_ctx.llvm_from_ci && is_host_target(&dwn_ctx.host_target, &target); + !ci_llvm + } + // We're building from the in-tree src/llvm-project sources. + Some(Target { llvm_config: None, .. }) => false, + None => false, + } +} + +pub fn is_host_target(host_target: &TargetSelection, target: &TargetSelection) -> bool { + host_target == target +} + +pub(crate) fn ci_llvm_root<'a>(dwn_ctx: impl AsRef>) -> PathBuf { + let dwn_ctx = dwn_ctx.as_ref(); + assert!(dwn_ctx.llvm_from_ci); + dwn_ctx.out.join(dwn_ctx.host_target).join("ci-llvm") +} + +/// Returns the content of the given file at a specific commit. +pub(crate) fn read_file_by_commit<'a>( + dwn_ctx: impl AsRef>, + file: &Path, + commit: &str, +) -> String { + let dwn_ctx = dwn_ctx.as_ref(); + assert!( + dwn_ctx.rust_info.is_managed_git_subrepository(), + "`Config::read_file_by_commit` is not supported in non-git sources." + ); + + let mut git = helpers::git(Some(dwn_ctx.src)); + git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); + git.run_capture_stdout(dwn_ctx.exec_ctx).stdout() +} diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 7ec6c62a07d02..5ded44cef1447 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -1,14 +1,17 @@ +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::fs::{self, File}; use std::io::{BufRead, BufReader, BufWriter, ErrorKind, Write}; use std::path::{Path, PathBuf}; -use std::sync::OnceLock; +use std::sync::{Arc, Mutex, OnceLock}; +use build_helper::git::PathFreshness; use xz2::bufread::XzDecoder; -use crate::core::config::{BUILDER_CONFIG_FILENAME, TargetSelection}; +use crate::core::config::{BUILDER_CONFIG_FILENAME, Target, TargetSelection}; use crate::utils::build_stamp::BuildStamp; +use crate::utils::channel; use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{exe, hex_encode, move_file}; use crate::{Config, t}; @@ -398,14 +401,21 @@ impl Config { /// Only should be used for pre config initialization downloads. pub(crate) struct DownloadContext<'a> { - host_target: TargetSelection, - out: &'a Path, - patch_binaries_for_nix: Option, - exec_ctx: &'a ExecutionContext, - stage0_metadata: &'a build_helper::stage0_parser::Stage0, - llvm_assertions: bool, - bootstrap_cache_path: &'a Option, - is_running_on_ci: bool, + pub path_modification_cache: Arc, PathFreshness>>>, + pub src: &'a Path, + pub rust_info: &'a channel::GitInfo, + pub submodules: &'a Option, + pub download_rustc_commit: &'a Option, + pub host_target: TargetSelection, + pub llvm_from_ci: bool, + pub target_config: &'a HashMap, + pub out: &'a Path, + pub patch_binaries_for_nix: Option, + pub exec_ctx: &'a ExecutionContext, + pub stage0_metadata: &'a build_helper::stage0_parser::Stage0, + pub llvm_assertions: bool, + pub bootstrap_cache_path: &'a Option, + pub is_running_on_ci: bool, } impl<'a> AsRef> for DownloadContext<'a> { @@ -417,7 +427,14 @@ impl<'a> AsRef> for DownloadContext<'a> { impl<'a> From<&'a Config> for DownloadContext<'a> { fn from(value: &'a Config) -> Self { DownloadContext { + path_modification_cache: value.path_modification_cache.clone(), + src: &value.src, host_target: value.host_target, + rust_info: &value.rust_info, + download_rustc_commit: &value.download_rustc_commit, + submodules: &value.submodules, + llvm_from_ci: value.llvm_from_ci, + target_config: &value.target_config, out: &value.out, patch_binaries_for_nix: value.patch_binaries_for_nix, exec_ctx: &value.exec_ctx, diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 9bcf64dd62e2a..d85f6f5fdd5c7 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -10,6 +10,7 @@ LL | Box::new(|| x) | | | captured by this `Fn` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | Box::new(|| x.clone()) diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index 732af1593d606..e9e054407662f 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -12,6 +12,11 @@ LL | let _h = to_fn_once(move || -> isize { *bar }); | | | `bar` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/borrowck-move-by-capture.rs:3:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index af65deb16dcf8..ef02280888671 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -13,6 +13,11 @@ LL | LL | self.b; | ^^^^^^ `self.b` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-103624.rs:7:36 + | +LL | async fn spawn_blocking(f: impl (Fn() -> T) + Send + Sync + 'static) -> T { + | ^^^^^^^^^^^ note: if `StructB` implemented `Clone`, you could clone the value --> $DIR/issue-103624.rs:23:1 | diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index a0c7cac2addd0..043e336cd86df 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -10,6 +10,11 @@ LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-87456-point-to-closure.rs:3:24 + | +LL | fn take_mut(_val: impl FnMut()) {} + | ^^^^^^^ help: consider borrowing here | LL | let _foo: String = &val; diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 177e9c8d2487e..d33330413103f 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -10,6 +10,11 @@ LL | y.into_iter(); | | | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:5:28 + | +LL | fn call(f: F) where F : Fn() { + | ^^^^ note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index 42ac632256401..b6d8f08616383 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -10,6 +10,7 @@ LL | id(Box::new(|| *v)) | | | captured by this `FnMut` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: if `T` implemented `Clone`, you could clone the value --> $DIR/issue-4335.rs:5:10 | diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 51d0f85c031f5..dfc983bf48744 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -10,6 +10,11 @@ LL | let _f = to_fn(|| test(i)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:3:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let _f = to_fn(|| test(i.clone())); diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 5754603700653..7f9a8e50dae66 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -10,6 +10,11 @@ LL | expect_fn(|| drop(x.0)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-52663-span-decl-captured-variable.rs:1:33 + | +LL | fn expect_fn(f: F) where F : Fn() { + | ^^^^ help: consider cloning the value if the performance cost is acceptable | LL | expect_fn(|| drop(x.0.clone())); diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index f37dc320fa315..8081f7b3a8b0f 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -44,6 +44,7 @@ LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | foo(f.clone()); diff --git a/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs b/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs index b315df5dba287..a1c9645a1ae23 100644 --- a/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs +++ b/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs @@ -3,3 +3,13 @@ pub struct A { pub a: isize = 42, } + +struct Priv; + +pub struct B { + pub a: Priv = Priv, +} + +pub struct C { + pub a: Priv, +} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs new file mode 100644 index 0000000000000..047505c3f1cab --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs @@ -0,0 +1,29 @@ +//@ aux-build:struct_field_default.rs +#![feature(default_field_values)] + +extern crate struct_field_default as xc; + +use m::S; + +mod m { + pub struct S { + pub field: () = (), + pub field1: Priv1 = Priv1 {}, + pub field2: Priv2 = Priv2, + } + struct Priv1 {} + struct Priv2; +} + +fn main() { + let _ = S { field: (), field1: m::Priv1 {} }; + //~^ ERROR missing field `field2` + //~| ERROR struct `Priv1` is private + let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; + //~^ ERROR struct `Priv1` is private + //~| ERROR unit struct `Priv2` is private + let _ = xc::B { a: xc::Priv }; + //~^ ERROR unit struct `Priv` is private + let _ = xc::C { a: xc::Priv }; + //~^ ERROR unit struct `Priv` is private +} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr new file mode 100644 index 0000000000000..66de73561db28 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr @@ -0,0 +1,105 @@ +error[E0603]: struct `Priv1` is private + --> $DIR/non-exhaustive-ctor-2.rs:19:39 + | +LL | let _ = S { field: (), field1: m::Priv1 {} }; + | ------ ^^^^^ private struct + | | + | while setting this field + | +note: the struct `Priv1` is defined here + --> $DIR/non-exhaustive-ctor-2.rs:14:4 + | +LL | struct Priv1 {} + | ^^^^^^^^^^^^ +help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression + | +LL - let _ = S { field: (), field1: m::Priv1 {} }; +LL + let _ = S { field: (), .. }; + | + +error[E0603]: struct `Priv1` is private + --> $DIR/non-exhaustive-ctor-2.rs:22:39 + | +LL | let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; + | ------ ^^^^^ private struct + | | + | while setting this field + | +note: the struct `Priv1` is defined here + --> $DIR/non-exhaustive-ctor-2.rs:14:4 + | +LL | struct Priv1 {} + | ^^^^^^^^^^^^ +help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression + | +LL - let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; +LL + let _ = S { field: (), field2: m::Priv2, .. }; + | + +error[E0603]: unit struct `Priv2` is private + --> $DIR/non-exhaustive-ctor-2.rs:22:60 + | +LL | let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; + | ------ ^^^^^ private unit struct + | | + | while setting this field + | +note: the unit struct `Priv2` is defined here + --> $DIR/non-exhaustive-ctor-2.rs:15:4 + | +LL | struct Priv2; + | ^^^^^^^^^^^^^ +help: the type `Priv2` of field `field2` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression + | +LL - let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; +LL + let _ = S { field: (), field1: m::Priv1 {}, .. }; + | + +error[E0603]: unit struct `Priv` is private + --> $DIR/non-exhaustive-ctor-2.rs:25:28 + | +LL | let _ = xc::B { a: xc::Priv }; + | - ^^^^ private unit struct + | | + | while setting this field + | +note: the unit struct `Priv` is defined here + --> $DIR/auxiliary/struct_field_default.rs:7:1 + | +LL | struct Priv; + | ^^^^^^^^^^^ +help: the type `Priv` of field `a` is private, but you can construct the default value defined for it in `B` using `..` in the struct initializer expression + | +LL - let _ = xc::B { a: xc::Priv }; +LL + let _ = xc::B { .. }; + | + +error[E0603]: unit struct `Priv` is private + --> $DIR/non-exhaustive-ctor-2.rs:27:28 + | +LL | let _ = xc::C { a: xc::Priv }; + | - ^^^^ private unit struct + | | + | while setting this field + | +note: the unit struct `Priv` is defined here + --> $DIR/auxiliary/struct_field_default.rs:7:1 + | +LL | struct Priv; + | ^^^^^^^^^^^ + +error[E0063]: missing field `field2` in initializer of `S` + --> $DIR/non-exhaustive-ctor-2.rs:19:13 + | +LL | let _ = S { field: (), field1: m::Priv1 {} }; + | ^ missing `field2` + | +help: all remaining fields have default values, you can use those values with `..` + | +LL | let _ = S { field: (), field1: m::Priv1 {}, .. }; + | ++++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0063, E0603. +For more information about an error, try `rustc --explain E0063`. diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs index 44eac3691a3be..34088f219b2b8 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs @@ -11,8 +11,55 @@ struct X(Y); struct Y; fn consume_fn(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures fn consume_fnmut(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures + +trait T { + fn consume_fn(_f: F) { } + //~^ HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + fn method_consume_fn(&self, _f: F) { } + //~^ HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures +} +impl T for () {} pub fn main() { } @@ -73,6 +120,120 @@ fn move_into_fn() { }); } +fn move_into_assoc_fn() { + let e = Either::One(X(Y)); + let mut em = Either::One(X(Y)); + + let x = X(Y); + + // move into Fn + + <() as T>::consume_fn(|| { + let X(_t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) + | Either::Two(_t) => (), + } + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + + let X(mut _t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) + | Either::Two(mut _t) => (), + } + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + }); +} + +fn move_into_method() { + let e = Either::One(X(Y)); + let mut em = Either::One(X(Y)); + + let x = X(Y); + + // move into Fn + + ().method_consume_fn(|| { + let X(_t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) + | Either::Two(_t) => (), + } + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + + let X(mut _t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) + | Either::Two(mut _t) => (), + } + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + }); +} + fn move_into_fnmut() { let e = Either::One(X(Y)); let mut em = Either::One(X(Y)); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index edda2cbc735a2..132a31c8f7ceb 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:28:21 + --> $DIR/move-into-closure.rs:75:21 | LL | let x = X(Y); | - captured outer variable @@ -12,13 +12,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:31:34 + --> $DIR/move-into-closure.rs:78:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -32,13 +37,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:34:37 + --> $DIR/move-into-closure.rs:81:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -52,13 +62,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:37:15 + --> $DIR/move-into-closure.rs:84:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -75,13 +90,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:43:15 + --> $DIR/move-into-closure.rs:90:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -98,13 +118,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:51:25 + --> $DIR/move-into-closure.rs:98:25 | LL | let x = X(Y); | - captured outer variable @@ -118,13 +143,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:54:38 + --> $DIR/move-into-closure.rs:101:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -138,13 +168,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:57:41 + --> $DIR/move-into-closure.rs:104:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -158,13 +193,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:60:15 + --> $DIR/move-into-closure.rs:107:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -181,13 +221,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:66:15 + --> $DIR/move-into-closure.rs:113:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -204,13 +249,540 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:132:21 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +LL | let X(_t) = x; + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(_t) = &x; + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:135:34 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:138:37 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:141:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:147:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) => (), + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:155:25 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | let X(mut _t) = x; + | ------ ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(mut _t) = &x; + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:158:38 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:161:41 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:164:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:170:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) => (), + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:189:21 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +LL | let X(_t) = x; + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(_t) = &x; + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:192:34 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:195:37 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:198:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:204:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) => (), + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:212:25 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | let X(mut _t) = x; + | ------ ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(mut _t) = &x; + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:215:38 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:218:41 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:221:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:227:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) => (), + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:85:21 + --> $DIR/move-into-closure.rs:246:21 | LL | let x = X(Y); | - captured outer variable @@ -223,13 +795,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:88:34 + --> $DIR/move-into-closure.rs:249:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -243,13 +820,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:91:37 + --> $DIR/move-into-closure.rs:252:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -263,13 +845,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:94:15 + --> $DIR/move-into-closure.rs:255:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -286,13 +873,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:100:15 + --> $DIR/move-into-closure.rs:261:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -309,13 +901,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:108:25 + --> $DIR/move-into-closure.rs:269:25 | LL | let x = X(Y); | - captured outer variable @@ -329,13 +926,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:111:38 + --> $DIR/move-into-closure.rs:272:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -349,13 +951,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:114:41 + --> $DIR/move-into-closure.rs:275:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -369,13 +976,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:117:15 + --> $DIR/move-into-closure.rs:278:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -392,13 +1004,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:123:15 + --> $DIR/move-into-closure.rs:284:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -415,13 +1032,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:130:15 + --> $DIR/move-into-closure.rs:291:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -438,11 +1060,16 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + -error: aborting due to 21 previous errors +error: aborting due to 41 previous errors For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index c73e874b40368..c8aa6667b583f 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -14,6 +14,11 @@ LL | LL | var = Some(NotCopyable); | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move2.rs:1:1 | @@ -38,6 +43,12 @@ LL | move || { LL | LL | var = Some(NotCopyableButCloneable); | --- variable moved due to use in closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index 68c52352a6512..2c9a86c036be4 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -9,6 +9,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -37,6 +38,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -57,6 +63,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider borrowing here | LL | let x = &var; @@ -77,6 +84,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ { diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 8d9a61cb68126..9d87402a15bff 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -10,6 +10,11 @@ LL | let f = to_fn(|| drop(x)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn(|| drop(x.clone())); @@ -27,6 +32,11 @@ LL | let f = to_fn_mut(|| drop(x)); | | | captured by this `FnMut` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn_mut(|| drop(x.clone())); @@ -43,6 +53,12 @@ LL | let f = to_fn(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `Fn` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -55,6 +71,12 @@ LL | let f = to_fn_mut(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `FnMut` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ error: aborting due to 4 previous errors