diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index f1b31365013ec..e7b639c9af187 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -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 SingleAttributeParser for RustcScalableVectorParser { } } +pub(crate) struct LangParser; + +impl SingleAttributeParser for LangParser { + const PATH: &[Symbol] = &[sym::lang]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = 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 { + 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 NoArgsAttributeParser for RustcHasIncoherentInherentImplsParser { @@ -641,6 +668,15 @@ impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParse const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls; } +pub(crate) struct PanicHandlerParser; + +impl NoArgsAttributeParser for PanicHandlerParser { + const PATH: &[Symbol] = &[sym::panic_handler]; + const ON_DUPLICATE: OnDuplicate = 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 NoArgsAttributeParser for RustcHiddenTypeOfOpaquesParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index c6f0914bfbdaf..93a2b819c51f5 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -181,6 +181,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, @@ -253,6 +254,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 2eb585671fff9..e98969dda300b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -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> { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 9ecb7ab9fd457..edd73f418036b 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -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 diff --git a/compiler/rustc_error_codes/src/error_codes/E0264.md b/compiler/rustc_error_codes/src/error_codes/E0264.md index 33ddf3405acca..b8d0f7d4d6af0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0264.md +++ b/compiler/rustc_error_codes/src/error_codes/E0264.md @@ -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(); } ``` diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index e28ecd06b89bc..9ed4856e3a2d2 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -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, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index b4cf244bfb8aa..b656cc26bd8ed 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -50,6 +50,7 @@ impl AttributeKind { Ignore { .. } => No, Inline(..) => No, InstructionSet(..) => No, + Lang(..) => Yes, Link(..) => No, LinkName { .. } => Yes, // Needed for rustdoc LinkOrdinal { .. } => No, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..0325fd2ceab94 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -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 HashStable 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); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 08f2597e874d8..e7d546874c834 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -256,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Fundamental | AttributeKind::Ignore { .. } | AttributeKind::InstructionSet(..) + | AttributeKind::Lang(..) | AttributeKind::LinkName { .. } | AttributeKind::LinkOrdinal { .. } | AttributeKind::LinkSection { .. } @@ -395,8 +396,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // need to be fixed | sym::deprecated_safe // FIXME(deprecated_safe) // internal - | sym::panic_handler - | sym::lang | sym::default_lib_allocator | sym::rustc_diagnostic_item | sym::rustc_nonnull_optimization_guaranteed @@ -786,15 +785,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, }); } @@ -858,7 +856,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 @@ -868,7 +866,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::LangItemWithTargetFeature { attr_span, - name: lang_item, + name: lang_item.name(), sig_span: sig.span, }); } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 75bac9eff2c71..e56d27721bddd 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -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 { - 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 diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f420bba9b4e9c..630a397813095 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -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, diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index b9417af13b113..25aea8e9f82a0 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -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; } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 811b5e9300141..4200003ea1d1a 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -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() { diff --git a/tests/ui/error-codes/E0264.rs b/tests/ui/error-codes/E0264.rs index 6adaf01fb5244..855644796ed45 100644 --- a/tests/ui/error-codes/E0264.rs +++ b/tests/ui/error-codes/E0264.rs @@ -1,8 +1,8 @@ #![feature(lang_items)] extern "C" { - #[lang = "cake"] - fn cake(); //~ ERROR E0264 + #[lang = "copy"] + fn copy(); //~ ERROR E0264 } fn main() {} diff --git a/tests/ui/error-codes/E0264.stderr b/tests/ui/error-codes/E0264.stderr index 3503fb229e4f3..6442f42e689d6 100644 --- a/tests/ui/error-codes/E0264.stderr +++ b/tests/ui/error-codes/E0264.stderr @@ -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 diff --git a/tests/ui/lowering/issue-96847.rs b/tests/ui/lowering/issue-96847.rs index 9408f6b9b4abc..a1fd105d9dd4a 100644 --- a/tests/ui/lowering/issue-96847.rs +++ b/tests/ui/lowering/issue-96847.rs @@ -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"); } } diff --git a/tests/ui/lowering/issue-96847.stderr b/tests/ui/lowering/issue-96847.stderr new file mode 100644 index 0000000000000..2cded32f9fb86 --- /dev/null +++ b/tests/ui/lowering/issue-96847.stderr @@ -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`.