From b65e1fdcb8f85ccb91a99b81e8c4f99d9c8ee643 Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Sun, 18 Jan 2026 22:21:51 +0100 Subject: [PATCH 1/2] Port `#[patchable_function_entry]` to attr parser --- .../src/attributes/codegen_attrs.rs | 97 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 17 +++- .../src/session_diagnostics.rs | 15 +++ compiler/rustc_codegen_ssa/messages.ftl | 11 --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 61 +----------- compiler/rustc_codegen_ssa/src/errors.rs | 33 ------- .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 15 +-- .../patchable-function-entry-attribute.rs | 19 +++- .../patchable-function-entry-attribute.stderr | 64 ++++++++---- 13 files changed, 205 insertions(+), 135 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 5bbaeda18df3f..063fa12d38961 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -717,3 +717,100 @@ impl NoArgsAttributeParser for EiiForeignItemParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiForeignItem; } + +pub(crate) struct PatchableFunctionEntryParser; + +impl SingleAttributeParser for PatchableFunctionEntryParser { + const PATH: &[Symbol] = &[sym::patchable_function_entry]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(meta_item_list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let mut prefix = None; + let mut entry = None; + + if meta_item_list.len() == 0 { + cx.expected_list(meta_item_list.span, args); + return None; + } + + let mut errored = false; + + for item in meta_item_list.mixed() { + let Some(meta_item) = item.meta_item() else { + errored = true; + cx.expected_name_value(item.span(), None); + continue; + }; + + let Some(name_value_lit) = meta_item.args().name_value() else { + errored = true; + cx.expected_name_value(item.span(), None); + continue; + }; + + let attrib_to_write = match meta_item.ident().map(|ident| ident.name) { + Some(sym::prefix_nops) => { + // Duplicate prefixes are not allowed + if prefix.is_some() { + errored = true; + cx.duplicate_key(meta_item.path().span(), sym::prefix_nops); + continue; + } + &mut prefix + } + Some(sym::entry_nops) => { + // Duplicate entries are not allowed + if entry.is_some() { + errored = true; + cx.duplicate_key(meta_item.path().span(), sym::entry_nops); + continue; + } + &mut entry + } + _ => { + errored = true; + cx.expected_specific_argument( + meta_item.path().span(), + &[sym::prefix_nops, sym::entry_nops], + ); + continue; + } + }; + + let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else { + errored = true; + cx.expected_integer_literal(name_value_lit.value_span); + continue; + }; + + let Ok(val) = val.get().try_into() else { + errored = true; + cx.expected_integer_literal_in_range( + name_value_lit.value_span, + u8::MIN as isize, + u8::MAX as isize, + ); + continue; + }; + + *attrib_to_write = Some(val); + } + + if errored { + None + } else { + Some(AttributeKind::PatchableFunctionEntry { + prefix: prefix.unwrap_or(0), + entry: entry.unwrap_or(0), + }) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0c882fee01c8a..7bcc1f3448e5e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -23,8 +23,8 @@ use crate::attributes::cfi_encoding::CfiEncodingParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, - RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser, - ThreadLocalParser, TrackCallerParser, UsedParser, + PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, + TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ @@ -223,6 +223,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, @@ -504,6 +505,18 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } + pub(crate) fn expected_integer_literal_in_range( + &self, + span: Span, + lower_bound: isize, + upper_bound: isize, + ) -> ErrorGuaranteed { + self.emit_parse_error( + span, + AttributeParseErrorReason::ExpectedIntegerLiteralInRange { lower_bound, upper_bound }, + ) + } + pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed { let span = match args { ArgParser::NoArgs => span, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 85e7891b1e64b..f9748542beb94 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -525,6 +525,10 @@ pub(crate) enum AttributeParseErrorReason<'a> { byte_string: Option, }, ExpectedIntegerLiteral, + ExpectedIntegerLiteralInRange { + lower_bound: isize, + upper_bound: isize, + }, ExpectedAtLeastOneArgument, ExpectedSingleArgument, ExpectedList, @@ -596,6 +600,17 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedIntegerLiteral => { diag.span_label(self.span, "expected an integer literal here"); } + AttributeParseErrorReason::ExpectedIntegerLiteralInRange { + lower_bound, + upper_bound, + } => { + diag.span_label( + self.span, + format!( + "expected an integer literal in the range of {lower_bound}..={upper_bound}" + ), + ); + } AttributeParseErrorReason::ExpectedSingleArgument => { diag.span_label(self.span, "expected a single argument here"); diag.code(E0805); diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 4875f309cca5f..a49f411a7df62 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -48,8 +48,6 @@ codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$err codegen_ssa_error_writing_def_file = error writing .DEF file: {$error} -codegen_ssa_expected_name_value_pair = expected name value pair - codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified codegen_ssa_extract_bundled_libs_archive_member = failed to get data from archive member '{$rlib}': {$error} @@ -90,9 +88,6 @@ codegen_ssa_incorrect_cgu_reuse_type = codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient. -codegen_ssa_invalid_literal_value = invalid literal value - .label = value must be an integer between `0` and `255` - codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}` codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}` @@ -225,9 +220,6 @@ codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error} codegen_ssa_no_saved_object_file = cached cgu {$cgu_name} should have an object file, but doesn't -codegen_ssa_out_of_range_integer = integer value out of range - .label = value must be between `0` and `255` - codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status} .note = {$output} @@ -357,9 +349,6 @@ codegen_ssa_unable_to_run_dsymutil = unable to run `dsymutil`: {$error} codegen_ssa_unable_to_write_debugger_visualizer = unable to write debugger visualizer file `{$path}`: {$error} -codegen_ssa_unexpected_parameter_name = unexpected parameter name - .label = expected `{$prefix_nops}` or `{$entry_nops}` - codegen_ssa_unknown_archive_kind = don't know how to build archive of type: {$kind} diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index e35d884b6711c..296c96ce4ae60 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -47,59 +47,6 @@ fn try_fn_sig<'tcx>( } } -// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr -fn parse_patchable_function_entry( - tcx: TyCtxt<'_>, - attr: &Attribute, -) -> Option { - attr.meta_item_list().and_then(|l| { - let mut prefix = None; - let mut entry = None; - for item in l { - let Some(meta_item) = item.meta_item() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let Some(name_value_lit) = meta_item.name_value_literal() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let attrib_to_write = match meta_item.name() { - Some(sym::prefix_nops) => &mut prefix, - Some(sym::entry_nops) => &mut entry, - _ => { - tcx.dcx().emit_err(errors::UnexpectedParameterName { - span: item.span(), - prefix_nops: sym::prefix_nops, - entry_nops: sym::entry_nops, - }); - continue; - } - }; - - let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { - tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span }); - continue; - }; - - let Ok(val) = val.get().try_into() else { - tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); - continue; - }; - - *attrib_to_write = Some(val); - } - - if let (None, None) = (prefix, entry) { - tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); - } - - Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0))) - }) -} - /// Spans that are collected when processing built-in attributes, /// that are useful for emitting diagnostics later. #[derive(Default)] @@ -353,6 +300,10 @@ fn process_builtin_attrs( AttributeKind::RustcOffloadKernel => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL } + AttributeKind::PatchableFunctionEntry { prefix, entry } => { + codegen_fn_attrs.patchable_function_entry = + Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + } _ => {} } } @@ -362,10 +313,6 @@ fn process_builtin_attrs( }; match name { - sym::patchable_function_entry => { - codegen_fn_attrs.patchable_function_entry = - parse_patchable_function_entry(tcx, attr); - } _ => {} } } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 39727685aec1a..6a97de4c2b132 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -136,39 +136,6 @@ pub(crate) struct RequiresRustAbi { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_expected_name_value_pair)] -pub(crate) struct ExpectedNameValuePair { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_unexpected_parameter_name)] -pub(crate) struct UnexpectedParameterName { - #[primary_span] - #[label] - pub span: Span, - pub prefix_nops: Symbol, - pub entry_nops: Symbol, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_literal_value)] -pub(crate) struct InvalidLiteralValue { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_out_of_range_integer)] -pub(crate) struct OutOfRangeInteger { - #[primary_span] - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_copy_path_buf)] pub(crate) struct CopyPathBuf { diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 314f36d6132d3..6e84d60f062f9 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -879,6 +879,9 @@ pub enum AttributeKind { /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint). PassByValue(Span), + /// Represents `#[patchable_function_entry]` + PatchableFunctionEntry { prefix: u8, entry: u8 }, + /// Represents `#[path]` Path(Symbol, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index b55a5d0e29e14..1c13cef64fead 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -88,6 +88,7 @@ impl AttributeKind { Optimize(..) => No, ParenSugar(..) => No, PassByValue(..) => Yes, + PatchableFunctionEntry { .. } => Yes, Path(..) => No, PatternComplexityLimit { .. } => No, PinV2(..) => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 806f5c4d3ed91..c2ad644688fce 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -171,7 +171,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); -print_disp!(u16, u128, usize, bool, NonZero, Limit); +print_disp!(u8, u16, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8074ae4298923..3ddf51f6c9b1f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -324,6 +324,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcReallocator | AttributeKind::RustcNounwind | AttributeKind::RustcOffloadKernel + | AttributeKind::PatchableFunctionEntry { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -349,7 +350,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::deny | sym::forbid // need to be fixed - | sym::patchable_function_entry // FIXME(patchable_function_entry) | sym::deprecated_safe // FIXME(deprecated_safe) // internal | sym::prelude_import diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 3d51731df792d..f817a0b0d91b1 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -26,12 +26,6 @@ error[E0463]: can't find crate for `wloop` LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate -error: malformed `patchable_function_entry` attribute input - --> $DIR/malformed-attrs.rs:114:1 - | -LL | #[patchable_function_entry] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` - error: malformed `allow` attribute input --> $DIR/malformed-attrs.rs:184:1 | @@ -444,6 +438,15 @@ LL | #[instruction_set] | = note: for more information, visit +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/malformed-attrs.rs:114:1 + | +LL | #[patchable_function_entry] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + error[E0565]: malformed `coroutine` attribute input --> $DIR/malformed-attrs.rs:117:5 | diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs index 1e376c9ff3c1a..5cbeccf1b0e4f 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs @@ -1,17 +1,26 @@ #![feature(patchable_function_entry)] fn main() {} -#[patchable_function_entry(prefix_nops = 256, entry_nops = 0)]//~error: integer value out of range +#[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] +//~^ ERROR malformed pub fn too_high_pnops() {} -#[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)]//~error: invalid literal value +#[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] +//~^ ERROR malformed pub fn non_int_nop() {} -#[patchable_function_entry]//~error: malformed `patchable_function_entry` attribute input +#[patchable_function_entry] +//~^ ERROR malformed `patchable_function_entry` attribute input pub fn malformed_attribute() {} -#[patchable_function_entry(prefix_nops = 10, something = 0)]//~error: unexpected parameter name +#[patchable_function_entry(prefix_nops = 10, something = 0)] +//~^ ERROR malformed pub fn unexpected_parameter_name() {} -#[patchable_function_entry()]//~error: must specify at least one parameter +#[patchable_function_entry()] +//~^ ERROR malformed pub fn no_parameters_given() {} + +#[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] +//~^ ERROR malformed +pub fn duplicate_parameter() {} diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index 9357a86c4153f..43fc0c0518af1 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -1,32 +1,58 @@ -error: malformed `patchable_function_entry` attribute input - --> $DIR/patchable-function-entry-attribute.rs:10:1 - | -LL | #[patchable_function_entry] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` - -error: integer value out of range - --> $DIR/patchable-function-entry-attribute.rs:4:42 +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:4:1 | LL | #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] - | ^^^ value must be between `0` and `255` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^^^^^^^^^^^^^^^^ + | | | + | | expected an integer literal in the range of 0..=255 + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: invalid literal value - --> $DIR/patchable-function-entry-attribute.rs:7:42 +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:8:1 | LL | #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] - | ^^^^^^^^^^^^^ value must be an integer between `0` and `255` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------^^^^^^^^^^^^^^^^^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: unexpected parameter name - --> $DIR/patchable-function-entry-attribute.rs:13:46 +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:12:1 | -LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] - | ^^^^^^^^^^^^^ expected `prefix_nops` or `entry_nops` +LL | #[patchable_function_entry] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: must specify at least one parameter +error[E0539]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:16:1 | +LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^^^^^^ + | | | + | | valid arguments are `prefix_nops` or `entry_nops` + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:20:1 + | LL | #[patchable_function_entry()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^--^ + | | | + | | expected this to be a list + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + +error[E0538]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:24:1 + | +LL | #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^ + | | | + | | found `prefix_nops` used as a key more than once + | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors +Some errors have detailed explanations: E0538, E0539. +For more information about an error, try `rustc --explain E0538`. From a0b3ee2f76444c8cd82afc77246c868426290b37 Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Mon, 19 Jan 2026 11:22:24 +0100 Subject: [PATCH 2/2] only process parsed attrs for codegen check --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 441 +++++++++--------- 1 file changed, 213 insertions(+), 228 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 296c96ce4ae60..d55e134109d66 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -68,251 +68,236 @@ fn process_builtin_attrs( let mut interesting_spans = InterestingAttributeDiagnosticSpans::default(); let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); - for attr in attrs.iter() { - if let hir::Attribute::Parsed(p) = attr { - match p { - AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, - AttributeKind::ExportName { name, .. } => { - codegen_fn_attrs.symbol_name = Some(*name) + let parsed_attrs = attrs + .iter() + .filter_map(|attr| if let hir::Attribute::Parsed(attr) = attr { Some(attr) } else { None }); + for attr in parsed_attrs { + match attr { + AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, + AttributeKind::ExportName { name, .. } => codegen_fn_attrs.symbol_name = Some(*name), + AttributeKind::Inline(inline, span) => { + codegen_fn_attrs.inline = *inline; + interesting_spans.inline = Some(*span); + } + AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, + AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), + AttributeKind::LinkName { name, .. } => { + // FIXME Remove check for foreign functions once #[link_name] on non-foreign + // functions is a hard error + if tcx.is_foreign_item(did) { + codegen_fn_attrs.symbol_name = Some(*name); } - AttributeKind::Inline(inline, span) => { - codegen_fn_attrs.inline = *inline; - interesting_spans.inline = Some(*span); + } + AttributeKind::LinkOrdinal { ordinal, span } => { + codegen_fn_attrs.link_ordinal = Some(*ordinal); + interesting_spans.link_ordinal = Some(*span); + } + AttributeKind::LinkSection { name, .. } => codegen_fn_attrs.link_section = Some(*name), + AttributeKind::NoMangle(attr_span) => { + interesting_spans.no_mangle = Some(*attr_span); + if tcx.opt_item_name(did.to_def_id()).is_some() { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } else { + tcx.dcx() + .span_delayed_bug(*attr_span, "no_mangle should be on a named function"); } - AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, - AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), - AttributeKind::LinkName { name, .. } => { - // FIXME Remove check for foreign functions once #[link_name] on non-foreign - // functions is a hard error - if tcx.is_foreign_item(did) { - codegen_fn_attrs.symbol_name = Some(*name); + } + AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, + AttributeKind::TargetFeature { features, attr_span, was_forced } => { + let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { + tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); + continue; + }; + let safe_target_features = + matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures); + codegen_fn_attrs.safe_target_features = safe_target_features; + if safe_target_features && !was_forced { + if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { + // The `#[target_feature]` attribute is allowed on + // WebAssembly targets on all functions. Prior to stabilizing + // the `target_feature_11` feature, `#[target_feature]` was + // only permitted on unsafe functions because on most targets + // execution of instructions that are not supported is + // considered undefined behavior. For WebAssembly which is a + // 100% safe target at execution time it's not possible to + // execute undefined instructions, and even if a future + // feature was added in some form for this it would be a + // deterministic trap. There is no undefined behavior when + // executing WebAssembly so `#[target_feature]` is allowed + // on safe functions (but again, only for WebAssembly) + // + // Note that this is also allowed if `actually_rustdoc` so + // if a target is documenting some wasm-specific code then + // it's not spuriously denied. + // + // Now that `#[target_feature]` is permitted on safe functions, + // this exception must still exist for allowing the attribute on + // `main`, `start`, and other functions that are not usually + // allowed. + } else { + check_target_feature_trait_unsafe(tcx, did, *attr_span); } } - AttributeKind::LinkOrdinal { ordinal, span } => { - codegen_fn_attrs.link_ordinal = Some(*ordinal); - interesting_spans.link_ordinal = Some(*span); + from_target_feature_attr( + tcx, + did, + features, + *was_forced, + rust_target_features, + &mut codegen_fn_attrs.target_features, + ); + } + AttributeKind::TrackCaller(attr_span) => { + let is_closure = tcx.is_closure_like(did.to_def_id()); + + if !is_closure + && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span) + && fn_sig.skip_binder().abi() != ExternAbi::Rust + { + tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); } - AttributeKind::LinkSection { name, .. } => { - codegen_fn_attrs.link_section = Some(*name) + if is_closure + && !tcx.features().closure_track_caller() + && !attr_span.allows_unstable(sym::closure_track_caller) + { + feature_err( + &tcx.sess, + sym::closure_track_caller, + *attr_span, + "`#[track_caller]` on closures is currently unstable", + ) + .emit(); } - AttributeKind::NoMangle(attr_span) => { - interesting_spans.no_mangle = Some(*attr_span); - if tcx.opt_item_name(did.to_def_id()).is_some() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER + } + AttributeKind::Used { used_by, .. } => match used_by { + UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, + UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, + UsedBy::Default => { + let used_form = if tcx.sess.target.os == Os::Illumos { + // illumos' `ld` doesn't support a section header that would represent + // `#[used(linker)]`, see + // https://github.com/rust-lang/rust/issues/146169. For that target, + // downgrade as if `#[used(compiler)]` was requested and hope for the + // best. + CodegenFnAttrFlags::USED_COMPILER } else { - tcx.dcx().span_delayed_bug( - *attr_span, - "no_mangle should be on a named function", - ); - } - } - AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, - AttributeKind::TargetFeature { features, attr_span, was_forced } => { - let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { - tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); - continue; + CodegenFnAttrFlags::USED_LINKER }; - let safe_target_features = - matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures); - codegen_fn_attrs.safe_target_features = safe_target_features; - if safe_target_features && !was_forced { - if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { - // The `#[target_feature]` attribute is allowed on - // WebAssembly targets on all functions. Prior to stabilizing - // the `target_feature_11` feature, `#[target_feature]` was - // only permitted on unsafe functions because on most targets - // execution of instructions that are not supported is - // considered undefined behavior. For WebAssembly which is a - // 100% safe target at execution time it's not possible to - // execute undefined instructions, and even if a future - // feature was added in some form for this it would be a - // deterministic trap. There is no undefined behavior when - // executing WebAssembly so `#[target_feature]` is allowed - // on safe functions (but again, only for WebAssembly) - // - // Note that this is also allowed if `actually_rustdoc` so - // if a target is documenting some wasm-specific code then - // it's not spuriously denied. - // - // Now that `#[target_feature]` is permitted on safe functions, - // this exception must still exist for allowing the attribute on - // `main`, `start`, and other functions that are not usually - // allowed. - } else { - check_target_feature_trait_unsafe(tcx, did, *attr_span); - } - } - from_target_feature_attr( - tcx, - did, - features, - *was_forced, - rust_target_features, - &mut codegen_fn_attrs.target_features, - ); + codegen_fn_attrs.flags |= used_form; } - AttributeKind::TrackCaller(attr_span) => { - let is_closure = tcx.is_closure_like(did.to_def_id()); + }, + AttributeKind::FfiConst(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST, + AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, + AttributeKind::StdInternalSymbol(_) => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + } + AttributeKind::Linkage(linkage, span) => { + let linkage = Some(*linkage); - if !is_closure - && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span) - && fn_sig.skip_binder().abi() != ExternAbi::Rust - { - tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); - } - if is_closure - && !tcx.features().closure_track_caller() - && !attr_span.allows_unstable(sym::closure_track_caller) - { - feature_err( - &tcx.sess, - sym::closure_track_caller, - *attr_span, - "`#[track_caller]` on closures is currently unstable", - ) - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER - } - AttributeKind::Used { used_by, .. } => match used_by { - UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, - UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, - UsedBy::Default => { - let used_form = if tcx.sess.target.os == Os::Illumos { - // illumos' `ld` doesn't support a section header that would represent - // `#[used(linker)]`, see - // https://github.com/rust-lang/rust/issues/146169. For that target, - // downgrade as if `#[used(compiler)]` was requested and hope for the - // best. - CodegenFnAttrFlags::USED_COMPILER - } else { - CodegenFnAttrFlags::USED_LINKER - }; - codegen_fn_attrs.flags |= used_form; - } - }, - AttributeKind::FfiConst(_) => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST - } - AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, - AttributeKind::StdInternalSymbol(_) => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL - } - AttributeKind::Linkage(linkage, span) => { - let linkage = Some(*linkage); - - if tcx.is_foreign_item(did) { - codegen_fn_attrs.import_linkage = linkage; - - if tcx.is_mutable_static(did.into()) { - let mut diag = tcx.dcx().struct_span_err( - *span, - "extern mutable statics are not allowed with `#[linkage]`", - ); - diag.note( - "marking the extern static mutable would allow changing which \ - symbol the static references rather than make the target of the \ - symbol mutable", - ); - diag.emit(); - } - } else { - codegen_fn_attrs.linkage = linkage; + if tcx.is_foreign_item(did) { + codegen_fn_attrs.import_linkage = linkage; + + if tcx.is_mutable_static(did.into()) { + let mut diag = tcx.dcx().struct_span_err( + *span, + "extern mutable statics are not allowed with `#[linkage]`", + ); + diag.note( + "marking the extern static mutable would allow changing which \ + symbol the static references rather than make the target of the \ + symbol mutable", + ); + diag.emit(); } + } else { + codegen_fn_attrs.linkage = linkage; } - AttributeKind::Sanitize { span, .. } => { - interesting_spans.sanitize = Some(*span); - } - AttributeKind::ObjcClass { classname, .. } => { - codegen_fn_attrs.objc_class = Some(*classname); - } - AttributeKind::ObjcSelector { methname, .. } => { - codegen_fn_attrs.objc_selector = Some(*methname); - } - AttributeKind::EiiForeignItem => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; - } - AttributeKind::EiiImpls(impls) => { - for i in impls { - let foreign_item = match i.resolution { - EiiImplResolution::Macro(def_id) => { - let Some(extern_item) = find_attr!( - tcx.get_all_attrs(def_id), - AttributeKind::EiiDeclaration(target) => target.foreign_item - ) else { - tcx.dcx().span_delayed_bug( - i.span, - "resolved to something that's not an EII", - ); - continue; - }; - extern_item - } - EiiImplResolution::Known(decl) => decl.foreign_item, - EiiImplResolution::Error(_eg) => continue, - }; - - // this is to prevent a bug where a single crate defines both the default and explicit implementation - // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure - // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. - // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that - // the default implementation is used while an explicit implementation is given. - if - // if this is a default impl - i.is_default - // iterate over all implementations *in the current crate* - // (this is ok since we generate codegen fn attrs in the local crate) - // if any of them is *not default* then don't emit the alias. - && tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) - { - continue; + } + AttributeKind::Sanitize { span, .. } => { + interesting_spans.sanitize = Some(*span); + } + AttributeKind::ObjcClass { classname, .. } => { + codegen_fn_attrs.objc_class = Some(*classname); + } + AttributeKind::ObjcSelector { methname, .. } => { + codegen_fn_attrs.objc_selector = Some(*methname); + } + AttributeKind::EiiForeignItem => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + } + AttributeKind::EiiImpls(impls) => { + for i in impls { + let foreign_item = match i.resolution { + EiiImplResolution::Macro(def_id) => { + let Some(extern_item) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::EiiDeclaration(target) => target.foreign_item + ) else { + tcx.dcx().span_delayed_bug( + i.span, + "resolved to something that's not an EII", + ); + continue; + }; + extern_item } + EiiImplResolution::Known(decl) => decl.foreign_item, + EiiImplResolution::Error(_eg) => continue, + }; - codegen_fn_attrs.foreign_item_symbol_aliases.push(( - foreign_item, - if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, - Visibility::Default, - )); - codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + // this is to prevent a bug where a single crate defines both the default and explicit implementation + // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure + // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. + // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that + // the default implementation is used while an explicit implementation is given. + if + // if this is a default impl + i.is_default + // iterate over all implementations *in the current crate* + // (this is ok since we generate codegen fn attrs in the local crate) + // if any of them is *not default* then don't emit the alias. + && tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) + { + continue; } + + codegen_fn_attrs.foreign_item_symbol_aliases.push(( + foreign_item, + if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, + Visibility::Default, + )); + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } - AttributeKind::ThreadLocal => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL - } - AttributeKind::InstructionSet(instruction_set) => { - codegen_fn_attrs.instruction_set = Some(*instruction_set) - } - AttributeKind::RustcAllocator => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR - } - AttributeKind::RustcDeallocator => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR - } - AttributeKind::RustcReallocator => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR - } - AttributeKind::RustcAllocatorZeroed => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED - } - AttributeKind::RustcNounwind => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND - } - AttributeKind::RustcOffloadKernel => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL - } - AttributeKind::PatchableFunctionEntry { prefix, entry } => { - codegen_fn_attrs.patchable_function_entry = - Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); - } - _ => {} } - } - - let Some(name) = attr.name() else { - continue; - }; - - match name { + AttributeKind::ThreadLocal => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL + } + AttributeKind::InstructionSet(instruction_set) => { + codegen_fn_attrs.instruction_set = Some(*instruction_set) + } + AttributeKind::RustcAllocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR + } + AttributeKind::RustcDeallocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR + } + AttributeKind::RustcReallocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR + } + AttributeKind::RustcAllocatorZeroed => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED + } + AttributeKind::RustcNounwind => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND + } + AttributeKind::RustcOffloadKernel => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL + } + AttributeKind::PatchableFunctionEntry { prefix, entry } => { + codegen_fn_attrs.patchable_function_entry = + Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + } _ => {} } }