From 5156c51ebd2cce431703b2a1c6c783062d4d7279 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 9 Jul 2026 10:28:02 +0200 Subject: [PATCH] Move lang item collection to the resolver --- Cargo.lock | 2 +- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_middle/src/middle/resolve.rs | 3 +- compiler/rustc_middle/src/queries.rs | 1 - compiler/rustc_passes/Cargo.toml | 1 - compiler/rustc_passes/src/diagnostics.rs | 131 ------- compiler/rustc_passes/src/lang_items.rs | 332 ------------------ compiler/rustc_passes/src/lib.rs | 3 - compiler/rustc_resolve/Cargo.toml | 1 + compiler/rustc_resolve/src/diagnostics/mod.rs | 133 ++++++- compiler/rustc_resolve/src/late.rs | 44 +++ compiler/rustc_resolve/src/late/lang_items.rs | 256 ++++++++++++++ .../src/late}/weak_lang_items.rs | 0 compiler/rustc_resolve/src/lib.rs | 19 +- tests/ui/lang-items/issue-83471.stderr | 18 +- tests/ui/panic-handler/weak-lang-item.rs | 2 + tests/ui/panic-handler/weak-lang-item.stderr | 11 +- 17 files changed, 475 insertions(+), 483 deletions(-) delete mode 100644 compiler/rustc_passes/src/lang_items.rs create mode 100644 compiler/rustc_resolve/src/late/lang_items.rs rename compiler/{rustc_passes/src => rustc_resolve/src/late}/weak_lang_items.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 5ff455c57f056..2dace16bea1f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4662,7 +4662,6 @@ dependencies = [ "rustc_ast_lowering", "rustc_attr_ir", "rustc_attr_parsing", - "rustc_crate_store", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4787,6 +4786,7 @@ dependencies = [ "rustc_ast_pretty", "rustc_attr_ir", "rustc_attr_parsing", + "rustc_crate_store", "rustc_data_structures", "rustc_errors", "rustc_expand", diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 73bebb22bc508..978eddc5e143d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -900,6 +900,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { providers.queries.analysis = analysis; providers.queries.resolver_for_lowering_raw = resolver_for_lowering_raw; providers.queries.stripped_cfg_items = |tcx, _| &tcx.resolutions(()).stripped_cfg_items[..]; + providers.queries.get_lang_items = |tcx, ()| &tcx.resolutions(()).lang_items; providers.queries.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).2; providers.queries.early_lint_checks = early_lint_checks; providers.queries.env_var_os = env_var_os; diff --git a/compiler/rustc_middle/src/middle/resolve.rs b/compiler/rustc_middle/src/middle/resolve.rs index 1c92f0ee2a8b6..54357dd4fff12 100644 --- a/compiler/rustc_middle/src/middle/resolve.rs +++ b/compiler/rustc_middle/src/middle/resolve.rs @@ -3,7 +3,7 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, NodeId}; -use rustc_attr_ir::StrippedCfgItem; +use rustc_attr_ir::{LanguageItems, StrippedCfgItem}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -197,6 +197,7 @@ pub struct ResolverGlobalCtxt { pub delegation_infos: FxIndexMap, pub delegation_inherent_fn_map: FxIndexMap>, + pub lang_items: LanguageItems, } #[derive(Debug)] diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 56889f84f38d6..90a5e03d3a21f 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2295,7 +2295,6 @@ rustc_queries! { /// Returns the lang items defined in all crates by loading them from metadata of dependencies /// and collecting the ones from the current crate. query get_lang_items(_: ()) -> &'tcx LanguageItems { - arena_cache eval_always desc { "calculating the lang items map" } } diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 5e2fa8647671d..827e2b1f1bbc9 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -10,7 +10,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } -rustc_crate_store = { path = "../rustc_crate_store" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index e090fd0669cd8..5dc29956ff255 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -9,7 +9,6 @@ use rustc_middle::ty::Ty; use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use crate::check_attr::ProcMacroKind; -use crate::lang_items::Duplicate; #[derive(Diagnostic)] #[diag("`{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`")] @@ -180,30 +179,6 @@ pub(crate) struct InvalidMayDangle { pub attr_span: Span, } -#[derive(Diagnostic)] -#[diag("`#[panic_handler]` function required, but not found")] -pub(crate) struct MissingPanicHandler; - -#[derive(Diagnostic)] -#[diag("unwinding panics are not supported without std")] -#[help("using nightly cargo, use -Zbuild-std with panic=\"abort\" to avoid unwinding")] -#[note( - "since the core library is usually precompiled with panic=\"unwind\", rebuilding your crate with panic=\"abort\" may not be enough to fix the problem" -)] -pub(crate) struct PanicUnwindWithoutStd; - -#[derive(Diagnostic)] -#[diag("lang item required, but not found: `{$name}`")] -#[note( - "this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library" -)] -#[help( - "you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`" -)] -pub(crate) struct MissingLangItem { - pub name: Symbol, -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { @@ -370,112 +345,6 @@ impl<'a> Diagnostic<'a> for NoMainErr { diag } } -pub(crate) struct DuplicateLangItem { - pub local_span: Option, - pub lang_item_name: Symbol, - pub crate_name: Symbol, - pub dependency_of: Option, - pub is_local: bool, - pub path: String, - pub first_defined_span: Option, - pub orig_crate_name: Option, - pub orig_dependency_of: Option, - pub orig_is_local: bool, - pub orig_path: String, - pub(crate) duplicate: Duplicate, -} - -impl Diagnostic<'_> for DuplicateLangItem { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { - let mut diag = Diag::new( - dcx, - level, - match self.duplicate { - Duplicate::Plain => msg!("found duplicate lang item `{$lang_item_name}`"), - Duplicate::Crate => { - msg!("duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`") - } - Duplicate::CrateDepends => msg!( - "duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`" - ), - }, - ); - diag.code(E0152); - diag.arg("lang_item_name", self.lang_item_name); - diag.arg("crate_name", self.crate_name); - if let Some(dependency_of) = self.dependency_of { - diag.arg("dependency_of", dependency_of); - } - diag.arg("path", self.path); - if let Some(orig_crate_name) = self.orig_crate_name { - diag.arg("orig_crate_name", orig_crate_name); - } - if let Some(orig_dependency_of) = self.orig_dependency_of { - diag.arg("orig_dependency_of", orig_dependency_of); - } - diag.arg("orig_path", self.orig_path); - if let Some(span) = self.local_span { - diag.span(span); - } - if let Some(span) = self.first_defined_span { - diag.span_note(span, msg!("the lang item is first defined here")); - } else { - if self.orig_dependency_of.is_none() { - diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}`")); - } else { - diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)")); - } - - if self.orig_is_local { - diag.note(msg!("first definition in the local crate (`{$orig_crate_name}`)")); - } else { - diag.note(msg!( - "first definition in `{$orig_crate_name}` loaded from {$orig_path}" - )); - } - - if self.is_local { - diag.note(msg!("second definition in the local crate (`{$crate_name}`)")); - } else { - diag.note(msg!("second definition in `{$crate_name}` loaded from {$path}")); - } - } - diag - } -} - -#[derive(Diagnostic)] -#[diag("`{$name}` lang item must be applied to a {$kind} with {$at_least -> - [true] at least {$num} - *[false] {$num} - } generic {$num -> - [one] argument - *[other] arguments - }", code = E0718)] -pub(crate) struct IncorrectTarget<'a> { - #[primary_span] - pub span: Span, - #[label( - "this {$kind} has {$actual_num} generic {$actual_num -> - [one] argument - *[other] arguments - }" - )] - pub generics_span: Span, - pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` - pub kind: &'static str, - pub num: usize, - pub actual_num: usize, - pub at_least: bool, -} - -#[derive(Diagnostic)] -#[diag("lang items are not allowed in stable dylibs")] -pub(crate) struct IncorrectCrateType { - #[primary_span] - pub span: Span, -} #[derive(Diagnostic)] #[diag( diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs deleted file mode 100644 index ab7b0f375e609..0000000000000 --- a/compiler/rustc_passes/src/lang_items.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Detecting lang items. -//! -//! Language items are items that represent concepts intrinsic to the language -//! itself. Examples are: -//! -//! * Traits that specify "kinds"; e.g., `Sync`, `Send`. -//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. -//! * Functions called by the compiler itself. - -use rustc_ast as ast; -use rustc_ast::visit; -use rustc_attr_ir::lang_items::{GenericRequirement, LangItem, LanguageItems}; -use rustc_attr_ir::target::Target; -use rustc_crate_store::ExternCrate; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::middle::resolve::ResolverAstLowering; -use rustc_middle::query::Providers; -use rustc_middle::ty::TyCtxt; -use rustc_span::{Span, Symbol, sym}; - -use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget}; -use crate::weak_lang_items; - -pub(crate) enum Duplicate { - Plain, - Crate, - CrateDepends, -} - -struct LanguageItemCollector<'ast, 'tcx> { - items: LanguageItems, - tcx: TyCtxt<'tcx>, - resolver: &'ast ResolverAstLowering<'tcx>, - parent_item: Option<&'ast ast::Item>, -} - -impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - resolver: &'ast ResolverAstLowering<'tcx>, - ) -> LanguageItemCollector<'ast, 'tcx> { - LanguageItemCollector { tcx, resolver, items: LanguageItems::new(), parent_item: None } - } - - fn check_for_lang( - &mut self, - actual_target: Target, - def_id: LocalDefId, - attrs: &'ast [ast::Attribute], - item_span: Span, - generics: Option<&'ast ast::Generics>, - ) { - if let Some((name, attr_span)) = extract_ast(attrs) { - match LangItem::from_name(name) { - // Known lang item - Some(lang_item) => { - if actual_target != lang_item.target() { - // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast - // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`. - if !(lang_item.is_weak() - && actual_target == Target::Fn - && lang_item.target() == Target::ForeignFn - && matches!(lang_item, LangItem::PanicImpl)) - { - self.tcx - .dcx() - .delayed_bug(format!("lang item target is checked in attribute parser: {:?} has {} but expected {}", def_id, actual_target, lang_item.target())); - return; - } - } - // Weak lang items are handled separately - if lang_item.is_weak() && actual_target == Target::ForeignFn { - self.items.missing.push(lang_item); - } else { - // Weak only lang items are always handled here - self.collect_item_extended( - lang_item, - def_id, - item_span, - attr_span, - generics, - actual_target, - ); - } - } - // Unknown lang item. - _ => { - self.tcx.dcx().delayed_bug("unknown lang item"); - } - } - } - } - - fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option) { - // Check for duplicates. - if let Some(original_def_id) = self.items.get(lang_item) - && original_def_id != item_def_id - { - let lang_item_name = lang_item.name(); - let crate_name = self.tcx.crate_name(item_def_id.krate); - let mut dependency_of = None; - let is_local = item_def_id.is_local(); - let path = if is_local { - String::new() - } else { - self.tcx - .crate_extern_paths(item_def_id.krate) - .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(", ") - }; - - let mut orig_crate_name = None; - let mut orig_dependency_of = None; - let orig_is_local = original_def_id.is_local(); - let orig_path = if orig_is_local { - String::new() - } else { - self.tcx - .crate_extern_paths(original_def_id.krate) - .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(", ") - }; - - if !original_def_id.is_local() { - orig_crate_name = Some(self.tcx.crate_name(original_def_id.krate)); - if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) = - self.tcx.extern_crate(original_def_id.krate) - { - orig_dependency_of = Some(self.tcx.crate_name(*inner_dependency_of)); - } - } - - let duplicate = if item_span.is_some() { - Duplicate::Plain - } else { - match self.tcx.extern_crate(item_def_id.krate) { - Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => { - dependency_of = Some(self.tcx.crate_name(*inner_dependency_of)); - Duplicate::CrateDepends - } - _ => Duplicate::Crate, - } - }; - - // When there's a duplicate lang item, something went very wrong and there's no value - // in recovering or doing anything. Give the user the one message to let them debug the - // mess they created and then wish them farewell. - self.tcx.dcx().emit_fatal(DuplicateLangItem { - local_span: item_span, - lang_item_name, - crate_name, - dependency_of, - is_local, - path, - first_defined_span: original_def_id.as_local().map(|did| self.tcx.source_span(did)), - orig_crate_name, - orig_dependency_of, - orig_is_local, - orig_path, - duplicate, - }); - } else { - // Matched. - self.items.set(lang_item, item_def_id); - } - } - - // Like collect_item() above, but also checks whether the lang item is declared - // with the right number of generic arguments. - fn collect_item_extended( - &mut self, - lang_item: LangItem, - item_def_id: LocalDefId, - item_span: Span, - attr_span: Span, - generics: Option<&'ast ast::Generics>, - target: Target, - ) { - let name = lang_item.name(); - - if let Some(generics) = generics { - // Now check whether the lang_item has the expected number of generic - // arguments. Generally speaking, binary and indexing operations have - // one (for the RHS/index), unary operations have none, the closure - // traits have one for the argument list, coroutines have one for the - // resume argument, and ordering/equality relations have one for the RHS - // Some other types like Box and various unsizing-related traits - // have minimum requirements. - - // FIXME: This still doesn't count, e.g., elided lifetimes and APITs. - let mut actual_num = generics.params.len(); - if target.is_associated_item() { - actual_num += self - .parent_item - .unwrap() - .opt_generics() - .map_or(0, |generics| generics.params.len()); - } - - let mut at_least = false; - let required = match lang_item.required_generics() { - GenericRequirement::Exact(num) if num != actual_num => Some(num), - GenericRequirement::Minimum(num) if actual_num < num => { - at_least = true; - Some(num) - } - // If the number matches, or there is no requirement, handle it normally - _ => None, - }; - - if let Some(num) = required { - // We are issuing E0718 "incorrect target" here, because while the - // item kind of the target is correct, the target is still wrong - // because of the wrong number of generic arguments. - self.tcx.dcx().emit_err(IncorrectTarget { - span: attr_span, - generics_span: generics.span, - name: name.as_str(), - kind: target.name(), - num, - actual_num, - at_least, - }); - - // return early to not collect the lang item - return; - } - } - - if self.tcx.crate_types().contains(&rustc_structures::CrateType::Sdylib) { - self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span }); - } - - self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span)); - } -} - -/// Traverses and collects all the lang items in all crates. -fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { - let (resolver, krate) = tcx.resolver_for_lowering(); - let resolver = &*resolver.borrow(); - let krate = &*krate.borrow(); - - // Initialize the collector. - let mut collector = LanguageItemCollector::new(tcx, resolver); - - // Collect lang items in other crates. - for &cnum in tcx.used_crates(()).iter() { - for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() { - collector.collect_item(lang_item, def_id, None); - } - } - - // Collect lang items local to this crate. - visit::Visitor::visit_crate(&mut collector, krate); - - // Find all required but not-yet-defined lang items. - weak_lang_items::check_crate(tcx, &mut collector.items); - - // Return all the lang items that were found. - collector.items -} - -impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { - fn visit_item(&mut self, i: &'ast ast::Item) { - let target = Target::from_ast_item(i); - - self.check_for_lang( - target, - self.resolver.owners[&i.id].def_id, - &i.attrs, - i.span, - i.opt_generics(), - ); - - let parent_item = self.parent_item.replace(i); - visit::walk_item(self, i); - self.parent_item = parent_item; - } - - fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { - self.check_for_lang( - Target::from_foreign_item_kind(&i.kind), - self.resolver.owners[&i.id].def_id, - &i.attrs, - i.span, - None, - ); - } - - fn visit_variant(&mut self, variant: &'ast ast::Variant) { - self.check_for_lang( - Target::Variant, - self.resolver.owners[&self.parent_item.unwrap().id].node_id_to_def_id[&variant.id], - &variant.attrs, - variant.span, - None, - ); - } - - fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) { - let target = Target::from_assoc_item_kind(&i.kind, ctxt); - let generics = i.opt_generics(); - - self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics); - - visit::walk_assoc_item(self, i, ctxt); - } -} - -/// 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 `rustc_attr_ir::Attribute` -/// use `find_attr!(attrs, Lang(lang_item))`. -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/lib.rs b/compiler/rustc_passes/src/lib.rs index 77bd179e8dd80..d37d1bea932fe 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -25,13 +25,11 @@ pub mod entry; mod fake_doc_items; pub mod hir_id_validator; pub mod input_stats; -mod lang_items; pub mod layout_test; mod lib_features; mod reachable; pub mod stability; mod upvars; -mod weak_lang_items; pub fn provide(providers: &mut Providers) { canonical_symbols::provide(providers); @@ -40,7 +38,6 @@ pub fn provide(providers: &mut Providers) { debugger_visualizer::provide(providers); diagnostic_items::provide(providers); entry::provide(providers); - lang_items::provide(providers); lib_features::provide(providers); reachable::provide(providers); stability::provide(providers); diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 36db6adc7f920..d28936f8624fd 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -15,6 +15,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_crate_store = { path = "../rustc_crate_store" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index 01ec5bcb6cf29..4dc4572ad5176 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -8,7 +8,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Spanned, Symbol}; use crate::Res; -use crate::late::PatternSource; +use crate::late::{Duplicate, PatternSource}; pub(crate) mod impls; @@ -1785,3 +1785,134 @@ pub(crate) struct ToolAttrAmbiguity { pub attr_path: String, pub tool: Symbol, } + +pub(crate) struct DuplicateLangItem { + pub local_span: Option, + pub lang_item_name: Symbol, + pub crate_name: Symbol, + pub dependency_of: Option, + pub is_local: bool, + pub path: String, + pub first_defined_span: Option, + pub orig_crate_name: Option, + pub orig_dependency_of: Option, + pub orig_is_local: bool, + pub orig_path: String, + pub(crate) duplicate: Duplicate, +} + +impl Diagnostic<'_> for DuplicateLangItem { + #[track_caller] + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { + let mut diag = Diag::new( + dcx, + level, + match self.duplicate { + Duplicate::Plain => msg!("found duplicate lang item `{$lang_item_name}`"), + Duplicate::Crate => { + msg!("duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`") + } + Duplicate::CrateDepends => msg!( + "duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`" + ), + }, + ); + diag.code(E0152); + diag.arg("lang_item_name", self.lang_item_name); + diag.arg("crate_name", self.crate_name); + if let Some(dependency_of) = self.dependency_of { + diag.arg("dependency_of", dependency_of); + } + diag.arg("path", self.path); + if let Some(orig_crate_name) = self.orig_crate_name { + diag.arg("orig_crate_name", orig_crate_name); + } + if let Some(orig_dependency_of) = self.orig_dependency_of { + diag.arg("orig_dependency_of", orig_dependency_of); + } + diag.arg("orig_path", self.orig_path); + if let Some(span) = self.local_span { + diag.span(span); + } + if let Some(span) = self.first_defined_span { + diag.span_note(span, msg!("the lang item is first defined here")); + } else { + if self.orig_dependency_of.is_none() { + diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}`")); + } else { + diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)")); + } + + if self.orig_is_local { + diag.note(msg!("first definition in the local crate (`{$orig_crate_name}`)")); + } else { + diag.note(msg!( + "first definition in `{$orig_crate_name}` loaded from {$orig_path}" + )); + } + + if self.is_local { + diag.note(msg!("second definition in the local crate (`{$crate_name}`)")); + } else { + diag.note(msg!("second definition in `{$crate_name}` loaded from {$path}")); + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag("`#[panic_handler]` function required, but not found")] +pub(crate) struct MissingPanicHandler; + +#[derive(Diagnostic)] +#[diag("unwinding panics are not supported without std")] +#[help("using nightly cargo, use -Zbuild-std with panic=\"abort\" to avoid unwinding")] +#[note( + "since the core library is usually precompiled with panic=\"unwind\", rebuilding your crate with panic=\"abort\" may not be enough to fix the problem" +)] +pub(crate) struct PanicUnwindWithoutStd; + +#[derive(Diagnostic)] +#[diag("lang items are not allowed in stable dylibs")] +pub(crate) struct IncorrectCrateType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("`{$name}` lang item must be applied to a {$kind} with {$at_least -> + [true] at least {$num} + *[false] {$num} + } generic {$num -> + [one] argument + *[other] arguments + }", code = E0718)] +pub(crate) struct IncorrectTarget<'a> { + #[primary_span] + pub span: Span, + #[label( + "this {$kind} has {$actual_num} generic {$actual_num -> + [one] argument + *[other] arguments + }" + )] + pub generics_span: Span, + pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` + pub kind: &'static str, + pub num: usize, + pub actual_num: usize, + pub at_least: bool, +} + +#[derive(Diagnostic)] +#[diag("lang item required, but not found: `{$name}`")] +#[note( + "this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library" +)] +#[help( + "you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`" +)] +pub(crate) struct MissingLangItem { + pub name: Symbol, +} diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bffcf60a920ff..13a8d8b8f25e9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -17,6 +17,7 @@ use rustc_ast::visit::{ AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, }; use rustc_ast::*; +use rustc_attr_ir::target::Target; use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -50,8 +51,11 @@ use crate::{ }; mod diagnostics; +mod lang_items; +pub(super) mod weak_lang_items; use diagnostics::{ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime}; +pub(super) use lang_items::{Duplicate, collect_item}; #[derive(Copy, Clone, Debug)] struct BindingInfo { @@ -1130,6 +1134,13 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc with_owner(self, foreign_item.id, |this| { this.resolve_doc_links(&foreign_item.attrs, MaybeExported::Ok(foreign_item.id)); let def_kind = this.r.tcx.def_kind(this.r.current_owner.def_id); + this.check_for_lang( + Target::from_foreign_item_kind(&foreign_item.kind), + this.r.current_owner.def_id, + &foreign_item.attrs, + foreign_item.span, + None, + ); match foreign_item.kind { ForeignItemKind::TyAlias(TyAlias { ref generics, .. }) => { this.with_generic_param_rib( @@ -1549,6 +1560,13 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc self.visit_vis(&v.vis); self.visit_ident(&v.ident); self.visit_variant_data(&v.data); + self.check_for_lang( + Target::Variant, + self.r.current_owner.node_id_to_def_id[&v.id], + &v.attrs, + v.span, + None, + ); if let Some(discr) = &v.disr_expr { self.resolve_anon_const(discr, AnonConstKind::EnumDiscriminant); } @@ -2989,6 +3007,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { debug!("(resolving item) resolving {:?} ({:?})", item.kind.ident(), item.kind); let def_kind = self.r.tcx.def_kind(self.r.current_owner.def_id); + + self.check_for_lang( + Target::from_ast_item(item), + self.r.current_owner.def_id, + &item.attrs, + item.span, + item.opt_generics(), + ); + match &item.kind { ItemKind::TyAlias(TyAlias { generics, .. }) => { self.with_generic_param_rib( @@ -3588,6 +3615,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + self.check_for_lang( + Target::from_assoc_item_kind(&item.kind, AssocCtxt::Trait), + self.r.current_owner.def_id, + &item.attrs, + item.span, + item.opt_generics(), + ); match &item.kind { AssocItemKind::Const(ast::ConstItem { generics, ty, body, define_opaque, .. }) => { self.with_generic_param_rib( @@ -3819,6 +3853,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis))); let prev = self.diag_metadata.current_impl_item.take(); self.diag_metadata.current_impl_item = Some(&item); + self.check_for_lang( + Target::from_assoc_item_kind( + &item.kind, + AssocCtxt::Impl { of_trait: is_in_trait_impl }, + ), + self.r.current_owner.def_id, + &item.attrs, + item.span, + item.opt_generics(), + ); match &item.kind { AssocItemKind::Const(ast::ConstItem { ident, diff --git a/compiler/rustc_resolve/src/late/lang_items.rs b/compiler/rustc_resolve/src/late/lang_items.rs new file mode 100644 index 0000000000000..b53928df24dc8 --- /dev/null +++ b/compiler/rustc_resolve/src/late/lang_items.rs @@ -0,0 +1,256 @@ +//! Detecting lang items. +//! +//! Language items are items that represent concepts intrinsic to the language +//! itself. Examples are: +//! +//! * Traits that specify "kinds"; e.g., `Sync`, `Send`. +//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. +//! * Functions called by the compiler itself. + +use rustc_ast as ast; +use rustc_attr_ir::target::Target; +use rustc_attr_ir::{GenericRequirement, LangItem, LanguageItems}; +use rustc_crate_store::ExternCrate; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::TyCtxt; +use rustc_span::{Span, sym}; +use rustc_structures::CrateType; + +use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget}; +use crate::late::{LateResolutionVisitor, RibKind}; + +pub(crate) enum Duplicate { + Plain, + Crate, + CrateDepends, +} + +impl<'tcx> LateResolutionVisitor<'_, '_, '_, 'tcx> { + pub(super) fn check_for_lang( + &mut self, + actual_target: Target, + def_id: LocalDefId, + attrs: &[ast::Attribute], + item_span: Span, + generics: Option<&ast::Generics>, + ) { + let attrs = 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, + }) + }); + if let Some((name, attr_span)) = attrs { + match LangItem::from_name(name) { + // Known lang item + Some(lang_item) => { + if actual_target != lang_item.target() { + // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast + // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`. + if !(lang_item.is_weak() + && actual_target == Target::Fn + && lang_item.target() == Target::ForeignFn + && matches!(lang_item, LangItem::PanicImpl)) + { + self.r.tcx + .dcx() + .delayed_bug(format!("lang item target is checked in attribute parser: {:?} has {} but expected {}", def_id, actual_target, lang_item.target())); + return; + } + } + // Weak lang items are handled separately + if lang_item.is_weak() && actual_target == Target::ForeignFn { + self.r.lang_items.missing.push(lang_item); + } else { + // Weak only lang items are always handled here + self.collect_item_extended( + lang_item, + def_id, + item_span, + attr_span, + generics, + actual_target, + ); + } + } + // Unknown lang item. + _ => { + self.r.tcx.dcx().delayed_bug("unknown lang item"); + } + } + } + } + + // Like collect_item() above, but also checks whether the lang item is declared + // with the right number of generic arguments. + fn collect_item_extended( + &mut self, + lang_item: LangItem, + item_def_id: LocalDefId, + item_span: Span, + attr_span: Span, + generics: Option<&ast::Generics>, + target: Target, + ) { + let name = lang_item.name(); + + if let Some(generics) = generics { + // Now check whether the lang_item has the expected number of generic + // arguments. Generally speaking, binary and indexing operations have + // one (for the RHS/index), unary operations have none, the closure + // traits have one for the argument list, coroutines have one for the + // resume argument, and ordering/equality relations have one for the RHS + // Some other types like Box and various unsizing-related traits + // have minimum requirements. + + // FIXME: This still doesn't count, e.g., elided lifetimes and APITs. + let mut actual_num = generics.params.len(); + if target.is_associated_item() { + for rib in self.ribs.type_ns.iter().rev() { + match rib.kind { + RibKind::Item(_, _) => { + actual_num += rib.bindings.len(); + break; + } + _ => {} + } + } + } + + let mut at_least = false; + let required = match lang_item.required_generics() { + GenericRequirement::Exact(num) if num != actual_num => Some(num), + GenericRequirement::Minimum(num) if actual_num < num => { + at_least = true; + Some(num) + } + // If the number matches, or there is no requirement, handle it normally + _ => None, + }; + + if let Some(num) = required { + // We are issuing E0718 "incorrect target" here, because while the + // item kind of the target is correct, the target is still wrong + // because of the wrong number of generic arguments. + self.r.tcx.dcx().emit_err(IncorrectTarget { + span: attr_span, + generics_span: generics.span, + name: name.as_str(), + kind: target.name(), + num, + actual_num, + at_least, + }); + + // return early to not collect the lang item + return; + } + } + + if self.r.tcx.crate_types().contains(&CrateType::Sdylib) { + self.r.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span }); + } + + collect_item( + self.r.tcx, + &mut self.r.lang_items, + lang_item, + item_def_id.to_def_id(), + Some(item_span), + ); + } +} + +pub(crate) fn collect_item( + tcx: TyCtxt<'_>, + lang_items: &mut LanguageItems, + lang_item: LangItem, + item_def_id: DefId, + item_span: Option, +) { + // Check for duplicates. + if let Some(original_def_id) = lang_items.get(lang_item) + && original_def_id != item_def_id + { + let (item_def_id, original_def_id, item_span) = if let Some(original_def_id) = + original_def_id.as_local() + && !item_def_id.is_local() + { + // The crate-local definitions are loaded before the upstream crate definitions, + // because we only know which crates are being loaded after the resolver is fully done. + // In that case we swap the order of the items to report it meaningfully to the user. + (original_def_id.to_def_id(), item_def_id, Some(tcx.source_span(original_def_id))) + } else { + (item_def_id, original_def_id, item_span) + }; + let lang_item_name = lang_item.name(); + let crate_name = tcx.crate_name(item_def_id.krate); + let mut dependency_of = None; + let is_local = item_def_id.is_local(); + let path = if is_local { + String::new() + } else { + tcx.crate_extern_paths(item_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + }; + + let mut orig_crate_name = None; + let mut orig_dependency_of = None; + let orig_is_local = original_def_id.is_local(); + let orig_path = if orig_is_local { + String::new() + } else { + tcx.crate_extern_paths(original_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + }; + + if !original_def_id.is_local() { + orig_crate_name = Some(tcx.crate_name(original_def_id.krate)); + if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) = + tcx.extern_crate(original_def_id.krate) + { + orig_dependency_of = Some(tcx.crate_name(*inner_dependency_of)); + } + } + + let duplicate = if item_span.is_some() { + Duplicate::Plain + } else { + match tcx.extern_crate(item_def_id.krate) { + Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => { + dependency_of = Some(tcx.crate_name(*inner_dependency_of)); + Duplicate::CrateDepends + } + _ => Duplicate::Crate, + } + }; + + // When there's a duplicate lang item, something went very wrong and there's no value + // in recovering or doing anything. Give the user the one message to let them debug the + // mess they created and then wish them farewell. + tcx.dcx().emit_fatal(DuplicateLangItem { + local_span: item_span, + lang_item_name, + crate_name, + dependency_of, + is_local, + path, + first_defined_span: original_def_id.as_local().map(|did| tcx.source_span(did)), + orig_crate_name, + orig_dependency_of, + orig_is_local, + orig_path, + duplicate, + }); + } else { + // Matched. + lang_items.set(lang_item, item_def_id); + } +} diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_resolve/src/late/weak_lang_items.rs similarity index 100% rename from compiler/rustc_passes/src/weak_lang_items.rs rename to compiler/rustc_resolve/src/late/weak_lang_items.rs diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 58d6b8005c787..0fc9c31f43e32 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -42,7 +42,7 @@ use rustc_ast::{ self as ast, AngleBracketedArg, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, Expr, ExprKind, GenericArg, GenericArgs, Generics, NodeId, Path, attr, }; -use rustc_attr_ir::{StrippedCfgItem, find_attr}; +use rustc_attr_ir::{LanguageItems, StrippedCfgItem, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, default}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; @@ -1601,6 +1601,8 @@ pub struct Resolver<'ra, 'tcx> { /// Stores `#[diagnostic::on_unknown]` attributes placed on module declarations. on_unknown_data: FxHashMap = default::fx_hash_map(), features: &'tcx Features, + + lang_items: LanguageItems, } /// This provides memory for the rest of the crate. The `'ra` lifetime that is @@ -1920,6 +1922,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { current_crate_outer_attr_insert_span, disambiguators: Default::default(), features: tcx.features(), + lang_items: LanguageItems::new(), .. }; @@ -1990,6 +1993,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let confused_type_with_std_module = self.confused_type_with_std_module; let paths_matching_assoc_types = self.paths_matching_assoc_types; let effective_visibilities = self.effective_visibilities; + let lang_items = self.lang_items; let stripped_cfg_items = self .stripped_cfg_items @@ -2026,6 +2030,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { stripped_cfg_items, delegation_infos: self.delegation_infos, delegation_inherent_fn_map: self.delegation_inherent_fn_map, + lang_items, }; let ast_lowering = ResolverAstLowering { partial_res_map: self.partial_res_map, @@ -2119,6 +2124,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx .sess .time("resolve_postprocess", || self.cstore_mut().postprocess(self.tcx, krate)); + + self.tcx.sess.time("finalize_lang_items", || self.finalize_lang_items()); }); // Don't mutate the cstore or stable crate id map from here on. @@ -2700,6 +2707,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } self.main_def = Some(MainDefinition { res, is_import, span }); } + + fn finalize_lang_items(&mut self) { + // Collect lang items in other crates. + for &cnum in self.tcx.used_crates(()) { + for &(def_id, lang_item) in self.tcx.defined_lang_items(cnum).iter() { + late::collect_item(self.tcx, &mut self.lang_items, lang_item, def_id, None); + } + } + late::weak_lang_items::check_crate(self.tcx, &mut self.lang_items); + } } fn with_owner<'ra, 'tcx, R: AsMut>, T>( diff --git a/tests/ui/lang-items/issue-83471.stderr b/tests/ui/lang-items/issue-83471.stderr index ffc48accdd917..49b3bfe84dc61 100644 --- a/tests/ui/lang-items/issue-83471.stderr +++ b/tests/ui/lang-items/issue-83471.stderr @@ -1,3 +1,12 @@ +error[E0718]: `fn` lang item must be applied to a trait with 1 generic argument + --> $DIR/issue-83471.rs:20:1 + | +LL | #[lang = "fn"] + | ^^^^^^^^^^^^^^ +... +LL | trait Fn { + | - this trait has 0 generic arguments + error[E0573]: cannot find type `export_name` in this scope --> $DIR/issue-83471.rs:24:13 | @@ -16,15 +25,6 @@ LL | fn call(export_name); = note: for more information, see = note: `#[warn(anonymous_parameters)]` (part of `#[warn(rust_2018_compatibility)]`) on by default -error[E0718]: `fn` lang item must be applied to a trait with 1 generic argument - --> $DIR/issue-83471.rs:20:1 - | -LL | #[lang = "fn"] - | ^^^^^^^^^^^^^^ -... -LL | trait Fn { - | - this trait has 0 generic arguments - error[E0658]: lang items are subject to change --> $DIR/issue-83471.rs:8:3 | diff --git a/tests/ui/panic-handler/weak-lang-item.rs b/tests/ui/panic-handler/weak-lang-item.rs index 6c3a5b2b271c2..d43f80cc55d6f 100644 --- a/tests/ui/panic-handler/weak-lang-item.rs +++ b/tests/ui/panic-handler/weak-lang-item.rs @@ -1,6 +1,8 @@ //@ edition:2015 //@ aux-build:weak-lang-items.rs //@ needs-unwind since it affects the error output +//~? ERROR: `#[panic_handler]` function required +//~? ERROR: unwinding panics are not supported without std #![no_std] diff --git a/tests/ui/panic-handler/weak-lang-item.stderr b/tests/ui/panic-handler/weak-lang-item.stderr index d7b453c96e488..5dcb37df68921 100644 --- a/tests/ui/panic-handler/weak-lang-item.stderr +++ b/tests/ui/panic-handler/weak-lang-item.stderr @@ -1,5 +1,5 @@ error[E0259]: the name `core` is defined multiple times - --> $DIR/weak-lang-item.rs:7:1 + --> $DIR/weak-lang-item.rs:9:1 | LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ `core` reimported here @@ -10,6 +10,13 @@ help: you can use `as` to change the binding name of the import LL | extern crate core as other_core; | +++++++++++++ -error: aborting due to 1 previous error +error: `#[panic_handler]` function required, but not found + +error: unwinding panics are not supported without std + | + = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0259`.