From cca28656e5caf70a5d3fb2c2668683f397b946fd Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 29 Jan 2026 12:51:51 +0000 Subject: [PATCH 01/18] sess: `-Zbranch-protection` is a target modifier `-Zbranch-protection` only makes sense if the entire crate graph has the option set, otherwise the security properties that branch protection provides won't be effective. This flag is unstable so I don't think this warrants an MCP. --- compiler/rustc_session/src/options.rs | 2 +- .../aarch64-naked-fn-no-bti-prolog.rs | 2 +- .../rmake.rs | 1 + .../run-make/pointer-auth-link-with-c/rmake.rs | 18 +++++++++++++++--- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 74b3aa11d0d80..e9e4ddaa60562 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2240,7 +2240,7 @@ options! { (default: no)"), box_noalias: bool = (true, parse_bool, [TRACKED], "emit noalias metadata for box (default: yes)"), - branch_protection: Option = (None, parse_branch_protection, [TRACKED], + branch_protection: Option = (None, parse_branch_protection, [TRACKED TARGET_MODIFIER], "set options for branch target identification and pointer authentication on AArch64"), build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], "whether the stable interface is being built"), diff --git a/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs b/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs index 860ecc3cfcd04..430d4a59da6df 100644 --- a/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs +++ b/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti +//@ compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti -Cunsafe-allow-abi-mismatch=branch-protection //@ assembly-output: emit-asm //@ needs-asm-support //@ only-aarch64 diff --git a/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs b/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs index 2ac5fdee063c2..1d06fe118e067 100644 --- a/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs @@ -32,6 +32,7 @@ fn main() { .opt_level("2") .linker(&env_var("CLANG")) .link_arg("-fuse-ld=lld") + .arg("-Cunsafe-allow-abi-mismatch=branch-protection") .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") .input("test.rs") .output("test.bin") diff --git a/tests/run-make/pointer-auth-link-with-c/rmake.rs b/tests/run-make/pointer-auth-link-with-c/rmake.rs index 1ddcb79d64ff4..1ac68c95559c6 100644 --- a/tests/run-make/pointer-auth-link-with-c/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c/rmake.rs @@ -15,7 +15,11 @@ use run_make_support::{build_native_static_lib, cc, is_windows_msvc, llvm_ar, ru fn main() { build_native_static_lib("test"); - rustc().arg("-Zbranch-protection=bti,gcs,pac-ret,leaf").input("test.rs").run(); + rustc() + .arg("-Cunsafe-allow-abi-mismatch=branch-protection") + .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") + .input("test.rs") + .run(); run("test"); cc().arg("-v") .arg("-c") @@ -25,7 +29,11 @@ fn main() { .run(); let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); - rustc().arg("-Zbranch-protection=bti,gcs,pac-ret,leaf").input("test.rs").run(); + rustc() + .arg("-Cunsafe-allow-abi-mismatch=branch-protection") + .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") + .input("test.rs") + .run(); run("test"); // FIXME: +pc was only recently added to LLVM @@ -37,6 +45,10 @@ fn main() { // .run(); // let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; // llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); - // rustc().arg("-Zbranch-protection=bti,pac-ret,pc,leaf").input("test.rs").run(); + // rustc() + // .arg("-Cunsafe-allow-abi-mismatch=branch-protection") + // .arg("-Zbranch-protection=bti,pac-ret,pc,leaf") + // .input("test.rs") + // .run(); // run("test"); } From 965f1e76816ab6d837f83a2ef0c95af794f0cc8b Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Thu, 1 Jan 2026 18:53:27 +0100 Subject: [PATCH 02/18] Introduce #[diagnostic::on_move] This might be helpful for smart pointers to explains why they aren't Copy and what to do instead or just to let the user know that .clone() is very cheap and can be called without a performance penalty. --- .../src/attributes/diagnostic/mod.rs | 12 +++- .../src/attributes/diagnostic/on_move.rs | 72 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_borrowck/src/borrowck_errors.rs | 25 ++++--- .../src/diagnostics/conflict_errors.rs | 57 +++++++++++++-- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 7 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_lint/src/early/diagnostics.rs | 12 ++++ compiler/rustc_lint/src/lints.rs | 29 ++++++++ compiler/rustc_lint_defs/src/lib.rs | 8 +++ compiler/rustc_passes/src/check_attr.rs | 57 +++++++++++++++ compiler/rustc_passes/src/errors.rs | 7 ++ compiler/rustc_resolve/src/macros.rs | 2 +- compiler/rustc_span/src/symbol.rs | 2 + .../on_move/auxiliary/other.rs | 8 +++ .../error_is_shown_in_downstream_crates.rs | 14 ++++ ...error_is_shown_in_downstream_crates.stderr | 21 ++++++ .../on_move/on_move_simple.rs | 17 +++++ .../on_move/on_move_simple.stderr | 29 ++++++++ .../on_move/on_move_with_format.rs | 32 +++++++++ .../on_move/on_move_with_format.stderr | 55 ++++++++++++++ .../report_warning_on_duplicated_options.rs | 22 ++++++ ...eport_warning_on_duplicated_options.stderr | 49 +++++++++++++ .../report_warning_on_invalid_formats.rs | 18 +++++ .../report_warning_on_invalid_formats.stderr | 38 ++++++++++ ...ort_warning_on_invalid_meta_item_syntax.rs | 14 ++++ ...warning_on_invalid_meta_item_syntax.stderr | 38 ++++++++++ ...on_malformed_options_without_delimiters.rs | 18 +++++ ...alformed_options_without_delimiters.stderr | 44 ++++++++++++ ...g_on_malformed_options_without_literals.rs | 18 +++++ ..._malformed_options_without_literals.stderr | 44 ++++++++++++ .../report_warning_on_missing_options.rs | 14 ++++ .../report_warning_on_missing_options.stderr | 38 ++++++++++ .../on_move/report_warning_on_non_adt.rs | 23 ++++++ .../on_move/report_warning_on_non_adt.stderr | 41 +++++++++++ .../report_warning_on_unknown_options.rs | 19 +++++ .../report_warning_on_unknown_options.stderr | 38 ++++++++++ .../feature-gate-diagnostic-on-move.rs | 18 +++++ .../feature-gate-diagnostic-on-move.stderr | 29 ++++++++ 41 files changed, 975 insertions(+), 20 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_simple.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 8f114b3284485..63780676bead9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -22,6 +22,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem pub(crate) mod do_not_recommend; pub(crate) mod on_const; +pub(crate) mod on_move; pub(crate) mod on_unimplemented; #[derive(Copy, Clone)] @@ -32,6 +33,8 @@ pub(crate) enum Mode { DiagnosticOnUnimplemented, /// `#[diagnostic::on_const]` DiagnosticOnConst, + /// `#[diagnostic::on_move]` + DiagnosticOnMove, } fn merge_directives( @@ -113,6 +116,13 @@ fn parse_directive_items<'p, S: Stage>( span, ); } + Mode::DiagnosticOnMove => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnMoveAttr { span }, + span, + ); + } } continue; }} @@ -132,7 +142,7 @@ fn parse_directive_items<'p, S: Stage>( Mode::RustcOnUnimplemented => { cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); } - Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => { + Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, AttributeLintKind::IgnoredDiagnosticOption { diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs new file mode 100644 index 0000000000000..006b3b66658e0 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -0,0 +1,72 @@ +use rustc_feature::template; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; +use rustc_span::sym; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::target_checking::{ALL_TARGETS, AllowedTargets}; + +#[derive(Default)] +pub(crate) struct OnMoveParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnMoveParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + if !cx.features().diagnostic_on_move() { + return; + } + + let span = cx.attr_span; + self.span = Some(span); + let Some(list) = args.list() else { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnMove, + span, + ); + return; + }; + + if list.is_empty() { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter, + list.span, + ); + return; + } + + if let Some(directive) = parse_directive_items(cx, mode, list.mixed(), true) { + merge_directives(cx, &mut self.directive, (span, directive)); + } + } +} +impl AttributeParser for OnMoveParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_move], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnMove); + }, + )]; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnMove { span, directive: self.directive.map(|d| Box::new(d.1)) }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 190568bed508d..259a73de59853 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -29,6 +29,7 @@ use crate::attributes::debugger::*; use crate::attributes::deprecation::*; use crate::attributes::diagnostic::do_not_recommend::*; use crate::attributes::diagnostic::on_const::*; +use crate::attributes::diagnostic::on_move::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; @@ -149,6 +150,7 @@ attribute_parsers!( MacroUseParser, NakedParser, OnConstParser, + OnMoveParser, OnUnimplementedParser, RustcAlignParser, RustcAlignStaticParser, diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 0d3c554e41765..4fcb5f3b5a94d 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -324,18 +324,23 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { verb: &str, optional_adverb_for_moved: &str, moved_path: Option, + primary_message: Option, ) -> Diag<'infcx> { - let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); + if let Some(primary_message) = primary_message { + struct_span_code_err!(self.dcx(), use_span, E0382, "{}", primary_message) + } else { + let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); - struct_span_code_err!( - self.dcx(), - use_span, - E0382, - "{} of {}moved value{}", - verb, - optional_adverb_for_moved, - moved_path, - ) + struct_span_code_err!( + self.dcx(), + use_span, + E0382, + "{} of {}moved value{}", + verb, + optional_adverb_for_moved, + moved_path, + ) + } } pub(crate) fn cannot_borrow_path_as_mutable_because( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 641121597848c..7bbce730c1658 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,12 +6,16 @@ use std::ops::ControlFlow; use either::Either; use hir::{ClosureKind, Path}; use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err}; use rustc_hir as hir; +use rustc_hir::attrs::diagnostic::FormatArgs; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; -use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField}; +use rustc_hir::{ + CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField, find_attr, +}; use rustc_middle::bug; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::{ @@ -138,6 +142,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let partial_str = if is_partial_move { "partial " } else { "" }; let partially_str = if is_partial_move { "partially " } else { "" }; + let (on_move_message, on_move_label, on_move_notes) = if let ty::Adt(item_def, args) = + self.body.local_decls[moved_place.local].ty.kind() + && let Some(Some(directive)) = find_attr!(self.infcx.tcx, item_def.did(), OnMove { directive, .. } => directive) + { + let item_name = self.infcx.tcx.item_name(item_def.did()).to_string(); + let mut generic_args: Vec<_> = self + .infcx + .tcx + .generics_of(item_def.did()) + .own_params + .iter() + .filter_map(|param| Some((param.name, args[param.index as usize].to_string()))) + .collect(); + generic_args.push((kw::SelfUpper, item_name)); + + let args = FormatArgs { + this: String::new(), + trait_sugared: String::new(), + item_context: "", + generic_args, + }; + ( + directive.message.as_ref().map(|e| e.1.format(&args)), + directive.label.as_ref().map(|e| e.1.format(&args)), + directive.notes.iter().map(|e| e.format(&args)).collect(), + ) + } else { + (None, None, ThinVec::new()) + }; + let mut err = self.cannot_act_on_moved_value( span, desired_action.as_noun(), @@ -146,8 +180,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { moved_place, DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, ), + on_move_message, ); + for note in on_move_notes { + err.note(note); + } + let reinit_spans = maybe_reinitialized_locations .iter() .take(3) @@ -275,12 +314,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if needs_note { if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; - err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move, - ty, - place: ¬e_msg, - span, - }); + if let Some(on_move_label) = on_move_label { + err.span_label(span, on_move_label); + } else { + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move, + ty, + place: ¬e_msg, + span, + }); + } } else { err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note { is_partial_move, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3a2f548902d11..d74450e67cf1d 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1587,6 +1587,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool match sym { sym::on_unimplemented | sym::do_not_recommend => true, sym::on_const => features.diagnostic_on_const(), + sym::on_move => features.diagnostic_on_move(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 39e886227d946..18e03d94a997f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -470,6 +470,8 @@ declare_features! ( (unstable, derive_from, "1.91.0", Some(144889)), /// Allows giving non-const impls custom diagnostic messages if attempted to be used as const (unstable, diagnostic_on_const, "1.93.0", Some(143874)), + /// Allows giving on-move borrowck custom diagnostic messages for a type + (unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(150935)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index e8476c3d8c73b..deb22fcaa269a 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1180,13 +1180,18 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents `#[diagnostic::on_move]` + OnMove { + span: Span, + directive: Option>, + }, + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. OnUnimplemented { span: Span, /// None if the directive was malformed in some way. directive: Option>, }, - /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 27128f6996370..c19fc6976c6e6 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -77,6 +77,7 @@ impl AttributeKind { NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc OnConst { .. } => Yes, + OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, Optimize(..) => No, PanicRuntime => No, diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 458553fa747ca..c282eb34cf4d6 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -498,6 +498,18 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &AttributeLintKind::MissingOptionsForOnConst => { lints::MissingOptionsForOnConstAttr.into_diag(dcx, level) } + &AttributeLintKind::MalformedOnMoveAttr { span } => { + lints::MalformedOnMoveAttrLint { span }.into_diag(dcx, level) + } + &AttributeLintKind::OnMoveMalformedFormatLiterals { name } => { + lints::OnMoveMalformedFormatLiterals { name }.into_diag(dcx, level) + } + &AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter => { + lints::OnMoveMalformedAttrExpectedLiteralOrDelimiter.into_diag(dcx, level) + } + &AttributeLintKind::MissingOptionsForOnMove => { + lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level) + } } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d8b62e81b0cbc..02448035216ea 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3953,6 +3953,11 @@ pub(crate) struct MissingOptionsForOnUnimplementedAttr; #[help("at least one of the `message`, `note` and `label` options are expected")] pub(crate) struct MissingOptionsForOnConstAttr; +#[derive(Diagnostic)] +#[diag("missing options for `on_move` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnMoveAttr; + #[derive(Diagnostic)] #[diag("malformed `on_unimplemented` attribute")] #[help("only `message`, `note` and `label` are allowed as options")] @@ -3973,3 +3978,27 @@ pub(crate) struct MalformedOnConstAttrLint { #[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] #[note("this method was used to add checks to the `Eq` derive macro")] pub(crate) struct EqInternalMethodImplemented; + +#[derive(Diagnostic)] +#[diag("unknown or malformed `on_move` attribute")] +#[help( + "only `message`, `note` and `label` are allowed as options. Their values must be string literals" +)] +pub(crate) struct MalformedOnMoveAttrLint { + #[label("invalid option found here")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help("expect `Self` as format argument")] +pub(crate) struct OnMoveMalformedFormatLiterals { + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("expected a literal or missing delimiter")] +#[help( + "only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma" +)] +pub(crate) struct OnMoveMalformedAttrExpectedLiteralOrDelimiter; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1492df50a418a..ef7067b25baa3 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -840,6 +840,9 @@ pub enum AttributeLintKind { MalformedOnConstAttr { span: Span, }, + MalformedOnMoveAttr { + span: Span, + }, MalformedDiagnosticFormat { warning: FormatWarning, }, @@ -855,6 +858,11 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + MissingOptionsForOnMove, + OnMoveMalformedFormatLiterals { + name: Symbol, + }, + OnMoveMalformedAttrExpectedLiteralOrDelimiter, } #[derive(Debug, Clone, HashStable_Generic)] diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bec6ab7e83551..e495088c55649 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -74,6 +74,10 @@ struct DiagnosticOnConstOnlyForNonConstTraitImpls { item_span: Span, } +#[derive(Diagnostic)] +#[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")] +struct DiagnosticOnMoveOnlyForAdt; + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -233,6 +237,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} + Attribute::Parsed(AttributeKind::OnMove { span, directive }) => { + self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref()) + }, Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -684,6 +691,56 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // The traits' or the impls'? } + /// Checks if `#[diagnostic::on_move]` is applied to an ADT definition + fn check_diagnostic_on_move( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + directive: Option<&Directive>, + ) { + if !matches!(target, Target::Enum | Target::Struct | Target::Union) { + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnMoveOnlyForAdt, + ); + } + + if let Some(directive) = directive { + if let Node::Item(Item { + kind: + ItemKind::Struct(_, generics, _) + | ItemKind::Enum(_, generics, _) + | ItemKind::Union(_, generics, _), + .. + }) = self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + errors::OnMoveMalformedFormatLiterals { name: argument_name }, + ) + } + }); + } + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 7c7698ac3bb8e..de3b2e96f6ddd 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1434,3 +1434,10 @@ pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { #[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] pub help: bool, } + +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] +pub(crate) struct OnMoveMalformedFormatLiterals { + pub name: Symbol, +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 551d89ee6022a..54bca9be57650 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -707,7 +707,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } const DIAG_ATTRS: &[Symbol] = - &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const]; + &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const, sym::on_move]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 257ac3f51c2c1..1e59ff33f8dc8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -797,6 +797,7 @@ symbols! { diagnostic, diagnostic_namespace, diagnostic_on_const, + diagnostic_on_move, dialect, direct, discriminant_kind, @@ -1406,6 +1407,7 @@ symbols! { omit_gdb_pretty_printer_section, on, on_const, + on_move, on_unimplemented, opaque, opaque_generic_const_args, diff --git a/tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs b/tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs new file mode 100644 index 0000000000000..6cff8f4a19b52 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs @@ -0,0 +1,8 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +#[derive(Debug)] +pub struct Foo; diff --git a/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs new file mode 100644 index 0000000000000..69c61e62cfc60 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs @@ -0,0 +1,14 @@ +//@ aux-build:other.rs + +extern crate other; + +use other::Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr new file mode 100644 index 0000000000000..8cfe8d6af3f6c --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr @@ -0,0 +1,21 @@ +error[E0382]: Foo + --> $DIR/error_is_shown_in_downstream_crates.rs:12:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/error_is_shown_in_downstream_crates.rs:7:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs b/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs new file mode 100644 index 0000000000000..e6112e783bd19 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr b/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr new file mode 100644 index 0000000000000..483ff5a407bba --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr @@ -0,0 +1,29 @@ +error[E0382]: Foo + --> $DIR/on_move_simple.rs:15:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_simple.rs:10:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/on_move_simple.rs:8:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs new file mode 100644 index 0000000000000..f2025c2b2271b --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs @@ -0,0 +1,32 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo for {Self}", + label = "Bar for {Self}", +)] +#[derive(Debug)] +struct Foo; + +#[diagnostic::on_move( + message="Foo for {X}", + label="Bar for {X}", +)] +struct MyType { + _x: X, +} + +fn takes_foo(_: Foo) {} + +fn takes_mytype(_: MyType) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo for Foo + + let mytype = MyType { _x: 0 }; + takes_mytype(mytype); + let baz = mytype; + //~^ERROR Foo for i32 +} diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr new file mode 100644 index 0000000000000..6868f1571be8f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr @@ -0,0 +1,55 @@ +error[E0382]: Foo for Foo + --> $DIR/on_move_with_format.rs:25:15 + | +LL | let foo = Foo; + | --- Bar for Foo +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_with_format.rs:18:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/on_move_with_format.rs:8:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error[E0382]: Foo for i32 + --> $DIR/on_move_with_format.rs:30:15 + | +LL | let mytype = MyType { _x: 0 }; + | ------ Bar for i32 +LL | takes_mytype(mytype); + | ------ value moved here +LL | let baz = mytype; + | ^^^^^^ value used here after move + | +note: consider changing this parameter type in function `takes_mytype` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_with_format.rs:20:23 + | +LL | fn takes_mytype(_: MyType) {} + | ------------ ^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function +note: if `MyType` implemented `Clone`, you could clone the value + --> $DIR/on_move_with_format.rs:14:1 + | +LL | struct MyType { + | ^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_mytype(mytype); + | ------ you could clone this value + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs new file mode 100644 index 0000000000000..1af3daf0f3152 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs @@ -0,0 +1,22 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "first message", + label = "first label", +)] +#[diagnostic::on_move( + message = "second message", + //~^ WARN `message` is ignored due to previous definition of `message` [malformed_diagnostic_attributes] + label = "second label", + //~^ WARN `label` is ignored due to previous definition of `label` [malformed_diagnostic_attributes] +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR first message +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr new file mode 100644 index 0000000000000..f02f970149819 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr @@ -0,0 +1,49 @@ +warning: `message` is ignored due to previous definition of `message` + --> $DIR/report_warning_on_duplicated_options.rs:8:5 + | +LL | message = "first message", + | ------------------------- `message` is first declared here +... +LL | message = "second message", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is later redundantly declared here + | + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: `label` is ignored due to previous definition of `label` + --> $DIR/report_warning_on_duplicated_options.rs:10:5 + | +LL | label = "first label", + | --------------------- `label` is first declared here +... +LL | label = "second label", + | ^^^^^^^^^^^^^^^^^^^^^^ `label` is later redundantly declared here + +error[E0382]: first message + --> $DIR/report_warning_on_duplicated_options.rs:20:15 + | +LL | let foo = Foo; + | --- first label +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_duplicated_options.rs:15:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_duplicated_options.rs:13:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs new file mode 100644 index 0000000000000..f403ad1ff480f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo {Baz}", + //~^WARN unknown parameter `Baz` + label = "Bar", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr new file mode 100644 index 0000000000000..efb43c3562a4b --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr @@ -0,0 +1,38 @@ +warning: unknown parameter `Baz` + --> $DIR/report_warning_on_invalid_formats.rs:4:21 + | +LL | message = "Foo {Baz}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo {Baz} + --> $DIR/report_warning_on_invalid_formats.rs:16:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_invalid_formats.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_invalid_formats.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs new file mode 100644 index 0000000000000..2050403210d58 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs @@ -0,0 +1,14 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move = "foo"] +//~^WARN missing options for `on_move` attribute [malformed_diagnostic_attributes] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr new file mode 100644 index 0000000000000..39992b02e5804 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr @@ -0,0 +1,38 @@ +warning: missing options for `on_move` attribute + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:3:1 + | +LL | #[diagnostic::on_move = "foo"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:12:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:7:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs new file mode 100644 index 0000000000000..b5bb103d3f27e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( +//~^ WARN expected a literal or missing delimiter [malformed_diagnostic_attributes] +//~| HELP only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + message = "Foo" + label = "Bar", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr new file mode 100644 index 0000000000000..00d0657bfcbd5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr @@ -0,0 +1,44 @@ +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:3:22 + | +LL | #[diagnostic::on_move( + | ______________________^ +LL | | +LL | | +LL | | message = "Foo" +LL | | label = "Bar", +LL | | )] + | |_^ + | + = help: only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:16:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs new file mode 100644 index 0000000000000..c676b87ad8570 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( +//~^ WARN expected a literal or missing delimiter [malformed_diagnostic_attributes] +//~| HELP only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + message = Foo, + label = "Bar", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr new file mode 100644 index 0000000000000..6dc230b68c5fe --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr @@ -0,0 +1,44 @@ +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_malformed_options_without_literals.rs:3:22 + | +LL | #[diagnostic::on_move( + | ______________________^ +LL | | +LL | | +LL | | message = Foo, +LL | | label = "Bar", +LL | | )] + | |_^ + | + = help: only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options_without_literals.rs:16:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_malformed_options_without_literals.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_malformed_options_without_literals.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs new file mode 100644 index 0000000000000..e5603fd24ec98 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs @@ -0,0 +1,14 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move] +//~^WARN missing options for `on_move` attribute [malformed_diagnostic_attributes] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr new file mode 100644 index 0000000000000..f4e6d69faecb9 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr @@ -0,0 +1,38 @@ +warning: missing options for `on_move` attribute + --> $DIR/report_warning_on_missing_options.rs:3:1 + | +LL | #[diagnostic::on_move] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_missing_options.rs:12:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_missing_options.rs:7:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_missing_options.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs new file mode 100644 index 0000000000000..33e78ea5fc1fe --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs @@ -0,0 +1,23 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +struct Foo; + +#[diagnostic::on_move( +//~^WARN `#[diagnostic::on_move]` can only be applied to enums, structs or unions + message = "Foo", + label = "Bar", +)] +trait MyTrait {} + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr new file mode 100644 index 0000000000000..29c987fba48f1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr @@ -0,0 +1,41 @@ +warning: `#[diagnostic::on_move]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:9:1 + | +LL | / #[diagnostic::on_move( +LL | | +LL | | message = "Foo", +LL | | label = "Bar", +LL | | )] + | |__^ + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo + --> $DIR/report_warning_on_non_adt.rs:21:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_non_adt.rs:16:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_non_adt.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs new file mode 100644 index 0000000000000..651f6184cfac6 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs @@ -0,0 +1,19 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", + baz="Baz" + //~^WARN unknown or malformed `on_move` attribute + //~|HELP only `message`, `note` and `label` are allowed as options. Their values must be string literals +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr new file mode 100644 index 0000000000000..a09b8a96d2d12 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr @@ -0,0 +1,38 @@ +warning: unknown or malformed `on_move` attribute + --> $DIR/report_warning_on_unknown_options.rs:6:5 + | +LL | baz="Baz" + | ^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options. Their values must be string literals + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo + --> $DIR/report_warning_on_unknown_options.rs:17:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_unknown_options.rs:12:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_unknown_options.rs:10:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs new file mode 100644 index 0000000000000..a1f3b1fbbc860 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs @@ -0,0 +1,18 @@ +//! This is an unusual feature gate test, as it doesn't test the feature +//! gate, but the fact that not adding the feature gate will cause the +//! diagnostic to not emit the custom diagnostic message +//! +#[diagnostic::on_move( + message = "Foo" +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr new file mode 100644 index 0000000000000..9ba6f272cf92b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -0,0 +1,29 @@ +error[E0382]: use of moved value: `foo` + --> $DIR/feature-gate-diagnostic-on-move.rs:16:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/feature-gate-diagnostic-on-move.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/feature-gate-diagnostic-on-move.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. From eee0dc9bb137a4725cc6d397d0b88233e8ad40ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 3 Mar 2026 11:34:00 +0100 Subject: [PATCH 03/18] Rename `cycle_check` to `find_cycle` --- compiler/rustc_query_impl/src/job.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 7151d0b52fe23..6a14100a8e3d0 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -151,11 +151,11 @@ fn abstracted_waiters_of(job_map: &QueryJobMap<'_>, query: QueryJobId) -> Vec( +/// the cycle. `stack` will contain just the cycle on return if detected. +fn find_cycle<'tcx>( job_map: &QueryJobMap<'tcx>, query: QueryJobId, span: Span, @@ -186,7 +186,7 @@ fn cycle_check<'tcx>( continue; }; if let ControlFlow::Break(maybe_resumable) = - cycle_check(job_map, parent, abstracted_waiter.span, stack, visited) + find_cycle(job_map, parent, abstracted_waiter.span, stack, visited) { // Return the resumable waiter in `waiter.resumable` if present return ControlFlow::Break(abstracted_waiter.resumable.or(maybe_resumable)); @@ -228,7 +228,7 @@ fn connected_to_root<'tcx>( false } -/// Looks for query cycles starting from the last query in `jobs`. +/// Looks for a query cycle using the last query in `jobs`. /// If a cycle is found, all queries in the cycle is removed from `jobs` and /// the function return true. /// If a cycle was not found, the starting query is removed from `jobs` and @@ -242,7 +242,7 @@ fn remove_cycle<'tcx>( let mut stack = Vec::new(); // Look for a cycle starting with the last query in `jobs` if let ControlFlow::Break(resumable) = - cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + find_cycle(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) { // The stack is a vector of pairs of spans and queries; reverse it so that // the earlier entries require later entries From aa7d812f348da3e330cf3b99ee4a5ec373f2a38b Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 17 Mar 2026 09:27:25 +0000 Subject: [PATCH 04/18] remove `internal_impls_macro` feature --- library/core/src/lib.rs | 1 - library/core/src/marker.rs | 3 --- 2 files changed, 4 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e12c43068245a..60da6662de849 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -107,7 +107,6 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] -#![feature(internal_impls_macro)] #![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(panic_internals)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index becac514284e3..c79e8fc4060c1 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -52,9 +52,6 @@ use crate::pin::UnsafePinned; /// u32, /// } /// ``` -#[unstable(feature = "internal_impls_macro", issue = "none")] -// Allow implementations of `UnsizedConstParamTy` even though std cannot use that feature. -#[allow_internal_unstable(const_param_ty_trait)] macro marker_impls { ( $(#[$($meta:tt)*])* $Trait:ident for $({$($bounds:tt)*})? $T:ty $(, $($rest:tt)*)? ) => { $(#[$($meta)*])* impl< $($($bounds)*)? > $Trait for $T {} From e350a56cf9d7a013e1d1e52ab306bfc19324653a Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 24 Feb 2026 13:48:09 +0000 Subject: [PATCH 05/18] move features into the correct section --- library/core/src/lib.rs | 2 +- library/std/src/lib.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 60da6662de849..8a9decd33b1c1 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -107,7 +107,6 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] -#![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] @@ -143,6 +142,7 @@ #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] +#![feature(link_cfg)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(macro_metavar_expr_concat)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6fcb28edc7d84..c8c8a6c897142 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -275,9 +275,7 @@ #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] -#![feature(const_default)] #![feature(const_trait_impl)] -#![feature(core_float_math)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] @@ -287,16 +285,11 @@ #![feature(f16)] #![feature(f128)] #![feature(ffi_const)] -#![feature(formatting_options)] -#![feature(funnel_shifts)] #![feature(intra_doc_pointers)] -#![feature(iter_advance_by)] -#![feature(iter_next_chunk)] #![feature(lang_items)] #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] -#![feature(maybe_uninit_fill)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -314,7 +307,6 @@ #![feature(try_blocks)] #![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] -#![feature(uint_carryless_mul)] // tidy-alphabetical-end // // Library features (core): @@ -325,6 +317,8 @@ #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_convert)] +#![feature(const_default)] +#![feature(core_float_math)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(cstr_display)] @@ -340,13 +334,18 @@ #![feature(float_minimum_maximum)] #![feature(fmt_internals)] #![feature(fn_ptr_trait)] +#![feature(formatting_options)] +#![feature(funnel_shifts)] #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_must_use)] #![feature(int_from_ascii)] #![feature(ip)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] #![feature(maybe_uninit_array_assume_init)] +#![feature(maybe_uninit_fill)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -364,6 +363,7 @@ #![feature(sync_unsafe_cell)] #![feature(temporary_niche_types)] #![feature(ub_checks)] +#![feature(uint_carryless_mul)] #![feature(used_with_arg)] // tidy-alphabetical-end // From c8a2c46eda3d8a8cc0dbe92002543ab5867586a3 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 16 Mar 2026 14:33:39 +0100 Subject: [PATCH 06/18] bootstrap: Optionally print a backtrace if a command fails --- src/bootstrap/src/utils/exec.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index b264c961b6593..f9b0d2dbb2095 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -7,6 +7,7 @@ //! relevant to command execution in the bootstrap process. This includes settings such as //! dry-run mode, verbosity level, and failure behavior. +use std::backtrace::{Backtrace, BacktraceStatus}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Formatter}; @@ -930,6 +931,16 @@ Executed at: {executed_at}"#, if stderr.captures() { writeln!(error_message, "\n--- STDERR vvv\n{}", output.stderr().trim()).unwrap(); } + let backtrace = if exec_ctx.verbosity > 1 { + Backtrace::force_capture() + } else if matches!(command.failure_behavior, BehaviorOnFailure::Ignore) { + Backtrace::disabled() + } else { + Backtrace::capture() + }; + if matches!(backtrace.status(), BacktraceStatus::Captured) { + writeln!(error_message, "\n--- BACKTRACE vvv\n{backtrace}").unwrap(); + } match command.failure_behavior { BehaviorOnFailure::DelayFail => { From 74c51ce75e240c27c7a8650fdf9e0038464c7e19 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 18 Mar 2026 17:28:17 +0100 Subject: [PATCH 07/18] Don't emit `missing_doc_code_examples` on impl items --- src/librustdoc/passes/check_doc_test_visibility.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 40dfdb890d66f..a1578ab40209a 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -79,8 +79,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) | clean::RequiredAssocTypeItem(..) - // check for trait impl - | clean::ImplItem(box clean::Impl { trait_: Some(_), .. }) + | clean::ImplItem(_) ) { return false; From 6d3b7809cf0dbe8395b26118247439e47a9f9e12 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 18 Mar 2026 17:28:55 +0100 Subject: [PATCH 08/18] Add more tests for rustdoc `missing_doc_code_examples` lint --- .../lints/lint-missing-doc-code-example.rs | 6 +++++- .../lint-missing-doc-code-example.stderr | 20 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs index 8e5c31d50edc7..731d19bffec52 100644 --- a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs +++ b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs @@ -78,7 +78,11 @@ impl Clone for Struct { } } - +impl Struct { // No doc or code example and it's fine! + pub fn bar() {} + //~^ ERROR missing code example in this documentation + //~| ERROR missing documentation for an associated function +} /// doc /// diff --git a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr index 22533b9816a74..6382c5926e8fc 100644 --- a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr +++ b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr @@ -1,3 +1,15 @@ +error: missing documentation for an associated function + --> $DIR/lint-missing-doc-code-example.rs:82:5 + | +LL | pub fn bar() {} + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-missing-doc-code-example.rs:2:9 + | +LL | #![deny(missing_docs)] + | ^^^^^^^^^^^^ + error: missing code example in this documentation --> $DIR/lint-missing-doc-code-example.rs:38:3 | @@ -28,5 +40,11 @@ error: missing code example in this documentation LL | /// Doc | ^^^^^^^ -error: aborting due to 4 previous errors +error: missing code example in this documentation + --> $DIR/lint-missing-doc-code-example.rs:82:5 + | +LL | pub fn bar() {} + | ^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors From 45b22efe06630703e20de19c589299e6a592469e Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 18 Mar 2026 21:29:45 +0100 Subject: [PATCH 09/18] tests: Activate must_not_suspend test for MutexGuard dropped before await The test pass in `nightly-2023-09-24` but fail in `nightly-2023-09-23`: $ rustc +nightly-2023-09-23 --edition 2018 tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs error: `MutexGuard` held across a suspend point, but should not be --> tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs:13:9 | 13 | let lock = foo.lock().unwrap(); | ^^^^ ... 18 | bar().await; | ----- the value is held across this suspend point | --- src/tools/tidy/src/issues.txt | 1 - .../{issue-89562.rs => mutex-guard-dropped-before-await.rs} | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) rename tests/ui/lint/must_not_suspend/{issue-89562.rs => mutex-guard-dropped-before-await.rs} (67%) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 668a8abf1d8bb..a9dc449afa835 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1476,7 +1476,6 @@ ui/lint/issue-97094.rs ui/lint/issue-99387.rs ui/lint/let_underscore/issue-119696-err-on-fn.rs ui/lint/let_underscore/issue-119697-extra-let.rs -ui/lint/must_not_suspend/issue-89562.rs ui/lint/unused/issue-103320-must-use-ops.rs ui/lint/unused/issue-104397.rs ui/lint/unused/issue-105061-array-lint.rs diff --git a/tests/ui/lint/must_not_suspend/issue-89562.rs b/tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs similarity index 67% rename from tests/ui/lint/must_not_suspend/issue-89562.rs rename to tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs index 99a548130720f..d1c48c3c51457 100644 --- a/tests/ui/lint/must_not_suspend/issue-89562.rs +++ b/tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs @@ -1,9 +1,13 @@ +//! Regression test for + //@ edition:2018 //@ run-pass +#![feature(must_not_suspend)] +#![deny(must_not_suspend)] + use std::sync::Mutex; -// Copied from the issue. Allow-by-default for now, so run-pass pub async fn foo() { let foo = Mutex::new(1); let lock = foo.lock().unwrap(); From bef4b476c17685077a7f2cf680743ab8bc2cd11b Mon Sep 17 00:00:00 2001 From: Ada Bohm Date: Thu, 19 Mar 2026 09:22:03 +0100 Subject: [PATCH 10/18] Updates derive_where and removes workaround --- Cargo.lock | 4 ++-- compiler/rustc_type_ir/Cargo.toml | 2 +- compiler/rustc_type_ir/src/binder.rs | 20 +++----------------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98567f858e9f1..539f66ffb3bb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,9 +1140,9 @@ version = "0.1.96" [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 10c53c881a362..b00f9eebb8266 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" -derive-where = "1.2.7" +derive-where = "1.6.1" ena = "0.14.4" indexmap = "2.0.0" rustc-hash = "2.0.0" diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index fc8b39f7c01f0..0b0f0fd2f4249 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -26,11 +26,7 @@ use crate::{self as ty, DebruijnIndex, Interner, UniverseIndex}; /// for more details. /// /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -// FIXME(derive-where#136): Need to use separate `derive_where` for -// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` -// impls from incorrectly relying on `T: Copy` and `T: Ord`. -#[derive_where(Copy; I: Interner, T: Copy)] -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)] +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner, T)] #[derive(GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Binder { @@ -365,12 +361,7 @@ impl TypeVisitor for ValidateBoundVars { /// `instantiate`. /// /// See for more details. -// FIXME(derive-where#136): Need to use separate `derive_where` for -// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` -// impls from incorrectly relying on `T: Copy` and `T: Ord`. -#[derive_where(Ord; I: Interner, T: Ord)] -#[derive_where(Copy; I: Interner, T: Copy)] -#[derive_where(Clone, PartialOrd, PartialEq, Hash, Debug; I: Interner, T)] +#[derive_where(Clone, Copy, PartialOrd, Ord, PartialEq, Hash, Debug; I: Interner, T)] #[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", @@ -964,12 +955,7 @@ pub enum BoundVarIndexKind { /// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are /// identified by both a universe, as well as a name residing within that universe. Distinct bound /// regions/types/consts within the same universe simply have an unknown relationship to one -// FIXME(derive-where#136): Need to use separate `derive_where` for -// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` -// impls from incorrectly relying on `T: Copy` and `T: Ord`. -#[derive_where(Ord; I: Interner, T: Ord)] -#[derive_where(Copy; I: Interner, T: Copy)] -#[derive_where(Clone, PartialOrd, PartialEq, Eq, Hash; I: Interner, T)] +#[derive_where(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash; I: Interner, T)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", From 74776c4008a668e70733e16d8cf8519f3551e388 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 19 Mar 2026 11:06:21 +1100 Subject: [PATCH 11/18] Rewrite `query_ensure_result`. It currently uses chaining which is concise but hard to read. There are up to four implicit matches occurring after the call to `execute_query_fn`. - The first `map` on `Option>>`. - The second `map` on `Option>`. - The third `map` on `Result`. - The `unwrap_or` on `Option>`. This commit rewrites it to use at most two matches. - An explicit match on `Option>>`. - An explicit match on `Result`. This is easier to read. It's also more efficient, though the code isn't hot enough for that to matter. --- compiler/rustc_middle/src/query/inner.rs | 43 +++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index c2525d26d4c67..402c448a1fa3a 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -77,23 +77,34 @@ where C: QueryCache>>, Result: Erasable, { + let convert = |value: Erased>| -> Result<(), ErrorGuaranteed> { + match erase::restore_val(value) { + Ok(_) => Ok(()), + Err(guar) => Err(guar), + } + }; + match try_get_cached(tcx, &query.cache, key) { - Some(value) => erase::restore_val(value).map(drop), - None => (query.execute_query_fn)( - tcx, - DUMMY_SP, - key, - QueryMode::Ensure { ensure_mode: EnsureMode::Ok }, - ) - .map(erase::restore_val) - .map(|value| value.map(drop)) - // Either we actually executed the query, which means we got a full `Result`, - // or we can just assume the query succeeded, because it was green in the - // incremental cache. If it is green, that means that the previous compilation - // that wrote to the incremental cache compiles successfully. That is only - // possible if the cache entry was `Ok(())`, so we emit that here, without - // actually encoding the `Result` in the cache or loading it from there. - .unwrap_or(Ok(())), + Some(value) => convert(value), + None => { + match (query.execute_query_fn)( + tcx, + DUMMY_SP, + key, + QueryMode::Ensure { ensure_mode: EnsureMode::Ok }, + ) { + // We executed the query. Convert the successful result. + Some(res) => convert(res), + + // Reaching here means we didn't execute the query, but we can just assume the + // query succeeded, because it was green in the incremental cache. If it is green, + // that means that the previous compilation that wrote to the incremental cache + // compiles successfully. That is only possible if the cache entry was `Ok(())`, so + // we emit that here, without actually encoding the `Result` in the cache or + // loading it from there. + None => Ok(()), + } + } } } From cec0a68d6a69a9c29efc4055f7215dff1c9de103 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:32:36 +0900 Subject: [PATCH 12/18] Resolve paths for `impl` restrictions using `smart_resolve_path` --- compiler/rustc_resolve/src/errors.rs | 4 +++ compiler/rustc_resolve/src/late.rs | 43 +++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 63ffdbc37f7d1..a73362eb805b2 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -561,6 +561,10 @@ pub(crate) struct ExpectedModuleFound { #[diag("cannot determine resolution for the visibility", code = E0578)] pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); +#[derive(Diagnostic)] +#[diag("trait implementation can only be restricted to ancestor modules")] +pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); + #[derive(Diagnostic)] #[diag("cannot use a tool module through an import")] pub(crate) struct ToolModuleImported { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c7b0cb192433e..94de55c247115 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -453,6 +453,8 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> { DefineOpaques, /// Resolving a macro Macro, + /// Paths for module or crate root. Used for restrictions. + Module, } impl PathSource<'_, '_, '_> { @@ -461,7 +463,8 @@ impl PathSource<'_, '_, '_> { PathSource::Type | PathSource::Trait(_) | PathSource::Struct(_) - | PathSource::DefineOpaques => TypeNS, + | PathSource::DefineOpaques + | PathSource::Module => TypeNS, PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) @@ -486,7 +489,8 @@ impl PathSource<'_, '_, '_> { | PathSource::DefineOpaques | PathSource::Delegation | PathSource::PreciseCapturingArg(..) - | PathSource::Macro => false, + | PathSource::Macro + | PathSource::Module => false, } } @@ -529,6 +533,7 @@ impl PathSource<'_, '_, '_> { PathSource::ReturnTypeNotation | PathSource::Delegation => "function", PathSource::PreciseCapturingArg(..) => "type or const parameter", PathSource::Macro => "macro", + PathSource::Module => "module", } } @@ -627,6 +632,7 @@ impl PathSource<'_, '_, '_> { ), PathSource::PreciseCapturingArg(MacroNS) => false, PathSource::Macro => matches!(res, Res::Def(DefKind::Macro(_), _)), + PathSource::Module => matches!(res, Res::Def(DefKind::Mod, _)), } } @@ -647,6 +653,12 @@ impl PathSource<'_, '_, '_> { (PathSource::PreciseCapturingArg(..), true) => E0799, (PathSource::PreciseCapturingArg(..), false) => E0800, (PathSource::Macro, _) => E0425, + // FIXME: There is no dedicated error code for this case yet. + // E0577 already covers the same situation for visibilities, + // so we reuse it here for now. It may make sense to generalize + // it for restrictions in the future. + (PathSource::Module, true) => E0577, + (PathSource::Module, false) => E0433, } } } @@ -2175,7 +2187,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { | PathSource::Type | PathSource::PreciseCapturingArg(..) | PathSource::ReturnTypeNotation - | PathSource::Macro => false, + | PathSource::Macro + | PathSource::Module => false, PathSource::Expr(..) | PathSource::Pat | PathSource::Struct(_) @@ -2801,7 +2814,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.diag_metadata.current_impl_items = None; } - ItemKind::Trait(box Trait { generics, bounds, items, .. }) => { + ItemKind::Trait(box Trait { generics, bounds, items, impl_restriction, .. }) => { + // resolve paths for `impl` restrictions + self.resolve_impl_restriction_path(impl_restriction); + // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib( &generics.params, @@ -4390,6 +4406,25 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } + fn resolve_impl_restriction_path(&mut self, restriction: &'ast ast::ImplRestriction) { + match &restriction.kind { + ast::RestrictionKind::Unrestricted => (), + ast::RestrictionKind::Restricted { path, id, shorthand: _ } => { + self.smart_resolve_path(*id, &None, path, PathSource::Module); + if let Some(res) = self.r.partial_res_map[&id].full_res() + && let Some(def_id) = res.opt_def_id() + { + if !self.r.is_accessible_from( + Visibility::Restricted(def_id), + self.parent_scope.module, + ) { + self.r.dcx().create_err(errors::RestrictionAncestorOnly(path.span)).emit(); + } + } + } + } + } + // High-level and context dependent path resolution routine. // Resolves the path and records the resolution into definition map. // If resolution fails tries several techniques to find likely From 4a60dae793e07440c32ec2d5ef71bf58cf1df7a9 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:43:32 +0900 Subject: [PATCH 13/18] Add UI tests for path resolution errors of `impl` restrictions --- .../restriction_resolution_errors.rs | 85 +++++++++++ .../restriction_resolution_errors.stderr | 140 ++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 tests/ui/impl-restriction/restriction_resolution_errors.rs create mode 100644 tests/ui/impl-restriction/restriction_resolution_errors.stderr diff --git a/tests/ui/impl-restriction/restriction_resolution_errors.rs b/tests/ui/impl-restriction/restriction_resolution_errors.rs new file mode 100644 index 0000000000000..b36f2cf9bdfba --- /dev/null +++ b/tests/ui/impl-restriction/restriction_resolution_errors.rs @@ -0,0 +1,85 @@ +#![feature(impl_restriction)] +#![expect(incomplete_features)] + +pub mod a { + pub enum E {} + pub mod d {} + pub mod b { + pub mod c {} + + // We do not use crate-relative paths here, since we follow the + // "uniform paths" approach used for type/expression paths. + pub impl(in a::b) trait T1 {} //~ ERROR cannot find module or crate `a` in this scope [E0433] + + pub impl(in ::std) trait T2 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in self::c) trait T3 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in super::d) trait T4 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in crate::c) trait T5 {} //~ ERROR cannot find module `c` in the crate root [E0433] + + pub impl(in super::E) trait T6 {} //~ ERROR expected module, found enum `super::E` [E0577] + + pub impl(in super::super::super) trait T7 {} //~ ERROR too many leading `super` keywords [E0433] + + // OK paths + pub impl(crate) trait T8 {} + pub impl(self) trait T9 {} + pub impl(super) trait T10 {} + pub impl(in crate::a) trait T11 {} + pub impl(in super::super) trait T12 {} + + // Check if we can resolve paths referring to modules declared later. + pub impl(in self::f) trait L1 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in super::G) trait L2 {} //~ ERROR expected module, found enum `super::G` [E0577] + + pub impl(in super::h) trait L3 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub mod f {} + } + + pub enum G {} + pub mod h {} +} + +pub impl(in crate::a) trait T13 {} //~ ERROR trait implementation can only be restricted to ancestor modules + +pub impl(in crate::a::E) trait T14 {} //~ ERROR expected module, found enum `crate::a::E` [E0577] + +pub impl(crate) trait T15 {} +pub impl(self) trait T16 {} + +pub impl(super) trait T17 {} //~ ERROR too many leading `super` keywords [E0433] + +// Check if we can resolve paths referring to modules declared later. +pub impl(in crate::j) trait L4 {} //~ ERROR trait implementation can only be restricted to ancestor modules + +pub impl(in crate::I) trait L5 {} //~ ERROR expected module, found enum `crate::I` [E0577] + +pub enum I {} +pub mod j {} + +// Check if we can resolve `use`d paths. +mod m1 { + pub impl(in crate::m2) trait U1 {} // OK +} + +use m1 as m2; + +mod m3 { + mod m4 { + pub impl(in crate::m2) trait U2 {} //~ ERROR trait implementation can only be restricted to ancestor modules + pub impl(in m6) trait U3 {} // OK + pub impl(in m6::m5) trait U4 {} //~ ERROR trait implementation can only be restricted to ancestor modules + pub impl(in m7) trait U5 {} //~ ERROR expected module, found enum `m7` [E0577] + + use crate::m3 as m6; + use crate::m3::E as m7; + } + mod m5 {} + pub enum E {} +} + +fn main() {} diff --git a/tests/ui/impl-restriction/restriction_resolution_errors.stderr b/tests/ui/impl-restriction/restriction_resolution_errors.stderr new file mode 100644 index 0000000000000..540803285c1b5 --- /dev/null +++ b/tests/ui/impl-restriction/restriction_resolution_errors.stderr @@ -0,0 +1,140 @@ +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:14:21 + | +LL | pub impl(in ::std) trait T2 {} + | ^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:16:21 + | +LL | pub impl(in self::c) trait T3 {} + | ^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:18:21 + | +LL | pub impl(in super::d) trait T4 {} + | ^^^^^^^^ + +error[E0433]: too many leading `super` keywords + --> $DIR/restriction_resolution_errors.rs:24:35 + | +LL | pub impl(in super::super::super) trait T7 {} + | ^^^^^ there are too many leading `super` keywords + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:34:21 + | +LL | pub impl(in self::f) trait L1 {} + | ^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:38:21 + | +LL | pub impl(in super::h) trait L3 {} + | ^^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:47:13 + | +LL | pub impl(in crate::a) trait T13 {} + | ^^^^^^^^ + +error[E0433]: too many leading `super` keywords + --> $DIR/restriction_resolution_errors.rs:54:10 + | +LL | pub impl(super) trait T17 {} + | ^^^^^ there are too many leading `super` keywords + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:57:13 + | +LL | pub impl(in crate::j) trait L4 {} + | ^^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:73:21 + | +LL | pub impl(in crate::m2) trait U2 {} + | ^^^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:75:21 + | +LL | pub impl(in m6::m5) trait U4 {} + | ^^^^^^ + +error[E0433]: cannot find module or crate `a` in this scope + --> $DIR/restriction_resolution_errors.rs:12:21 + | +LL | pub impl(in a::b) trait T1 {} + | ^ use of unresolved module or unlinked crate `a` + | +help: there is a crate or module with a similar name + | +LL - pub impl(in a::b) trait T1 {} +LL + pub impl(in c::b) trait T1 {} + | +help: consider importing this module + | +LL + use a; + | + +error[E0433]: cannot find module `c` in the crate root + --> $DIR/restriction_resolution_errors.rs:20:28 + | +LL | pub impl(in crate::c) trait T5 {} + | ^ not found in the crate root + +error[E0577]: expected module, found enum `super::E` + --> $DIR/restriction_resolution_errors.rs:22:21 + | +LL | pub impl(in super::E) trait T6 {} + | ^^^^^^^^ not a module + +error[E0577]: expected module, found enum `super::G` + --> $DIR/restriction_resolution_errors.rs:36:21 + | +LL | pub impl(in super::G) trait L2 {} + | ^^^^^^^^ not a module + +error[E0577]: expected module, found enum `crate::a::E` + --> $DIR/restriction_resolution_errors.rs:49:13 + | +LL | pub mod b { + | --------- similarly named module `b` defined here +... +LL | pub impl(in crate::a::E) trait T14 {} + | ^^^^^^^^^^^ + | +help: a module with a similar name exists + | +LL - pub impl(in crate::a::E) trait T14 {} +LL + pub impl(in crate::a::b) trait T14 {} + | + +error[E0577]: expected module, found enum `crate::I` + --> $DIR/restriction_resolution_errors.rs:59:13 + | +LL | pub mod a { + | --------- similarly named module `a` defined here +... +LL | pub impl(in crate::I) trait L5 {} + | ^^^^^^^^ + | +help: a module with a similar name exists + | +LL - pub impl(in crate::I) trait L5 {} +LL + pub impl(in crate::a) trait L5 {} + | + +error[E0577]: expected module, found enum `m7` + --> $DIR/restriction_resolution_errors.rs:76:21 + | +LL | pub impl(in m7) trait U5 {} + | ^^ not a module + +error: aborting due to 18 previous errors + +Some errors have detailed explanations: E0433, E0577. +For more information about an error, try `rustc --explain E0433`. From 082cdf7c48245088b3384e8a573e60b8dda14d9d Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Thu, 19 Mar 2026 10:27:40 +0000 Subject: [PATCH 14/18] Preserve braces around `self` in use tree pretty printing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST pretty printer strips braces from single-item `use` sub-groups, simplifying `use foo::{Bar}` to `use foo::Bar`. However, when the single item is `self`, this produces `use foo::self` which is not valid Rust (E0429) — the grammar requires `use foo::{self}`. This affects both `stringify!` and `rustc -Zunpretty=expanded`, causing `cargo expand` output to be unparseable when a crate uses `use path::{self}` imports (a common pattern in the ecosystem). The fix checks whether the single nested item's path starts with `self` before stripping braces. If so, the braces are preserved. Signed-off-by: Andrew V. Teylu --- compiler/rustc_ast_pretty/src/pprust/state/item.rs | 8 +++++++- tests/pretty/use-self-braces.rs | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/pretty/use-self-braces.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 3407feb3dcc3a..e997fdf498202 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -881,7 +881,13 @@ impl<'a> State<'a> { } if items.is_empty() { self.word("{}"); - } else if let [(item, _)] = items.as_slice() { + } else if let [(item, _)] = items.as_slice() + && !item + .prefix + .segments + .first() + .is_some_and(|seg| seg.ident.name == rustc_span::symbol::kw::SelfLower) + { self.print_use_tree(item); } else { let cb = self.cbox(INDENT_UNIT); diff --git a/tests/pretty/use-self-braces.rs b/tests/pretty/use-self-braces.rs new file mode 100644 index 0000000000000..8db2568943616 --- /dev/null +++ b/tests/pretty/use-self-braces.rs @@ -0,0 +1,10 @@ +//@ pp-exact +//@ edition:2021 + +#![allow(unused_imports)] + +// Braces around `self` must be preserved, because `use foo::self` is not valid Rust. +use std::io::{self}; +use std::fmt::{self, Debug}; + +fn main() {} From c77768590dbc2e63acf853ab86177de3fb4e5bfb Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Thu, 19 Mar 2026 10:27:40 +0000 Subject: [PATCH 15/18] Fix whitespace after fragment specifiers in macro pretty printing When a macro-generating-macro captures fragment specifier tokens (like `$x:ident`) as `tt` metavariables and replays them before a keyword (like `where`), the pretty printer concatenates them into an invalid fragment specifier (e.g. `$x:identwhere` instead of `$x:ident where`). This happens because `tt` captures preserve the original token spacing. When the fragment specifier name (e.g. `ident`) was originally the last token before a closing delimiter, it retains `JointHidden` spacing. The `print_tts` function only checks `space_between` for `Spacing::Alone` tokens, so `JointHidden` tokens skip the space check entirely, causing adjacent identifier-like tokens to merge. The fix adds a check in `print_tts` to insert a space between adjacent identifier-like tokens (identifiers and keywords) regardless of the original spacing, preventing them from being concatenated into invalid tokens. This is similar to the existing `space_between` mechanism that prevents token merging for `Spacing::Alone` tokens, extended to also handle `Joint`/`JointHidden` cases where two identifier-like tokens would merge. Signed-off-by: Andrew V. Teylu --- compiler/rustc_ast_pretty/src/pprust/state.rs | 20 ++++++++++++++++ .../macro-fragment-specifier-whitespace.pp | 24 +++++++++++++++++++ .../macro-fragment-specifier-whitespace.rs | 19 +++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 tests/pretty/macro-fragment-specifier-whitespace.pp create mode 100644 tests/pretty/macro-fragment-specifier-whitespace.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 4ba5dc541342a..2ff730e3331c8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -329,6 +329,19 @@ fn print_crate_inner<'a>( /// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,` /// - #73345: `#[allow(unused)]` must be printed rather than `# [allow(unused)]` /// +/// Returns `true` if both token trees are identifier-like tokens that would +/// merge into a single token if printed without a space between them. +/// E.g. `ident` + `where` would merge into `identwhere`. +fn idents_would_merge(tt1: &TokenTree, tt2: &TokenTree) -> bool { + fn is_ident_like(tt: &TokenTree) -> bool { + matches!( + tt, + TokenTree::Token(Token { kind: token::Ident(..) | token::NtIdent(..), .. }, _,) + ) + } + is_ident_like(tt1) && is_ident_like(tt2) +} + fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool { use Delimiter::*; use TokenTree::{Delimited as Del, Token as Tok}; @@ -794,6 +807,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere if let Some(next) = iter.peek() { if spacing == Spacing::Alone && space_between(tt, next) { self.space(); + } else if spacing != Spacing::Alone && idents_would_merge(tt, next) { + // When tokens from macro `tt` captures preserve their + // original `Joint`/`JointHidden` spacing, adjacent + // identifier-like tokens can be concatenated without a + // space (e.g. `$x:identwhere`). Insert a space to + // prevent this. + self.space(); } } } diff --git a/tests/pretty/macro-fragment-specifier-whitespace.pp b/tests/pretty/macro-fragment-specifier-whitespace.pp new file mode 100644 index 0000000000000..ee5a0f7a7c056 --- /dev/null +++ b/tests/pretty/macro-fragment-specifier-whitespace.pp @@ -0,0 +1,24 @@ +#![feature(prelude_import)] +#![no_std] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; +//@ pretty-mode:expanded +//@ pp-exact:macro-fragment-specifier-whitespace.pp + +// Test that fragment specifier names in macro definitions are properly +// separated from the following keyword/identifier token when pretty-printed. +// This is a regression test for a bug where `$x:ident` followed by `where` +// was pretty-printed as `$x:identwhere` (an invalid fragment specifier). + +macro_rules! outer { + ($d:tt $($params:tt)*) => + { + #[macro_export] macro_rules! inner + { ($($params)* where $d($rest:tt)*) => {}; } + }; +} +#[macro_export] +macro_rules! inner { ($x:ident where $ ($rest : tt)*) => {}; } + +fn main() {} diff --git a/tests/pretty/macro-fragment-specifier-whitespace.rs b/tests/pretty/macro-fragment-specifier-whitespace.rs new file mode 100644 index 0000000000000..54c6debd9a275 --- /dev/null +++ b/tests/pretty/macro-fragment-specifier-whitespace.rs @@ -0,0 +1,19 @@ +//@ pretty-mode:expanded +//@ pp-exact:macro-fragment-specifier-whitespace.pp + +// Test that fragment specifier names in macro definitions are properly +// separated from the following keyword/identifier token when pretty-printed. +// This is a regression test for a bug where `$x:ident` followed by `where` +// was pretty-printed as `$x:identwhere` (an invalid fragment specifier). + +macro_rules! outer { + ($d:tt $($params:tt)*) => { + #[macro_export] + macro_rules! inner { + ($($params)* where $d($rest:tt)*) => {}; + } + }; +} +outer!($ $x:ident); + +fn main() {} From e9740a4be5cf4e89accacae0bd868557d942d247 Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Thu, 19 Mar 2026 10:27:40 +0000 Subject: [PATCH 16/18] Insert space after float literal ending with `.` in pretty printer The pretty printer was collapsing unsuffixed float literals (like `0.`) with adjacent dot tokens, producing ambiguous or invalid output: - `0. ..45.` (range) became `0...45.` - `0. ..=360.` (inclusive range) became `0...=360.` - `0. .to_string()` (method call) became `0..to_string()` The fix inserts a space after an unsuffixed float literal ending with `.` when it appears as the start expression of a range operator or the receiver of a method call or field access, preventing the trailing dot from merging with the following `.`, `..`, or `..=` token. This is skipped when the expression is already parenthesized, since the closing `)` naturally provides separation. Signed-off-by: Andrew V. Teylu --- .../rustc_ast_pretty/src/pprust/state/expr.rs | 48 ++++++++++++++----- tests/pretty/float-trailing-dot.rs | 8 ++++ 2 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 tests/pretty/float-trailing-dot.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9b4ff2b63bd45..0c5928765bfa5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -260,12 +260,15 @@ impl<'a> State<'a> { // // loop { break x; }.method(); // - self.print_expr_cond_paren( - receiver, - receiver.precedence() < ExprPrecedence::Unambiguous, - fixup.leftmost_subexpression_with_dot(), - ); + let needs_paren = receiver.precedence() < ExprPrecedence::Unambiguous; + self.print_expr_cond_paren(receiver, needs_paren, fixup.leftmost_subexpression_with_dot()); + // If the receiver is an unsuffixed float literal like `0.`, insert + // a space so the `.` of the method call doesn't merge with the + // trailing dot: `0. .method()` instead of `0..method()`. + if !needs_paren && expr_ends_with_dot(receiver) { + self.word(" "); + } self.word("."); self.print_ident(segment.ident); if let Some(args) = &segment.args { @@ -658,11 +661,15 @@ impl<'a> State<'a> { ); } ast::ExprKind::Field(expr, ident) => { + let needs_paren = expr.precedence() < ExprPrecedence::Unambiguous; self.print_expr_cond_paren( expr, - expr.precedence() < ExprPrecedence::Unambiguous, + needs_paren, fixup.leftmost_subexpression_with_dot(), ); + if !needs_paren && expr_ends_with_dot(expr) { + self.word(" "); + } self.word("."); self.print_ident(*ident); } @@ -685,11 +692,15 @@ impl<'a> State<'a> { let fake_prec = ExprPrecedence::LOr; if let Some(e) = start { let start_fixup = fixup.leftmost_subexpression_with_operator(true); - self.print_expr_cond_paren( - e, - start_fixup.precedence(e) < fake_prec, - start_fixup, - ); + let needs_paren = start_fixup.precedence(e) < fake_prec; + self.print_expr_cond_paren(e, needs_paren, start_fixup); + // If the start expression is a float literal ending with + // `.`, we need a space before `..` or `..=` so that the + // dots don't merge. E.g. `0. ..45.` must not become + // `0...45.`. + if !needs_paren && expr_ends_with_dot(e) { + self.word(" "); + } } match limits { ast::RangeLimits::HalfOpen => self.word(".."), @@ -1025,3 +1036,18 @@ fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String template.push('"'); template } + +/// Returns `true` if the printed representation of this expression ends with +/// a `.` character — specifically, an unsuffixed float literal like `0.` or +/// `45.`. This is used to insert whitespace before range operators (`..`, +/// `..=`) so that the dots don't merge (e.g. `0. ..45.` instead of `0...45.`). +fn expr_ends_with_dot(expr: &ast::Expr) -> bool { + match &expr.kind { + ast::ExprKind::Lit(token_lit) => { + token_lit.kind == token::Float + && token_lit.suffix.is_none() + && token_lit.symbol.as_str().ends_with('.') + } + _ => false, + } +} diff --git a/tests/pretty/float-trailing-dot.rs b/tests/pretty/float-trailing-dot.rs new file mode 100644 index 0000000000000..63934bb8eaaa7 --- /dev/null +++ b/tests/pretty/float-trailing-dot.rs @@ -0,0 +1,8 @@ +//@ pp-exact + +fn main() { + let _ = 0. ..45.; + let _ = 0. ..=360.; + let _ = 0. ..; + let _ = 0. .to_string(); +} From caad6ee2404cbe49915052f342b6227437e4d2a9 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 19 Mar 2026 19:10:21 +0100 Subject: [PATCH 17/18] tests: Add regression test for async closures involving HRTBs I suspect the original code had several issues. The last one that made the code compile entered `nightly-2024-02-11`. The code fails to build with `nightly-2024-02-10`: $ rustc +nightly-2024-02-10 --edition 2018 tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs error[E0277]: expected a `FnOnce(&u8)` closure, found `{coroutine-closure@tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs:31:9: 31:30}` --> tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs:31:9 | 31 | foo(async move | f: &u8 | { *f }); | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce(&u8)` closure, found `{coroutine-closure@tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs:31:9: 31:30}` | | | required by a bound introduced by this call | (Note that you must add `#![feature(async_closure)]` to test with such old nightlies, since they are from before stabilization of async closures.) This was probably fixed by 3bb384aad6e7f6. --- .../unifying-function-types-involving-hrtb.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs diff --git a/tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs b/tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs new file mode 100644 index 0000000000000..05505206b7139 --- /dev/null +++ b/tests/ui/async-await/async-closures/unifying-function-types-involving-hrtb.rs @@ -0,0 +1,33 @@ +//! Regresssion test for . + +//@ edition:2018 +//@ check-pass + +use std::future::Future; + +trait Foo<'a> { + type Future: Future + 'a; + + fn start(self, f: &'a u8) -> Self::Future; +} + +impl<'a, Fn, Fut> Foo<'a> for Fn +where + Fn: FnOnce(&'a u8) -> Fut, + Fut: Future + 'a, +{ + type Future = Fut; + + fn start(self, f: &'a u8) -> Self::Future { (self)(f) } +} + +fn foo(f: F) where F: for<'a> Foo<'a> { + let bar = 5; + f.start(&bar); +} + +fn main() { + foo(async move | f: &u8 | { *f }); + + foo({ async fn baz(f: &u8) -> u8 { *f } baz }); +} From e72674e78c41ef332d7533324109db866f27621e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 22 Feb 2026 17:14:07 +0100 Subject: [PATCH 18/18] remove -Csoft-float --- compiler/rustc_codegen_llvm/src/back/write.rs | 12 +---- compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_session/src/errors.rs | 11 ---- compiler/rustc_session/src/lib.rs | 2 + compiler/rustc_session/src/options.rs | 52 ++++++++++++------- compiler/rustc_session/src/session.rs | 10 ---- 6 files changed, 38 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index efd4e55d5a856..ffa582737ca8c 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -23,9 +23,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; use rustc_span::{BytePos, InnerSpan, Pos, RemapPathScopeComponents, SpanData, SyntaxContext, sym}; -use rustc_target::spec::{ - Arch, CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel, -}; +use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; use crate::back::lto::{Buffer, ModuleBuffer}; @@ -206,13 +204,7 @@ pub(crate) fn target_machine_factory( let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); - let float_abi = if sess.target.arch == Arch::Arm && sess.opts.cg.soft_float { - llvm::FloatAbi::Soft - } else { - // `validate_commandline_args_with_session_available` has already warned about this being - // ignored. Let's make sure LLVM doesn't suddenly start using this flag on more targets. - to_llvm_float_abi(sess.target.llvm_floatabi) - }; + let float_abi = to_llvm_float_abi(sess.target.llvm_floatabi); let ffunction_sections = sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 6c2924dfe9bd8..f9d583bdf18c7 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -637,7 +637,6 @@ fn test_codegen_options_tracking_hash() { tracked!(profile_use, Some(PathBuf::from("abc"))); tracked!(relocation_model, Some(RelocModel::Pic)); tracked!(relro_level, Some(RelroLevel::Full)); - tracked!(soft_float, true); tracked!(split_debuginfo, Some(SplitDebuginfo::Packed)); tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); tracked!(target_cpu, Some(String::from("abc"))); diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index cb3f7363957ef..d1899de067b4f 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -517,17 +517,6 @@ pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, } -#[derive(Diagnostic)] -#[diag("`-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets")] -#[note("this may become a hard error in a future version of Rust")] -pub(crate) struct SoftFloatIgnored; - -#[derive(Diagnostic)] -#[diag("`-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead")] -#[note("it will be removed or ignored in a future version of Rust")] -#[note("see issue #129893 for more information")] -pub(crate) struct SoftFloatDeprecated; - #[derive(Diagnostic)] #[diag("unexpected `--cfg {$cfg}` flag")] #[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 98731a235d41d..1741dde90f5cf 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,5 +1,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![feature(const_option_ops)] +#![feature(const_trait_impl)] #![feature(default_field_values)] #![feature(iter_intersperse)] #![feature(macro_derive)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5db8ed823dfe3..d15c1ea4ee8fd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -611,7 +611,7 @@ macro_rules! options { $parse:ident, [$dep_tracking_marker:ident $( $tmod:ident )?], $desc:expr - $(, is_deprecated_and_do_nothing: $dnn:literal )?) + $(, removed: $removed:ident )?) ),* ,) => ( #[derive(Clone)] @@ -667,7 +667,7 @@ macro_rules! options { pub const $stat: OptionDescrs<$struct_name> = &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, - type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )?, + type_desc: desc::$parse, desc: $desc, removed: None $( .or(Some(RemovedOption::$removed)) )?, tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ]; mod $optmod { @@ -705,6 +705,12 @@ macro_rules! redirect_field { type OptionSetter = fn(&mut O, v: Option<&str>) -> bool; type OptionDescrs = &'static [OptionDesc]; +/// Indicates whether a removed option should warn or error. +enum RemovedOption { + Warn, + Err, +} + pub struct OptionDesc { name: &'static str, setter: OptionSetter, @@ -712,7 +718,7 @@ pub struct OptionDesc { type_desc: &'static str, // description for option from options table desc: &'static str, - is_deprecated_and_do_nothing: bool, + removed: Option, tmod: Option, } @@ -743,18 +749,18 @@ fn build_options( let option_to_lookup = key.replace('-', "_"); match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { - Some(OptionDesc { - name: _, - setter, - type_desc, - desc, - is_deprecated_and_do_nothing, - tmod, - }) => { - if *is_deprecated_and_do_nothing { + Some(OptionDesc { name: _, setter, type_desc, desc, removed, tmod }) => { + if let Some(removed) = removed { // deprecation works for prefixed options only assert!(!prefix.is_empty()); - early_dcx.early_warn(format!("`-{prefix} {key}`: {desc}")); + match removed { + RemovedOption::Warn => { + early_dcx.early_warn(format!("`-{prefix} {key}`: {desc}")) + } + RemovedOption::Err => { + early_dcx.early_fatal(format!("`-{prefix} {key}`: {desc}")) + } + } } if !setter(&mut op, value) { match value { @@ -783,6 +789,7 @@ fn build_options( #[allow(non_upper_case_globals)] mod desc { + pub(crate) const parse_ignore: &str = ""; // should not be user-visible pub(crate) const parse_no_value: &str = "no value"; pub(crate) const parse_bool: &str = "one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false`"; @@ -889,6 +896,12 @@ pub mod parse { pub(crate) use super::*; pub(crate) const MAX_THREADS_CAP: usize = 256; + /// Ignore the value. Used for removed options where we don't actually want to store + /// anything in the session. + pub(crate) fn parse_ignore(_slot: &mut (), _v: Option<&str>) -> bool { + true + } + /// This is for boolean options that don't take a value, and are true simply /// by existing on the command-line. /// @@ -2059,7 +2072,7 @@ options! { #[rustc_lint_opt_deny_field_access("documented to do nothing")] ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing", - is_deprecated_and_do_nothing: true), + removed: Warn), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), @@ -2098,7 +2111,7 @@ options! { inline_threshold: Option = (None, parse_opt_number, [UNTRACKED], "this option is deprecated and does nothing \ (consider using `-Cllvm-args=--inline-threshold=...`)", - is_deprecated_and_do_nothing: true), + removed: Warn), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage reports \ @@ -2139,7 +2152,7 @@ options! { #[rustc_lint_opt_deny_field_access("documented to do nothing")] no_stack_check: bool = (false, parse_no_value, [UNTRACKED], "this option is deprecated and does nothing", - is_deprecated_and_do_nothing: true), + removed: Warn), no_vectorize_loops: bool = (false, parse_no_value, [TRACKED], "disable loop vectorization optimization passes"), no_vectorize_slp: bool = (false, parse_no_value, [TRACKED], @@ -2173,8 +2186,11 @@ options! { "set rpath values in libs/exes (default: no)"), save_temps: bool = (false, parse_bool, [UNTRACKED], "save all temporary output files during compilation (default: no)"), - soft_float: bool = (false, parse_bool, [TRACKED], - "deprecated option: use soft float ABI (*eabihf targets only) (default: no)"), + #[rustc_lint_opt_deny_field_access("documented to do nothing")] + soft_float: () = ((), parse_ignore, [UNTRACKED], + "this option has been removed \ + (use a corresponding *eabi target instead)", + removed: Err), #[rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field")] split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 8290bdf91a030..fece46dd9711d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1360,16 +1360,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } } - - if sess.opts.cg.soft_float { - if sess.target.arch == Arch::Arm { - sess.dcx().emit_warn(errors::SoftFloatDeprecated); - } else { - // All `use_softfp` does is the equivalent of `-mfloat-abi` in GCC/clang, which only exists on ARM targets. - // We document this flag to only affect `*eabihf` targets, so let's show a warning for all other targets. - sess.dcx().emit_warn(errors::SoftFloatIgnored); - } - } } /// Holds data on the current incremental compilation session, if there is one.