Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 48 additions & 20 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::attributes::AttributeSafety;
use crate::session_diagnostics::{
EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
SanitizeInvalidStatic,
SanitizeInvalidStatic, TargetFeatureOnLangItem,
};
use crate::target_checking::Policy::AllowSilent;

Expand Down Expand Up @@ -513,26 +513,22 @@ fn parse_tf_attribute(
features
}

pub(crate) struct TargetFeatureParser;

impl CombineAttributeParser for TargetFeatureParser {
type Item = (Symbol, Span);
const PATH: &[Symbol] = &[sym::target_feature];
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
features: items,
attr_span: span,
was_forced: false,
};
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
const STABILITY: AttributeStability = AttributeStability::Stable;

fn extend(
cx: &mut AcceptContext<'_, '_>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
parse_tf_attribute(cx, args)
}
#[derive(Default)]
pub(crate) struct TargetFeatureParser {
features: ThinVec<(Symbol, Span)>,
first_span: Option<Span>,
}

impl AttributeParser for TargetFeatureParser {
const ATTRIBUTES: AcceptMapping<Self> = &[(
&[sym::target_feature],
template!(List: &["enable = \"feat1, feat2\""]),
AttributeStability::Stable,
|this, cx, args| {
this.first_span.get_or_insert(cx.attr_span);
this.features.extend(parse_tf_attribute(cx, args));
},
)];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Expand All @@ -544,6 +540,38 @@ impl CombineAttributeParser for TargetFeatureParser {
Warn(Target::MacroDef),
Warn(Target::MacroCall),
]);

fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
let first_span = self.first_span?;

// `#[target_feature]` is incompatible with lang item functions,
// except on WASM where calling target-feature functions is safe (see #84988).
if matches!(
cx.target,
Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)

@JonathanBrouwer JonathanBrouwer Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we need to check the target here. Is there any test that fails if you remove this condition, if so, could you add a comment in the code with the reason this check is needed?

View changes since the review

) && !cx.sess().target.is_like_wasm
&& !cx.sess().opts.actually_rustdoc
{
// `#[panic_handler]` is checked first so it takes priority in the diagnostic.
let lang_kind = cx
.all_attrs
.iter()
.find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));
if let Some(kind) = lang_kind {
cx.emit_err(TargetFeatureOnLangItem {
attr_span: first_span,
kind,
item_span: cx.target_span,
});
}
}

Some(AttributeKind::TargetFeature {
features: self.features,
attr_span: first_span,
was_forced: false,
})
}
}

pub(crate) struct ForceTargetFeatureParser;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ attribute_parsers!(
RustcAlignStaticParser,
RustcCguTestAttributeParser,
StabilityParser,
TargetFeatureParser,
UsedParser,
// tidy-alphabetical-end

Expand All @@ -172,7 +173,6 @@ attribute_parsers!(
Combine<RustcDumpLayoutParser>,
Combine<RustcMirParser>,
Combine<RustcThenThisWouldNeedParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
Combine<UnstableRemovedParser>,
// tidy-alphabetical-end
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,26 @@ pub(crate) struct DocAttributeNotAttribute {
pub attribute: Symbol,
}

#[derive(Diagnostic)]
#[diag(
"`#[target_feature]` cannot be applied to a {$kind ->
[panic_handler] `#[panic_handler]`
*[other] lang item
} function"
)]
pub(crate) struct TargetFeatureOnLangItem {
#[primary_span]
pub attr_span: Span,
pub kind: Symbol,
#[label(
"{$kind ->
[panic_handler] `#[panic_handler]`
*[other] lang item
} function is not allowed to have `#[target_feature]`"
)]
pub item_span: Span,
}

#[derive(Diagnostic)]
#[diag("missing 'since'", code = E0542)]
pub(crate) struct MissingSince {
Expand Down
35 changes: 1 addition & 34 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::Deprecated { span: attr_span, .. } => {
self.check_deprecated(hir_id, *attr_span, target)
}
AttributeKind::TargetFeature { attr_span, .. } => {
self.check_target_feature(hir_id, *attr_span, target, attrs)
}
AttributeKind::RustcDumpObjectLifetimeDefaults => {
self.check_dump_object_lifetime_defaults(hir_id);
}
Expand Down Expand Up @@ -405,6 +402,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::ShouldPanic { .. } => (),
AttributeKind::Splat(..) => (),
AttributeKind::Stability { .. } => (),
AttributeKind::TargetFeature { .. } => {}
AttributeKind::TestRunner(..) => (),
AttributeKind::ThreadLocal => (),
AttributeKind::TypeLengthLimit { .. } => (),
Expand Down Expand Up @@ -798,37 +796,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}

/// Checks if the `#[target_feature]` attribute on `item` is valid.
fn check_target_feature(
&self,
hir_id: HirId,
attr_span: Span,
target: Target,
attrs: &[Attribute],
) {
match target {
Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
| Target::Fn => {
// `#[target_feature]` is not allowed in lang items.
if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang)
// Calling functions with `#[target_feature]` is
// not unsafe on WASM, see #84988
&& !self.tcx.sess.target.is_like_wasm
&& !self.tcx.sess.opts.actually_rustdoc
{
let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();

self.dcx().emit_err(diagnostics::LangItemWithTargetFeature {
attr_span,
name: lang_item.name(),
sig_span: sig.span,
});
}
}
_ => {}
}
}

fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) {
if let Some(location) = match target {
Target::AssocTy => {
Expand Down
20 changes: 0 additions & 20 deletions compiler/rustc_passes/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,26 +378,6 @@ pub(crate) struct LangItemWithTrackCaller {
pub sig_span: Span,
}

#[derive(Diagnostic)]
#[diag(
"{$name ->
[panic_impl] `#[panic_handler]`
*[other] `{$name}` lang item
} function is not allowed to have `#[target_feature]`"
)]
pub(crate) struct LangItemWithTargetFeature {
#[primary_span]
pub attr_span: Span,
pub name: Symbol,
#[label(
"{$name ->
[panic_impl] `#[panic_handler]`
*[other] `{$name}` lang item
} function is not allowed to have `#[target_feature]`"
)]
pub sig_span: Span,
}

#[derive(Diagnostic)]
#[diag("`{$name}` lang item must be applied to a {$expected_target}", code = E0718)]
pub(crate) struct LangItemOnIncorrectTarget {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/lang-items/start_lang_item_with_target_feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub trait Sized: MetaSized {}

#[lang = "start"]
#[target_feature(enable = "avx2")]
//~^ ERROR `start` lang item function is not allowed to have `#[target_feature]`
//~^ ERROR #[target_feature]` cannot be applied to a lang item function
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
0
}
Expand Down
12 changes: 7 additions & 5 deletions tests/ui/lang-items/start_lang_item_with_target_feature.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
error: `start` lang item function is not allowed to have `#[target_feature]`
error: `#[target_feature]` cannot be applied to a lang item function
--> $DIR/start_lang_item_with_target_feature.rs:20:1
|
LL | #[target_feature(enable = "avx2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[target_feature(enable = "avx2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
| ------------------------------------------------------------------------------------------- `start` lang item function is not allowed to have `#[target_feature]`
LL | / fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
LL | | 0
LL | | }
| |_- lang item function is not allowed to have `#[target_feature]`

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use core::panic::PanicInfo;

#[panic_handler]
#[target_feature(enable = "avx2")]
//~^ ERROR `#[panic_handler]` function is not allowed to have `#[target_feature]`
//~^ ERROR `#[target_feature]` cannot be applied to a `#[panic_handler]` function
fn panic(info: &PanicInfo) -> ! {
unimplemented!();
}
12 changes: 7 additions & 5 deletions tests/ui/panic-handler/panic-handler-with-target-feature.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
error: `#[panic_handler]` function is not allowed to have `#[target_feature]`
error: `#[target_feature]` cannot be applied to a `#[panic_handler]` function
--> $DIR/panic-handler-with-target-feature.rs:10:1
|
LL | #[target_feature(enable = "avx2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[target_feature(enable = "avx2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn panic(info: &PanicInfo) -> ! {
| ------------------------------- `#[panic_handler]` function is not allowed to have `#[target_feature]`
LL | / fn panic(info: &PanicInfo) -> ! {
LL | | unimplemented!();
LL | | }
| |_- `#[panic_handler]` function is not allowed to have `#[target_feature]`

error: aborting due to 1 previous error

Loading