Rollup merge of #150193 - Bryntet:parse_instruction_set, r=JonathanBrouwer

Port `#[instruction_set]` to attribute parser

Please note the test changes, and deprecation of `E0778` and `E0779`

In my opinion, all errors related to this attribute are improved I think, except for if you have `#[instruction_set(arm::)]` in which case there's an `error: expected identifier, found <eof>`, which is quite unhelpful I think, but this seems to be a limitation of the general attribute parsing flow

r? `@JonathanBrouwer`
This commit is contained in:
Jonathan Brouwer 2025-12-31 14:30:47 +01:00 committed by GitHub
commit 5183d8f15d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 281 additions and 109 deletions

View file

@ -229,6 +229,8 @@ attr_parsing_unstable_cfg_target_compact =
attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
attr_parsing_unsupported_instruction_set = target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`
attr_parsing_unsupported_literal_suggestion =
consider removing the prefix

View file

@ -0,0 +1,73 @@
use rustc_hir::attrs::InstructionSetAttr;
use super::prelude::*;
use crate::session_diagnostics;
pub(crate) struct InstructionSetParser;
impl<S: Stage> SingleAttributeParser<S> for InstructionSetParser {
const PATH: &[Symbol] = &[sym::instruction_set];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
Allow(Target::Fn),
Allow(Target::Closure),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: true })),
]);
const TEMPLATE: AttributeTemplate = template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute");
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
const POSSIBLE_SYMBOLS: &[Symbol] = &[sym::arm_a32, sym::arm_t32];
const POSSIBLE_ARM_SYMBOLS: &[Symbol] = &[sym::a32, sym::t32];
let Some(maybe_meta_item) = args.list().and_then(MetaItemListParser::single) else {
cx.expected_specific_argument(cx.attr_span, POSSIBLE_SYMBOLS);
return None;
};
let Some(meta_item) = maybe_meta_item.meta_item() else {
cx.expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS);
return None;
};
let mut segments = meta_item.path().segments();
let Some(architecture) = segments.next() else {
cx.expected_specific_argument(meta_item.span(), POSSIBLE_SYMBOLS);
return None;
};
let Some(instruction_set) = segments.next() else {
cx.expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS);
return None;
};
let instruction_set = match architecture.name {
sym::arm => {
if !cx.sess.target.has_thumb_interworking {
cx.dcx().emit_err(session_diagnostics::UnsupportedInstructionSet {
span: cx.attr_span,
instruction_set: sym::arm,
current_target: &cx.sess.opts.target_triple,
});
return None;
}
match instruction_set.name {
sym::a32 => InstructionSetAttr::ArmA32,
sym::t32 => InstructionSetAttr::ArmT32,
_ => {
cx.expected_specific_argument(instruction_set.span, POSSIBLE_ARM_SYMBOLS);
return None;
}
}
}
_ => {
cx.expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS);
return None;
}
};
Some(AttributeKind::InstructionSet(instruction_set))
}
}

View file

@ -42,6 +42,7 @@ pub(crate) mod deprecation;
pub(crate) mod doc;
pub(crate) mod dummy;
pub(crate) mod inline;
pub(crate) mod instruction_set;
pub(crate) mod link_attrs;
pub(crate) mod lint_helpers;
pub(crate) mod loop_match;

View file

@ -37,6 +37,7 @@ use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::doc::DocParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::instruction_set::InstructionSetParser;
use crate::attributes::link_attrs::{
ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
LinkParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser,
@ -198,6 +199,7 @@ attribute_parsers!(
Single<ExportNameParser>,
Single<IgnoreParser>,
Single<InlineParser>,
Single<InstructionSetParser>,
Single<LinkNameParser>,
Single<LinkOrdinalParser>,
Single<LinkSectionParser>,

View file

@ -9,6 +9,7 @@ use rustc_feature::AttributeTemplate;
use rustc_hir::AttrPath;
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use rustc_target::spec::TargetTuple;
use crate::fluent_generated as fluent;
@ -936,3 +937,12 @@ pub(crate) struct DocAliasMalformed {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_unsupported_instruction_set)]
pub(crate) struct UnsupportedInstructionSet<'a> {
#[primary_span]
pub span: Span,
pub instruction_set: Symbol,
pub current_target: &'a TargetTuple,
}

View file

@ -8,8 +8,6 @@ codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to
codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error}
codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument
codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty
codegen_ssa_cgu_not_recorded =
@ -90,8 +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_instruction_set = invalid instruction set specified
codegen_ssa_invalid_literal_value = invalid literal value
.label = value must be an integer between `0` and `255`
@ -215,8 +211,6 @@ codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but
codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions
codegen_ssa_multiple_instruction_set = cannot specify more than one instruction set
codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times
.help = did you use `#[no_mangle]` on `fn main`? Use `#![no_main]` to suppress the usual Rust-generated entry point
@ -383,8 +377,6 @@ codegen_ssa_unstable_ctarget_feature =
unstable feature specified for `-Ctarget-feature`: `{$feature}`
.note = this feature is not stably supported; its behavior can change in the future
codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]`
codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
codegen_ssa_use_cargo_directive = use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)

View file

@ -3,9 +3,7 @@ use std::str::FromStr;
use rustc_abi::{Align, ExternAbi};
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_hir::attrs::{
AttributeKind, InlineAttr, InstructionSetAttr, Linkage, RtsanSetting, UsedBy,
};
use rustc_hir::attrs::{AttributeKind, InlineAttr, Linkage, RtsanSetting, UsedBy};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items};
@ -47,37 +45,6 @@ fn try_fn_sig<'tcx>(
}
}
// FIXME(jdonszelmann): remove when instruction_set becomes a parsed attr
fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<InstructionSetAttr> {
let list = attr.meta_item_list()?;
match &list[..] {
[MetaItemInner::MetaItem(set)] => {
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
match segments.as_slice() {
[sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => {
tcx.dcx().emit_err(errors::UnsupportedInstructionSet { span: attr.span() });
None
}
[sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
[sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
_ => {
tcx.dcx().emit_err(errors::InvalidInstructionSet { span: attr.span() });
None
}
}
}
[] => {
tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
None
}
_ => {
tcx.dcx().emit_err(errors::MultipleInstructionSet { span: attr.span() });
None
}
}
}
// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
fn parse_patchable_function_entry(
tcx: TyCtxt<'_>,
@ -353,6 +320,9 @@ fn process_builtin_attrs(
AttributeKind::ThreadLocal => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL
}
AttributeKind::InstructionSet(instruction_set) => {
codegen_fn_attrs.instruction_set = Some(*instruction_set)
}
_ => {}
}
}
@ -369,9 +339,6 @@ fn process_builtin_attrs(
sym::rustc_allocator_zeroed => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
sym::instruction_set => {
codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
}
sym::patchable_function_entry => {
codegen_fn_attrs.patchable_function_entry =
parse_patchable_function_entry(tcx, attr);

View file

@ -136,34 +136,6 @@ pub(crate) struct RequiresRustAbi {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_unsupported_instruction_set, code = E0779)]
pub(crate) struct UnsupportedInstructionSet {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_instruction_set, code = E0779)]
pub(crate) struct InvalidInstructionSet {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_bare_instruction_set, code = E0778)]
pub(crate) struct BareInstructionSet {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_multiple_instruction_set, code = E0779)]
pub(crate) struct MultipleInstructionSet {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_expected_name_value_pair)]
pub(crate) struct ExpectedNameValuePair {

View file

@ -1,8 +1,9 @@
#### Note: this error code is no longer emitted by the compiler
The `instruction_set` attribute was malformed.
Erroneous code example:
```compile_fail,E0778
```compile_fail
#![feature(isa_attribute)]
#[instruction_set()] // error: expected one argument

View file

@ -1,8 +1,9 @@
#### Note: this error code is no longer emitted by the compiler
An unknown argument was given to the `instruction_set` attribute.
Erroneous code example:
```compile_fail,E0779
```compile_fail
#![feature(isa_attribute)]
#[instruction_set(intel::x64)] // error: invalid argument

View file

@ -60,7 +60,17 @@ impl InlineAttr {
}
}
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
#[derive(
Copy,
Clone,
Encodable,
Decodable,
Debug,
PartialEq,
Eq,
HashStable_Generic,
PrintAttribute
)]
pub enum InstructionSetAttr {
ArmA32,
ArmT32,
@ -807,6 +817,9 @@ pub enum AttributeKind {
/// Represents `#[inline]` and `#[rustc_force_inline]`.
Inline(InlineAttr, Span),
/// Represents `#[instruction_set]`
InstructionSet(InstructionSetAttr),
/// Represents `#[link]`.
Link(ThinVec<LinkEntry>, Span),

View file

@ -54,6 +54,7 @@ impl AttributeKind {
Fundamental { .. } => Yes,
Ignore { .. } => No,
Inline(..) => No,
InstructionSet(..) => No,
Link(..) => No,
LinkName { .. } => Yes, // Needed for rustdoc
LinkOrdinal { .. } => No,

View file

@ -233,6 +233,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::Dummy
| AttributeKind::RustcBuiltinMacro { .. }
| AttributeKind::Ignore { .. }
| AttributeKind::InstructionSet(..)
| AttributeKind::Path(..)
| AttributeKind::NoImplicitPrelude(..)
| AttributeKind::AutomaticallyDerived(..)
@ -340,7 +341,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::cfg_trace
| sym::cfg_attr_trace
// need to be fixed
| sym::instruction_set // broken on stable!!!
| sym::patchable_function_entry // FIXME(patchable_function_entry)
| sym::deprecated_safe // FIXME(deprecated_safe)
// internal

View file

@ -472,6 +472,8 @@ symbols! {
arith_offset,
arm,
arm64ec,
arm_a32: "arm::a32",
arm_t32: "arm::t32",
arm_target_feature,
array,
as_dash_needed: "as-needed",

View file

@ -0,0 +1,111 @@
//@ add-minicore
//@ compile-flags: --target armv5te-none-eabi
//@ needs-llvm-components: arm
//@ ignore-backends: gcc
//@ edition: 2024
#![crate_type = "lib"]
#![feature(no_core, lang_items,)]
#![no_core]
extern crate minicore;
use minicore::*;
#[instruction_set(arm::a32)]
fn foo() {
}
#[instruction_set(arm)]
//~^ ERROR malformed `instruction_set` attribute input [E0539]
fn bar() {
}
#[instruction_set(arm::)]
//~^ ERROR expected identifier, found `<eof>`
fn bazz() {
}
#[instruction_set(arm::magic)]
//~^ ERROR malformed `instruction_set` attribute input [E0539]
fn bazzer() {
}
fn all_instruction_set_cases() {
#[instruction_set(arm::a32)]
|| {
0
};
#[instruction_set(arm::t32)]
async || {
0
};
}
struct Fooer;
impl Fooer {
#[instruction_set(arm::a32)]
fn fooest() {
}
}
trait Bazzest {
fn bazz();
#[instruction_set(arm::a32)]
fn bazziest() {
}
}
impl Bazzest for Fooer {
#[instruction_set(arm::t32)]
fn bazz() {}
}
// The following lang items need to be defined for the async closure to work
#[lang = "ResumeTy"]
pub struct ResumeTy(NonNull<Context<'static>>);
#[lang = "future_trait"]
pub trait Future {
/// The type of value produced on completion.
#[lang = "future_output"]
type Output;
// NOTE: misses the `poll` method.
}
#[lang = "async_drop"]
pub trait AsyncDrop {
// NOTE: misses the `drop` method.
}
#[lang = "Poll"]
pub enum Poll<T> {
#[lang = "Ready"]
Ready(T),
#[lang = "Pending"]
Pending,
}
#[lang = "Context"]
pub struct Context<'a> {
// NOTE: misses a bunch of fields.
_marker: PhantomData<fn(&'a ()) -> &'a ()>,
}
#[lang = "get_context"]
pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> {
// NOTE: the actual implementation looks different.
unsafe {mem::transmute(cx.0)}
}
#[lang = "pin"]
pub struct Pin<T>(T);

View file

@ -0,0 +1,31 @@
error[E0539]: malformed `instruction_set` attribute input
--> $DIR/instruction-set.rs:21:1
|
LL | #[instruction_set(arm)]
| ^^^^^^^^^^^^^^^^^^---^^
| | |
| | valid arguments are `arm::a32` or `arm::t32`
| help: must be of the form: `#[instruction_set(set)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute>
error: expected identifier, found `<eof>`
--> $DIR/instruction-set.rs:26:22
|
LL | #[instruction_set(arm::)]
| ^^ expected identifier
error[E0539]: malformed `instruction_set` attribute input
--> $DIR/instruction-set.rs:31:1
|
LL | #[instruction_set(arm::magic)]
| ^^^^^^^^^^^^^^^^^^^^^^^-----^^
| | |
| | valid arguments are `a32` or `t32`
| help: must be of the form: `#[instruction_set(set)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute>
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0539`.

View file

@ -0,0 +1,6 @@
//@ only-x86_64-unknown-linux-gnu
#[instruction_set(arm::a32)]
//~^ ERROR target `x86_64-unknown-linux-gnu` does not support `#[instruction_set(arm::*)]`
fn main() {
}

View file

@ -0,0 +1,8 @@
error: target `x86_64-unknown-linux-gnu` does not support `#[instruction_set(arm::*)]`
--> $DIR/invalid-instruction-set-target.rs:3:1
|
LL | #[instruction_set(arm::a32)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -26,14 +26,6 @@ error[E0463]: can't find crate for `wloop`
LL | extern crate wloop;
| ^^^^^^^^^^^^^^^^^^^ can't find crate
error: malformed `instruction_set` attribute input
--> $DIR/malformed-attrs.rs:112:1
|
LL | #[instruction_set]
| ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[instruction_set(set)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute>
error: malformed `patchable_function_entry` attribute input
--> $DIR/malformed-attrs.rs:114:1
|
@ -456,6 +448,17 @@ LL | #[proc_macro = 18]
| | didn't expect any arguments here
| help: must be of the form: `#[proc_macro]`
error[E0539]: malformed `instruction_set` attribute input
--> $DIR/malformed-attrs.rs:112:1
|
LL | #[instruction_set]
| ^^^^^^^^^^^^^^^^^^
| |
| valid arguments are `arm::a32` or `arm::t32`
| help: must be of the form: `#[instruction_set(set)]`
|
= note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute>
error[E0565]: malformed `coroutine` attribute input
--> $DIR/malformed-attrs.rs:117:5
|

View file

@ -1,4 +0,0 @@
#[instruction_set()] //~ ERROR
fn no_isa_defined() {}
fn main() {}

View file

@ -1,9 +0,0 @@
error[E0778]: `#[instruction_set]` requires an argument
--> $DIR/E0778.rs:1:1
|
LL | #[instruction_set()]
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0778`.

View file

@ -1,2 +0,0 @@
#[instruction_set(arm::magic)] //~ ERROR
fn main() {}

View file

@ -1,9 +0,0 @@
error[E0779]: invalid instruction set specified
--> $DIR/E0779.rs:1:1
|
LL | #[instruction_set(arm::magic)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0779`.