diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index ef0d81b2e0109..e1c15becc4d53 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -582,7 +582,7 @@ impl SingleAttributeParser for LangParser { // Only weak lang items may be applied to foreign items if [Target::ForeignFn, Target::ForeignStatic, Target::ForeignTy, Target::ForeignMod] .contains(&cx.target) - && !(lang_item.is_weak() || lang_item.is_weak_only()) + && !lang_item.is_weak() { cx.emit_err(UnknownExternLangItem { span: cx.attr_span, lang_item: lang_item.name() }); return None; @@ -1145,3 +1145,18 @@ impl NoArgsAttributeParser for RustcExhaustiveParser { const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcMustMatchExhaustively; } + +pub(crate) struct RustcCanonicalSymbolParser; + +impl NoArgsAttributeParser for RustcCanonicalSymbolParser { + const PATH: &[Symbol] = &[sym::rustc_canonical_symbol]; + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const STABILITY: AttributeStability = unstable!( + rustc_attrs, + "the `#[rustc_canonical_symbol]` attribute registers a function's symbol to be linted against \ + by the `invalid_runtime_symbol_definitions` and `suspicious_runtime_symbol_definitions` \ + lints" + ); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCanonicalSymbol; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 7ae275b940bd0..9e7522bc4e0e8 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -275,6 +275,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index cd01c29d40ac2..c7e9a939f2ba1 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -354,6 +354,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_has_incoherent_inherent_impls, sym::rustc_non_const_trait_method, + sym::rustc_canonical_symbol, sym::rustc_diagnostic_item, sym::prelude_import, sym::rustc_paren_sugar, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 765954d3c7369..9cf2be337fccb 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1372,6 +1372,10 @@ pub enum AttributeKind { builtin_name: Option, helper_attrs: ThinVec, }, + + /// Represents `#[rustc_canonical_symbol]` + RustcCanonicalSymbol, + /// Represents `#[rustc_capture_analysis]` RustcCaptureAnalysis, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index e36602a2e3c73..f703aebdbc6f1 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -110,6 +110,7 @@ impl AttributeKind { RustcAutodiff(..) => Yes, RustcBodyStability { .. } => No, RustcBuiltinMacro { .. } => Yes, + RustcCanonicalSymbol => No, RustcCaptureAnalysis => No, RustcCguTestAttr { .. } => No, RustcClean { .. } => No, diff --git a/compiler/rustc_hir/src/canonical_symbols.rs b/compiler/rustc_hir/src/canonical_symbols.rs new file mode 100644 index 0000000000000..83d299c5dd52a --- /dev/null +++ b/compiler/rustc_hir/src/canonical_symbols.rs @@ -0,0 +1,41 @@ +use rustc_data_structures::fx::FxIndexMap; +use rustc_macros::{Encodable, StableHash}; +use rustc_span::Symbol; +use rustc_span::def_id::DefId; + +/// A representation of a canonical symbol +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, StableHash)] +pub struct CanonicalSymbol { + pub def_id: DefId, + pub symbol: Symbol, +} + +#[derive(StableHash, Debug)] +pub struct CanonicalSymbols { + symbols: FxIndexMap, +} + +impl CanonicalSymbols { + /// Construct an empty collection of canonical symbols + pub fn new() -> Self { + Self { symbols: FxIndexMap::default() } + } + + pub fn get(&self, symbol: Symbol) -> Option { + self.symbols.get(&symbol).copied() + } + + pub fn set(&mut self, symbol: Symbol, def_id: DefId) -> Option { + let preexisting = self.symbols.insert(symbol, CanonicalSymbol { def_id, symbol }); + + if let Some(preexisting) = preexisting { + (preexisting.def_id != def_id).then_some(preexisting.def_id) + } else { + None + } + } + + pub fn iter(&self) -> impl Iterator { + self.symbols.values().copied() + } +} diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0ab9782c69356..1592dfdde4e6f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -453,22 +453,6 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); - - // Runtime symbols - MemCpy, sym::memcpy_fn, memcpy_fn, Target::ForeignFn, GenericRequirement::None; - MemMove, sym::memmove_fn, memmove_fn, Target::ForeignFn, GenericRequirement::None; - MemSet, sym::memset_fn, memset_fn, Target::ForeignFn, GenericRequirement::None; - MemCmp, sym::memcmp_fn, memcmp_fn, Target::ForeignFn, GenericRequirement::None; - Bcmp, sym::bcmp_fn, bcmp_fn, Target::ForeignFn, GenericRequirement::None; - StrLen, sym::strlen_fn, strlen_fn, Target::ForeignFn, GenericRequirement::None; - Open, sym::open_fn, open_fn, Target::ForeignFn, GenericRequirement::None; - Read, sym::read_fn, read_fn, Target::ForeignFn, GenericRequirement::None; - Write, sym::write_fn, write_fn, Target::ForeignFn, GenericRequirement::None; - Close, sym::close_fn, close_fn, Target::ForeignFn, GenericRequirement::None; - Malloc, sym::malloc_fn, malloc_fn, Target::ForeignFn, GenericRequirement::None; - Realloc, sym::realloc_fn, realloc_fn, Target::ForeignFn, GenericRequirement::None; - Free, sym::free_fn, free_fn, Target::ForeignFn, GenericRequirement::None; - Exit, sym::exit_fn, exit_fn, Target::ForeignFn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 7c2bf3c5b2797..c1e098b7b7f55 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -19,6 +19,7 @@ extern crate self as rustc_hir; mod arena; pub mod attrs; +pub mod canonical_symbols; pub mod def; pub mod def_path_hash_map; pub mod definitions; @@ -39,6 +40,7 @@ pub mod weak_lang_items; #[cfg(test)] mod tests; +pub use canonical_symbols::{CanonicalSymbol, CanonicalSymbols}; #[doc(no_inline)] pub use hir::*; pub use lang_items::{LangItem, LanguageItems}; diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index be3e2424d1782..85b0e3958c2d6 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -23,34 +23,7 @@ macro_rules! weak_lang_items { } } -macro_rules! weak_only_lang_items { - ($($item:ident,)*) => { - impl LangItem { - pub fn is_weak_only(self) -> bool { - matches!(self, $(LangItem::$item)|*) - } - } - } -} - weak_lang_items! { PanicImpl, rust_begin_unwind; EhPersonality, rust_eh_personality; } - -weak_only_lang_items! { - MemCpy, - MemMove, - MemSet, - MemCmp, - Bcmp, - StrLen, - Open, - Read, - Write, - Close, - Malloc, - Realloc, - Free, - Exit, -} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 04c89a5dd8c15..f17f75dc9d565 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1244,6 +1244,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { // diagnostic item. If the crate compiles without checking any diagnostic items, // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally. let _ = tcx.all_diagnostic_items(()); + + // This query is only invoked normally if a diagnostic is emitted that needs any + // canonical symbol. If the crate compiles without checking any runtime symbols, + // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally. + let _ = tcx.all_canonical_symbols(()); }); // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs index cfed2997c527e..a8fbddb693bab 100644 --- a/compiler/rustc_lint/src/runtime_symbols.rs +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -1,9 +1,9 @@ -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, FnSig, ForeignItemKind, LanguageItems}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir, CanonicalSymbol, FnSig, ForeignItemKind}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::infer::TyCtxtInferExt; use crate::lints::RedefiningRuntimeSymbolsDiag; @@ -73,31 +73,6 @@ declare_lint! { declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS, SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]); -static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ - // `core` symbols - ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, - ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn }, - ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn }, - ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn }, - ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn }, - ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn }, - // POSIX symbols - ExpectedSymbol { symbol: "open", lang: LanguageItems::open_fn }, - ExpectedSymbol { symbol: "read", lang: LanguageItems::read_fn }, - ExpectedSymbol { symbol: "write", lang: LanguageItems::write_fn }, - ExpectedSymbol { symbol: "close", lang: LanguageItems::close_fn }, - ExpectedSymbol { symbol: "malloc", lang: LanguageItems::malloc_fn }, - ExpectedSymbol { symbol: "realloc", lang: LanguageItems::realloc_fn }, - ExpectedSymbol { symbol: "free", lang: LanguageItems::free_fn }, - ExpectedSymbol { symbol: "exit", lang: LanguageItems::exit_fn }, -]; - -#[derive(Copy, Clone, Debug)] -struct ExpectedSymbol { - symbol: &'static str, - lang: fn(&LanguageItems) -> Option, -} - impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { // Bail-out if the item is not a function/method or static. @@ -162,16 +137,14 @@ impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { } fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) { - let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + let s = Symbol::intern(symbol_name); + let Some(CanonicalSymbol { symbol: _, def_id: expected_def_id }) = + cx.tcx.all_canonical_symbols(()).iter().find(|cs| cs.symbol == s) + else { // The symbol name does not correspond to a runtime symbols, bail out return; }; - let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { - // Can't find the corresponding language item, bail out - return; - }; - // Get the two function signatures let lang_sig = cx.tcx.normalize_erasing_regions( cx.typing_env(), @@ -222,16 +195,14 @@ fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalD } fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) { - let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + let s = Symbol::intern(symbol_name); + let Some(CanonicalSymbol { symbol: _, def_id: expected_def_id }) = + cx.tcx.all_canonical_symbols(()).iter().find(|cs| cs.symbol == s) + else { // The symbol name does not correspond to a runtime symbols, bail out return; }; - let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { - // Can't find the corresponding language item, bail out - return; - }; - // Get the static type let static_ty = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 5719d6610b6bc..ef42822f026a9 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -15,11 +15,11 @@ use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; -use rustc_hir::Safety; use rustc_hir::def::Res; use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::diagnostic_items::DiagnosticItems; +use rustc_hir::{CanonicalSymbols, Safety}; use rustc_index::Idx; use rustc_middle::middle::lib_features::LibFeatures; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; @@ -1263,6 +1263,18 @@ impl CrateMetadata { DiagnosticItems { id_to_name, name_to_id } } + /// Iterates over the canonical_symbols in the given crate. + fn get_canonical_symbols(&self, tcx: TyCtxt<'_>) -> CanonicalSymbols { + let mut canonical_symbols = CanonicalSymbols::new(); + + for (name, def_index) in self.root.canonical_symbols.decode((self, tcx)) { + let id = self.local_def_id(def_index); + let _ = canonical_symbols.set(name, id); + } + + canonical_symbols + } + fn get_mod_child(&self, tcx: TyCtxt<'_>, id: DefIndex) -> ModChild { let ident = self.item_ident(tcx, id); let res = Res::Def(self.def_kind(id), self.local_def_id(id)); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 8e4c6e18804af..2a138154f7020 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -399,6 +399,7 @@ provide! { tcx, def_id, other, cdata, intrinsic_raw => { cdata.get_intrinsic(tcx, def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items(tcx) } + canonical_symbols => { cdata.get_canonical_symbols(tcx) } missing_lang_items => { cdata.get_missing_lang_items(tcx) } missing_extern_crate_item => { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 79c1463ae2c05..8fa0c1b2dcdd8 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -639,6 +639,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items()); + let canonical_symbols = stat!("canonical-symbols", || self.encode_canonical_symbols()); + let native_libraries = stat!("native-libs", || self.encode_native_libraries()); let foreign_modules = stat!("foreign-modules", || self.encode_foreign_modules()); @@ -757,6 +759,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { stability_implications, lang_items, diagnostic_items, + canonical_symbols, lang_items_missing, stripped_cfg_items, native_libraries, @@ -2134,6 +2137,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(sorted.into_iter().map(|(k, v)| (*k, *v))) } + fn encode_canonical_symbols(&mut self) -> LazyArray<(Symbol, DefIndex)> { + empty_proc_macro!(self); + let tcx = self.tcx; + let canonical_symbols = &tcx.canonical_symbols(LOCAL_CRATE); + self.lazy_array(canonical_symbols.iter().map(|cs| (cs.symbol, cs.def_id.index))) + } + fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> { empty_proc_macro!(self); let tcx = self.tcx; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 05d8c8e9077e3..4847ddda90fd1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -269,6 +269,7 @@ pub(crate) struct CrateRoot { lang_items_missing: LazyArray, stripped_cfg_items: LazyArray>, diagnostic_items: LazyArray<(Symbol, DefIndex)>, + canonical_symbols: LazyArray<(Symbol, DefIndex)>, native_libraries: LazyArray, foreign_modules: LazyArray, traits: LazyArray, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 632483c55d1ff..cbd54ec959c6a 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -63,6 +63,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{ErrorGuaranteed, catch_fatal_errors}; use rustc_hir as hir; use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; +use rustc_hir::canonical_symbols::CanonicalSymbols; use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdSet, LocalModId}; use rustc_hir::lang_items::{LangItem, LanguageItems}; @@ -2262,6 +2263,13 @@ rustc_queries! { desc { "calculating the diagnostic items map" } } + /// Returns all the canonical symbols defined in all crates. + query all_canonical_symbols(_: ()) -> &'tcx CanonicalSymbols { + arena_cache + eval_always + desc { "calculating the canonical symbols map" } + } + /// Returns the lang items defined in another crate by loading it from metadata. query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, LangItem)] { desc { "calculating the lang items defined in a crate" } @@ -2275,6 +2283,13 @@ rustc_queries! { separate_provide_extern } + /// Returns the canonical symbols defined in a crate. + query canonical_symbols(_: CrateNum) -> &'tcx CanonicalSymbols { + arena_cache + desc { "calculating the canonical symbols map in a crate" } + separate_provide_extern + } + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { desc { "calculating the missing lang items in a crate" } separate_provide_extern diff --git a/compiler/rustc_passes/src/canonical_symbols.rs b/compiler/rustc_passes/src/canonical_symbols.rs new file mode 100644 index 0000000000000..746758d3e36f2 --- /dev/null +++ b/compiler/rustc_passes/src/canonical_symbols.rs @@ -0,0 +1,87 @@ +use rustc_hir::{CanonicalSymbols, ForeignItemId, find_attr}; +use rustc_middle::query::{LocalCrate, Providers}; +use rustc_middle::ty::{Instance, List, TyCtxt}; +use rustc_span::Symbol; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; + +use crate::diagnostics::DuplicateCanonicalSymbolInCrate; + +fn observe_item<'tcx>( + tcx: TyCtxt<'tcx>, + canonical_symbols: &mut CanonicalSymbols, + fid: ForeignItemId, +) { + let attrs = tcx.hir_attrs(fid.owner_id.into()); + if find_attr!(attrs, RustcCanonicalSymbol) { + let did = fid.owner_id.def_id; + let instance = Instance::new_raw(did.to_def_id(), List::identity_for_item(tcx, did)); + let symbol_name = tcx.symbol_name(instance); + let symbol_name = Symbol::intern(symbol_name.name); + + // insert into our table + collect_item(tcx, canonical_symbols, symbol_name, fid.owner_id.to_def_id()); + } +} + +fn collect_item( + tcx: TyCtxt<'_>, + canonical_symbols: &mut CanonicalSymbols, + symbol: Symbol, + item_def_id: DefId, +) { + if let Some(original_def_id) = canonical_symbols.set(symbol, item_def_id) { + report_duplicate_item(tcx, symbol, original_def_id, item_def_id); + } +} + +fn report_duplicate_item( + tcx: TyCtxt<'_>, + name: Symbol, + original_def_id: DefId, + item_def_id: DefId, +) { + let orig_span = tcx.hir_span_if_local(original_def_id); + let duplicate_span = tcx.hir_span_if_local(item_def_id); + tcx.dcx().emit_err(DuplicateCanonicalSymbolInCrate { + duplicate_span, + orig_span, + crate_name: tcx.crate_name(item_def_id.krate), + orig_crate_name: tcx.crate_name(original_def_id.krate), + different_crates: (item_def_id.krate != original_def_id.krate), + name, + }); +} + +/// Traverse and collect the canonical symbols in the current crate +fn canonical_symbols(tcx: TyCtxt<'_>, _: LocalCrate) -> CanonicalSymbols { + // Initialize the collector. + let mut canonical_symbols = CanonicalSymbols::new(); + + // Collect canonical symbols in this crate. + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.foreign_items() { + observe_item(tcx, &mut canonical_symbols, id); + } + + canonical_symbols +} + +/// Traverse and collect all the canonical symbols in all crates. +fn all_canonical_symbols(tcx: TyCtxt<'_>, (): ()) -> CanonicalSymbols { + // Initialize the collector. + let mut items = CanonicalSymbols::new(); + + // Collect all canonical symbols + for cnum in tcx.crates(()).iter().copied().chain(std::iter::once(LOCAL_CRATE)) { + for cs in tcx.canonical_symbols(cnum).iter() { + collect_item(tcx, &mut items, cs.symbol, cs.def_id); + } + } + + items +} + +pub(crate) fn provide(providers: &mut Providers) { + providers.canonical_symbols = canonical_symbols; + providers.all_canonical_symbols = all_canonical_symbols; +} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3139c8746b95a..1bf54a5e547cf 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -320,6 +320,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcAutodiff(..) => (), AttributeKind::RustcBodyStability { .. } => (), AttributeKind::RustcBuiltinMacro { .. } => (), + AttributeKind::RustcCanonicalSymbol => (), AttributeKind::RustcCaptureAnalysis => (), AttributeKind::RustcCguTestAttr(..) => (), AttributeKind::RustcClean(..) => (), diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index dd5247823231d..9b3a4099b8bee 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -788,7 +788,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) || find_attr!(tcx, def_id, Lang(..)) { + } else if has_used_like_attr(tcx, def_id) + || find_attr!(tcx, def_id, Lang(..) | RustcCanonicalSymbol) + { Some(ComesFromAllowExpect::No) } else { None diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 2589941d7dae1..b692c6d5a6826 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -376,6 +376,20 @@ pub(crate) struct DuplicateDiagnosticItemInCrate { pub name: Symbol, } +#[derive(Diagnostic)] +#[diag("duplicate canonical symbol in crate `{$crate_name}`: `{$name}`")] +pub(crate) struct DuplicateCanonicalSymbolInCrate { + #[primary_span] + pub duplicate_span: Option, + #[note("the canonical symbol is first defined here")] + pub orig_span: Option, + #[note("the canonical symbol is first defined in crate `{$orig_crate_name}`")] + pub different_crates: bool, + pub crate_name: Symbol, + pub orig_crate_name: Symbol, + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("fn_abi_of({$fn_name}) = {$fn_abi}")] pub(crate) struct AbiOf { diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 66cc83b41ac3a..90070fe42e026 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -11,6 +11,7 @@ use rustc_middle::query::Providers; pub mod abi_test; +mod canonical_symbols; mod check_attr; mod check_export; pub mod dead; @@ -31,6 +32,7 @@ mod upvars; mod weak_lang_items; pub fn provide(providers: &mut Providers) { + canonical_symbols::provide(providers); check_attr::provide(providers); dead::provide(providers); debugger_visualizer::provide(providers); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 287b790ea6d67..f3c2abbf5284f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,7 +515,6 @@ symbols! { await_macro, backchain, backend_repr, - bcmp_fn, begin_panic, bench, bevy_ecs, @@ -901,7 +900,6 @@ symbols! { exhaustive_integer_patterns, exhaustive_patterns, existential_type, - exit_fn, exp2f16, exp2f32, exp2f64, @@ -1013,7 +1011,6 @@ symbols! { format_unsafe_arg, fp, framework, - free_fn, freeze, freeze_impls, freg, @@ -1256,7 +1253,6 @@ symbols! { macro_vis_matcher, macros_in_extern, main, - malloc_fn, managed_boxes, manually_drop, map, @@ -1291,11 +1287,7 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, - memcmp_fn, - memcpy_fn, - memmove_fn, memory, - memset_fn, memtag, message, meta, @@ -1482,7 +1474,6 @@ symbols! { on_unmatched_args, opaque, opaque_module_name_placeholder: "", - open_fn, ops, opt_out_copy, optimize, @@ -1667,11 +1658,9 @@ symbols! { raw_identifiers, raw_ref_op, re_rebalance_coherence, - read_fn, read_via_copy, readonly, realloc, - realloc_fn, realtime, reason, reborrow, @@ -1770,6 +1759,7 @@ symbols! { rustc_attrs, rustc_autodiff, rustc_builtin_macro, + rustc_canonical_symbol, rustc_capture_analysis, rustc_clean, rustc_coherence_is_core, @@ -2060,7 +2050,6 @@ symbols! { strict_provenance_lints, string_deref_patterns, stringify, - strlen_fn, struct_field_attributes, struct_inherit, struct_variant, @@ -2378,7 +2367,6 @@ symbols! { write_box_via_move, write_bytes, write_fmt, - write_fn, write_macro, write_str, write_via_move, diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index df6396b84264d..88696fe02da9d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -93,22 +93,22 @@ mod runtime_symbols { use crate::ffi::{c_char, c_int, c_void}; unsafe extern "C" { - #[lang = "memcpy_fn"] + #[rustc_canonical_symbol] fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; - #[lang = "memmove_fn"] + #[rustc_canonical_symbol] fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; - #[lang = "memset_fn"] + #[rustc_canonical_symbol] fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; - #[lang = "memcmp_fn"] + #[rustc_canonical_symbol] fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; - #[lang = "bcmp_fn"] + #[rustc_canonical_symbol] fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; - #[lang = "strlen_fn"] + #[rustc_canonical_symbol] fn strlen(s: *const c_char) -> usize; } } diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index b9c5dfd24396f..2411ca0a48c48 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -10,13 +10,13 @@ mod runtime_symbols { use core::ffi::{c_size_t, c_void}; unsafe extern "C" { - #[lang = "malloc_fn"] + #[rustc_canonical_symbol] fn malloc(size: c_size_t) -> *mut c_void; - #[lang = "realloc_fn"] + #[rustc_canonical_symbol] fn realloc(ptr: *mut c_void, size: c_size_t) -> *mut c_void; - #[lang = "free_fn"] + #[rustc_canonical_symbol] fn free(ptr: *mut c_void); } } diff --git a/library/std/src/sys/exit.rs b/library/std/src/sys/exit.rs index c4fa3af803071..d0c67fbf6cbd7 100644 --- a/library/std/src/sys/exit.rs +++ b/library/std/src/sys/exit.rs @@ -79,7 +79,7 @@ cfg_select! { // See the `invalid_runtime_symbols_definitions` lint. mod runtime_symbols { unsafe extern "C" { - #[lang = "exit_fn"] + #[rustc_canonical_symbol] fn exit(status: core::ffi::c_int) -> !; } } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index cadbf1a21aa8f..d39dd4c0910ca 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -110,16 +110,16 @@ mod runtime_symbols { use core::ffi::{c_char, c_int, c_size_t, c_ssize_t, c_void}; unsafe extern "C" { - #[lang = "open_fn"] + #[rustc_canonical_symbol] fn open(pathname: *const c_char, flags: c_int, ...) -> c_int; - #[lang = "read_fn"] + #[rustc_canonical_symbol] fn read(fd: c_int, buf: *mut c_void, count: c_size_t) -> c_ssize_t; - #[lang = "write_fn"] + #[rustc_canonical_symbol] fn write(fd: c_int, buf: *const c_void, count: c_size_t) -> c_ssize_t; - #[lang = "close_fn"] + #[rustc_canonical_symbol] fn close(fd: c_int) -> c_int; } } diff --git a/tests/ui/tool-attributes/auxiliary/symbols.rs b/tests/ui/tool-attributes/auxiliary/symbols.rs new file mode 100644 index 0000000000000..8dac3bf9a524f --- /dev/null +++ b/tests/ui/tool-attributes/auxiliary/symbols.rs @@ -0,0 +1,6 @@ +#![feature(rustc_attrs)] + +extern "C" { + #[rustc_canonical_symbol] + fn foo(); +} diff --git a/tests/ui/tool-attributes/duplicate-canonical-symbols.rs b/tests/ui/tool-attributes/duplicate-canonical-symbols.rs new file mode 100644 index 0000000000000..edef5273c5a65 --- /dev/null +++ b/tests/ui/tool-attributes/duplicate-canonical-symbols.rs @@ -0,0 +1,21 @@ +//@ aux-build: symbols.rs + +#![feature(rustc_attrs)] + +extern crate symbols; + +extern "C" { + #[rustc_canonical_symbol] + fn foo(); +} + +mod bar { + extern "C" { + #[rustc_canonical_symbol] + fn foo(); + //~^ ERROR duplicate canonical symbol + //~^^ ERROR duplicate canonical symbol + } +} + +fn main() {} diff --git a/tests/ui/tool-attributes/duplicate-canonical-symbols.stderr b/tests/ui/tool-attributes/duplicate-canonical-symbols.stderr new file mode 100644 index 0000000000000..ccabf779411d8 --- /dev/null +++ b/tests/ui/tool-attributes/duplicate-canonical-symbols.stderr @@ -0,0 +1,22 @@ +error: duplicate canonical symbol in crate `duplicate_canonical_symbols`: `foo` + --> $DIR/duplicate-canonical-symbols.rs:15:9 + | +LL | fn foo(); + | ^^^^^^^^^ + | +note: the canonical symbol is first defined here + --> $DIR/duplicate-canonical-symbols.rs:9:5 + | +LL | fn foo(); + | ^^^^^^^^^ + +error: duplicate canonical symbol in crate `duplicate_canonical_symbols`: `foo` + --> $DIR/duplicate-canonical-symbols.rs:15:9 + | +LL | fn foo(); + | ^^^^^^^^^ + | + = note: the canonical symbol is first defined in crate `symbols` + +error: aborting due to 2 previous errors +