Rollup merge of #152568 - JonathanBrouwer:port_lang, r=jdonszelmann

Port `#[lang]` and `#[panic_handler]` to the new attribute parsers

For https://github.com/rust-lang/rust/issues/131229#issuecomment-2971351163

r? @jdonszelmann
This commit is contained in:
Jacob Pratt 2026-02-13 22:26:35 -05:00 committed by GitHub
commit 22f973db34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 105 additions and 56 deletions

View file

@ -1,6 +1,7 @@
use std::path::PathBuf;
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
use rustc_hir::LangItem;
use rustc_hir::attrs::{
BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior,
DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType,
@ -12,7 +13,7 @@ use rustc_span::Symbol;
use super::prelude::*;
use super::util::parse_single_integer;
use crate::session_diagnostics::{
AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange,
AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem,
};
pub(crate) struct RustcMainParser;
@ -626,6 +627,32 @@ impl<S: Stage> SingleAttributeParser<S> for RustcScalableVectorParser {
}
}
pub(crate) struct LangParser;
impl<S: Stage> SingleAttributeParser<S> for LangParser {
const PATH: &[Symbol] = &[sym::lang];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes`
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(name) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let Some(lang_item) = LangItem::from_name(name) else {
cx.emit_err(UnknownLangItem { span: cx.attr_span, name });
return None;
};
Some(AttributeKind::Lang(lang_item, cx.attr_span))
}
}
pub(crate) struct RustcHasIncoherentInherentImplsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcHasIncoherentInherentImplsParser {
@ -641,6 +668,15 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcHasIncoherentInherentImplsParse
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls;
}
pub(crate) struct PanicHandlerParser;
impl<S: Stage> NoArgsAttributeParser<S> for PanicHandlerParser {
const PATH: &[Symbol] = &[sym::panic_handler];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes`
const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::Lang(LangItem::PanicImpl, span);
}
pub(crate) struct RustcHiddenTypeOfOpaquesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcHiddenTypeOfOpaquesParser {

View file

@ -181,6 +181,7 @@ attribute_parsers!(
Single<IgnoreParser>,
Single<InlineParser>,
Single<InstructionSetParser>,
Single<LangParser>,
Single<LinkNameParser>,
Single<LinkOrdinalParser>,
Single<LinkSectionParser>,
@ -254,6 +255,7 @@ attribute_parsers!(
Single<WithoutArgs<NoMangleParser>>,
Single<WithoutArgs<NoStdParser>>,
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<PanicHandlerParser>>,
Single<WithoutArgs<PanicRuntimeParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,

View file

@ -1013,6 +1013,15 @@ pub(crate) struct DocAliasMalformed {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("definition of an unknown lang item: `{$name}`", code = E0522)]
pub(crate) struct UnknownLangItem {
#[primary_span]
#[label("definition of unknown lang item `{$name}`")]
pub span: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag("target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`")]
pub(crate) struct UnsupportedInstructionSet<'a> {

View file

@ -8,7 +8,7 @@ use rustc_hir::attrs::{
};
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};
use rustc_hir::{self as hir, Attribute, find_attr};
use rustc_middle::middle::codegen_fn_attrs::{
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
};
@ -504,7 +504,7 @@ fn handle_lang_items(
attrs: &[Attribute],
codegen_fn_attrs: &mut CodegenFnAttrs,
) {
let lang_item = lang_items::extract(attrs).and_then(|(name, _)| LangItem::from_name(name));
let lang_item = find_attr!(attrs, AttributeKind::Lang(lang, _) => lang);
// Weak lang items have the same semantics as "std internal" symbols in the
// sense that they're preserved through all our LTO passes and only

View file

@ -7,8 +7,8 @@ Erroneous code example:
#![allow(internal_features)]
extern "C" {
#[lang = "cake"] // error: unknown external lang item: `cake`
fn cake();
#[lang = "copy"] // error: unknown external lang item: `copy`
fn copy();
}
```

View file

@ -8,6 +8,7 @@ use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrStyle, Path, ast};
use rustc_data_structures::fx::FxIndexMap;
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_hir::LangItem;
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::def_id::DefId;
use rustc_span::hygiene::Transparency;
@ -953,6 +954,9 @@ pub enum AttributeKind {
/// Represents `#[instruction_set]`
InstructionSet(InstructionSetAttr),
/// Represents `#[lang]`
Lang(LangItem, Span),
/// Represents `#[link]`.
Link(ThinVec<LinkEntry>, Span),

View file

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

View file

@ -7,12 +7,12 @@
//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
//! * Functions called by the compiler itself.
use rustc_ast::attr::AttributeExt;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic};
use rustc_span::{Span, Symbol, kw, sym};
use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::{Symbol, kw, sym};
use crate::attrs::PrintAttribute;
use crate::def_id::DefId;
use crate::{MethodKind, Target};
@ -75,7 +75,7 @@ macro_rules! language_item_table {
$( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )*
) => {
/// A representation of all the valid lang items in Rust.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, BlobDecodable)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, BlobDecodable, PrintAttribute)]
pub enum LangItem {
$(
#[doc = concat!("The `", stringify!($name), "` lang item.")]
@ -150,18 +150,6 @@ impl<CTX> HashStable<CTX> for LangItem {
}
}
/// Extracts the first `lang = "$name"` out of a list of attributes.
/// The `#[panic_handler]` attribute is also extracted out when found.
pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> {
attrs.iter().find_map(|attr| {
Some(match attr {
_ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
_ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
_ => return None,
})
})
}
language_item_table! {
// Variant name, Name, Getter method name, Target Generic requirements;
Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);

View file

@ -256,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::Fundamental
| AttributeKind::Ignore { .. }
| AttributeKind::InstructionSet(..)
| AttributeKind::Lang(..)
| AttributeKind::LinkName { .. }
| AttributeKind::LinkOrdinal { .. }
| AttributeKind::LinkSection { .. }
@ -394,8 +395,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::deny
| sym::forbid
// internal
| sym::panic_handler
| sym::lang
| sym::default_lib_allocator
| sym::rustc_nonnull_optimization_guaranteed
| sym::rustc_inherit_overflow_checks
@ -784,15 +783,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Target::Fn => {
// `#[track_caller]` is not valid on weak lang items because they are called via
// `extern` declarations and `#[track_caller]` would alter their ABI.
if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
&& let Some(item) = hir::LangItem::from_name(lang_item)
if let Some(item) = find_attr!(attrs, AttributeKind::Lang(item, _) => item)
&& item.is_weak()
{
let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
self.dcx().emit_err(errors::LangItemWithTrackCaller {
attr_span,
name: lang_item,
name: item.name(),
sig_span: sig.span,
});
}
@ -856,7 +854,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
| Target::Fn => {
// `#[target_feature]` is not allowed in lang items.
if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
if let Some(lang_item) = find_attr!(attrs, AttributeKind::Lang(lang, _) => lang)
// Calling functions with `#[target_feature]` is
// not unsafe on WASM, see #84988
&& !self.tcx.sess.target.is_like_wasm
@ -866,7 +864,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.dcx().emit_err(errors::LangItemWithTargetFeature {
attr_span,
name: lang_item,
name: lang_item.name(),
sig_span: sig.span,
});
}

View file

@ -22,7 +22,7 @@ use rustc_middle::ty::{self, AssocTag, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::DEAD_CODE;
use rustc_session::lint::{self, LintExpectationId};
use rustc_span::{Symbol, kw, sym};
use rustc_span::{Symbol, kw};
use crate::errors::{
ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment,
@ -706,12 +706,6 @@ fn has_allow_dead_code_or_lang_attr(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
) -> Option<ComesFromAllowExpect> {
fn has_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.has_attr(def_id, sym::lang)
// Stable attribute for #[lang = "panic_impl"]
|| tcx.has_attr(def_id, sym::panic_handler)
}
fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level;
@ -732,7 +726,9 @@ fn has_allow_dead_code_or_lang_attr(
if has_allow_expect_dead_code(tcx, def_id) {
Some(ComesFromAllowExpect::Yes)
} else if has_used_like_attr(tcx, def_id) || has_lang_attr(tcx, def_id) {
} else if has_used_like_attr(tcx, def_id)
|| find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Lang(..))
{
Some(ComesFromAllowExpect::No)
} else {
None

View file

@ -465,15 +465,6 @@ pub(crate) struct LangItemOnIncorrectTarget {
pub actual_target: Target,
}
#[derive(Diagnostic)]
#[diag("definition of an unknown lang item: `{$name}`", code = E0522)]
pub(crate) struct UnknownLangItem {
#[primary_span]
#[label("definition of unknown lang item `{$name}`")]
pub span: Span,
pub name: Symbol,
}
pub(crate) struct InvalidAttrAtCrateLevel {
pub span: Span,
pub sugg_span: Option<Span>,

View file

@ -11,16 +11,15 @@ use rustc_ast as ast;
use rustc_ast::visit;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::{GenericRequirement, extract};
use rustc_hir::lang_items::GenericRequirement;
use rustc_hir::{LangItem, LanguageItems, MethodKind, Target};
use rustc_middle::query::Providers;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_session::cstore::ExternCrate;
use rustc_span::Span;
use rustc_span::{Span, Symbol, sym};
use crate::errors::{
DuplicateLangItem, IncorrectCrateType, IncorrectTarget, LangItemOnIncorrectTarget,
UnknownLangItem,
};
use crate::weak_lang_items;
@ -62,7 +61,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
item_span: Span,
generics: Option<&'ast ast::Generics>,
) {
if let Some((name, attr_span)) = extract(attrs) {
if let Some((name, attr_span)) = extract_ast(attrs) {
match LangItem::from_name(name) {
// Known lang item with attribute on correct target.
Some(lang_item) if actual_target == lang_item.target() => {
@ -86,7 +85,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
}
// Unknown lang item.
_ => {
self.tcx.dcx().emit_err(UnknownLangItem { span: attr_span, name });
self.tcx.dcx().delayed_bug("unknown lang item");
}
}
}
@ -359,6 +358,20 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
}
}
/// Extracts the first `lang = "$name"` out of a list of attributes.
/// The `#[panic_handler]` attribute is also extracted out when found.
///
/// This function is used for `ast::Attribute`, for `hir::Attribute` use the `find_attr!` macro with `AttributeKind::Lang`
pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> {
attrs.iter().find_map(|attr| {
Some(match attr {
_ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
_ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
_ => return None,
})
})
}
pub(crate) fn provide(providers: &mut Providers) {
providers.get_lang_items = get_lang_items;
}

View file

@ -13,6 +13,7 @@ use rustc_target::spec::Os;
use crate::errors::{
MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd, UnknownExternLangItem,
};
use crate::lang_items::extract_ast;
/// Checks the crate for usage of weak lang items, returning a vector of all the
/// lang items required by this crate, but not defined yet.
@ -46,7 +47,7 @@ struct WeakLangItemVisitor<'a, 'tcx> {
impl<'ast> visit::Visitor<'ast> for WeakLangItemVisitor<'_, '_> {
fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) {
if let Some((lang_item, _)) = lang_items::extract(&i.attrs) {
if let Some((lang_item, _)) = extract_ast(&i.attrs) {
if let Some(item) = LangItem::from_name(lang_item)
&& item.is_weak()
{

View file

@ -1,8 +1,8 @@
#![feature(lang_items)]
extern "C" {
#[lang = "cake"]
fn cake(); //~ ERROR E0264
#[lang = "copy"]
fn copy(); //~ ERROR E0264
}
fn main() {}

View file

@ -1,7 +1,7 @@
error[E0264]: unknown external lang item: `cake`
error[E0264]: unknown external lang item: `copy`
--> $DIR/E0264.rs:5:5
|
LL | fn cake();
LL | fn copy();
| ^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -1,4 +1,4 @@
//@ run-pass
//@ check-fail
// Test that this doesn't abort during AST lowering. In #96847 it did abort
// because the attribute was being lowered twice.
@ -9,6 +9,7 @@
fn main() {
for _ in [1,2,3] {
#![lang="foo"]
//~^ ERROR definition of an unknown lang item: `foo` [E0522]
println!("foo");
}
}

View file

@ -0,0 +1,9 @@
error[E0522]: definition of an unknown lang item: `foo`
--> $DIR/issue-96847.rs:11:9
|
LL | #![lang="foo"]
| ^^^^^^^^^^^^^^ definition of unknown lang item `foo`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0522`.