Skip to main content

rustc_passes/
lang_items.rs

1//! Detecting lang items.
2//!
3//! Language items are items that represent concepts intrinsic to the language
4//! itself. Examples are:
5//!
6//! * Traits that specify "kinds"; e.g., `Sync`, `Send`.
7//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
8//! * Functions called by the compiler itself.
9
10use rustc_ast as ast;
11use rustc_ast::visit;
12use rustc_data_structures::fx::FxHashMap;
13use rustc_hir::def_id::{DefId, LocalDefId};
14use rustc_hir::lang_items::GenericRequirement;
15use rustc_hir::{LangItem, LanguageItems, MethodKind, Target};
16use rustc_middle::query::Providers;
17use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
18use rustc_session::cstore::ExternCrate;
19use rustc_span::{Span, Symbol, sym};
20
21use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget};
22use crate::weak_lang_items;
23
24pub(crate) enum Duplicate {
25    Plain,
26    Crate,
27    CrateDepends,
28}
29
30enum CollectWeak {
31    Allowed,
32    Ignore,
33}
34
35struct LanguageItemCollector<'ast, 'tcx> {
36    items: LanguageItems,
37    tcx: TyCtxt<'tcx>,
38    resolver: &'ast ResolverAstLowering<'tcx>,
39    // FIXME(#118552): We should probably feed def_span eagerly on def-id creation
40    // so we can avoid constructing this map for local def-ids.
41    item_spans: FxHashMap<DefId, Span>,
42    parent_item: Option<&'ast ast::Item>,
43}
44
45impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
46    fn new(
47        tcx: TyCtxt<'tcx>,
48        resolver: &'ast ResolverAstLowering<'tcx>,
49    ) -> LanguageItemCollector<'ast, 'tcx> {
50        LanguageItemCollector {
51            tcx,
52            resolver,
53            items: LanguageItems::new(),
54            item_spans: FxHashMap::default(),
55            parent_item: None,
56        }
57    }
58
59    fn check_for_lang(
60        &mut self,
61        actual_target: Target,
62        def_id: LocalDefId,
63        attrs: &'ast [ast::Attribute],
64        item_span: Span,
65        generics: Option<&'ast ast::Generics>,
66        collect_weak: CollectWeak,
67    ) {
68        if let Some((name, attr_span)) = extract_ast(attrs) {
69            match LangItem::from_name(name) {
70                // Known lang item
71                Some(lang_item) => {
72                    if actual_target != lang_item.target() {
73                        self.tcx
74                            .dcx()
75                            .delayed_bug("lang item target is checked in attribute parser");
76                        return;
77                    }
78                    // Weak lang items are handled separately
79                    // Weak only lang items are always handled here
80                    if !lang_item.is_weak() || #[allow(non_exhaustive_omitted_patterns)] match collect_weak {
    CollectWeak::Allowed => true,
    _ => false,
}matches!(collect_weak, CollectWeak::Allowed) {
81                        self.collect_item_extended(
82                            lang_item,
83                            def_id,
84                            item_span,
85                            attr_span,
86                            generics,
87                            actual_target,
88                        );
89                    }
90                }
91                // Unknown lang item.
92                _ => {
93                    self.tcx.dcx().delayed_bug("unknown lang item");
94                }
95            }
96        }
97    }
98
99    fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option<Span>) {
100        // Check for duplicates.
101        if let Some(original_def_id) = self.items.get(lang_item)
102            && original_def_id != item_def_id
103        {
104            let lang_item_name = lang_item.name();
105            let crate_name = self.tcx.crate_name(item_def_id.krate);
106            let mut dependency_of = None;
107            let is_local = item_def_id.is_local();
108            let path = if is_local {
109                String::new()
110            } else {
111                self.tcx
112                    .crate_extern_paths(item_def_id.krate)
113                    .iter()
114                    .map(|p| p.display().to_string())
115                    .collect::<Vec<_>>()
116                    .join(", ")
117            };
118
119            let first_defined_span = self.item_spans.get(&original_def_id).copied();
120            let mut orig_crate_name = None;
121            let mut orig_dependency_of = None;
122            let orig_is_local = original_def_id.is_local();
123            let orig_path = if orig_is_local {
124                String::new()
125            } else {
126                self.tcx
127                    .crate_extern_paths(original_def_id.krate)
128                    .iter()
129                    .map(|p| p.display().to_string())
130                    .collect::<Vec<_>>()
131                    .join(", ")
132            };
133
134            if first_defined_span.is_none() {
135                orig_crate_name = Some(self.tcx.crate_name(original_def_id.krate));
136                if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
137                    self.tcx.extern_crate(original_def_id.krate)
138                {
139                    orig_dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
140                }
141            }
142
143            let duplicate = if item_span.is_some() {
144                Duplicate::Plain
145            } else {
146                match self.tcx.extern_crate(item_def_id.krate) {
147                    Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
148                        dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
149                        Duplicate::CrateDepends
150                    }
151                    _ => Duplicate::Crate,
152                }
153            };
154
155            // When there's a duplicate lang item, something went very wrong and there's no value
156            // in recovering or doing anything. Give the user the one message to let them debug the
157            // mess they created and then wish them farewell.
158            self.tcx.dcx().emit_fatal(DuplicateLangItem {
159                local_span: item_span,
160                lang_item_name,
161                crate_name,
162                dependency_of,
163                is_local,
164                path,
165                first_defined_span,
166                orig_crate_name,
167                orig_dependency_of,
168                orig_is_local,
169                orig_path,
170                duplicate,
171            });
172        } else {
173            // Matched.
174            self.items.set(lang_item, item_def_id);
175            // Collect span for error later
176            if let Some(item_span) = item_span {
177                self.item_spans.insert(item_def_id, item_span);
178            }
179        }
180    }
181
182    // Like collect_item() above, but also checks whether the lang item is declared
183    // with the right number of generic arguments.
184    fn collect_item_extended(
185        &mut self,
186        lang_item: LangItem,
187        item_def_id: LocalDefId,
188        item_span: Span,
189        attr_span: Span,
190        generics: Option<&'ast ast::Generics>,
191        target: Target,
192    ) {
193        let name = lang_item.name();
194
195        if let Some(generics) = generics {
196            // Now check whether the lang_item has the expected number of generic
197            // arguments. Generally speaking, binary and indexing operations have
198            // one (for the RHS/index), unary operations have none, the closure
199            // traits have one for the argument list, coroutines have one for the
200            // resume argument, and ordering/equality relations have one for the RHS
201            // Some other types like Box and various unsizing-related traits
202            // have minimum requirements.
203
204            // FIXME: This still doesn't count, e.g., elided lifetimes and APITs.
205            let mut actual_num = generics.params.len();
206            if target.is_associated_item() {
207                actual_num += self
208                    .parent_item
209                    .unwrap()
210                    .opt_generics()
211                    .map_or(0, |generics| generics.params.len());
212            }
213
214            let mut at_least = false;
215            let required = match lang_item.required_generics() {
216                GenericRequirement::Exact(num) if num != actual_num => Some(num),
217                GenericRequirement::Minimum(num) if actual_num < num => {
218                    at_least = true;
219                    Some(num)
220                }
221                // If the number matches, or there is no requirement, handle it normally
222                _ => None,
223            };
224
225            if let Some(num) = required {
226                // We are issuing E0718 "incorrect target" here, because while the
227                // item kind of the target is correct, the target is still wrong
228                // because of the wrong number of generic arguments.
229                self.tcx.dcx().emit_err(IncorrectTarget {
230                    span: attr_span,
231                    generics_span: generics.span,
232                    name: name.as_str(),
233                    kind: target.name(),
234                    num,
235                    actual_num,
236                    at_least,
237                });
238
239                // return early to not collect the lang item
240                return;
241            }
242        }
243
244        if self.tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) {
245            self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span });
246        }
247
248        self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span));
249    }
250}
251
252/// Traverses and collects all the lang items in all crates.
253fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
254    let (resolver, krate) = tcx.resolver_for_lowering();
255    let resolver = &*resolver.borrow();
256    let krate = &*krate.borrow();
257
258    // Initialize the collector.
259    let mut collector = LanguageItemCollector::new(tcx, resolver);
260
261    // Collect lang items in other crates.
262    for &cnum in tcx.used_crates(()).iter() {
263        for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
264            collector.collect_item(lang_item, def_id, None);
265        }
266    }
267
268    // Collect lang items local to this crate.
269    visit::Visitor::visit_crate(&mut collector, krate);
270
271    // Find all required but not-yet-defined lang items.
272    weak_lang_items::check_crate(tcx, &mut collector.items, krate);
273
274    // Return all the lang items that were found.
275    collector.items
276}
277
278impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
279    fn visit_item(&mut self, i: &'ast ast::Item) {
280        let target = match &i.kind {
281            ast::ItemKind::ExternCrate(..) => Target::ExternCrate,
282            ast::ItemKind::Use(_) => Target::Use,
283            ast::ItemKind::Static(_) => Target::Static,
284            ast::ItemKind::Const(_) | ast::ItemKind::ConstBlock(_) => Target::Const,
285            ast::ItemKind::Fn(_) | ast::ItemKind::Delegation(..) => Target::Fn,
286            ast::ItemKind::Mod(..) => Target::Mod,
287            ast::ItemKind::ForeignMod(_) => Target::ForeignFn,
288            ast::ItemKind::GlobalAsm(_) => Target::GlobalAsm,
289            ast::ItemKind::TyAlias(_) => Target::TyAlias,
290            ast::ItemKind::Enum(..) => Target::Enum,
291            ast::ItemKind::Struct(..) => Target::Struct,
292            ast::ItemKind::Union(..) => Target::Union,
293            ast::ItemKind::Trait(_) => Target::Trait,
294            ast::ItemKind::TraitAlias(..) => Target::TraitAlias,
295            ast::ItemKind::Impl(imp_) => Target::Impl { of_trait: imp_.of_trait.is_some() },
296            ast::ItemKind::MacroDef(..) => Target::MacroDef,
297            ast::ItemKind::MacCall(_) | ast::ItemKind::DelegationMac(_) => {
298                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("macros should have been expanded")));
}unreachable!("macros should have been expanded")
299            }
300        };
301
302        self.check_for_lang(
303            target,
304            self.resolver.owners[&i.id].def_id,
305            &i.attrs,
306            i.span,
307            i.opt_generics(),
308            CollectWeak::Allowed,
309        );
310
311        let parent_item = self.parent_item.replace(i);
312        visit::walk_item(self, i);
313        self.parent_item = parent_item;
314    }
315
316    fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) {
317        self.check_for_lang(
318            Target::Fn,
319            self.resolver.owners[&i.id].def_id,
320            &i.attrs,
321            i.span,
322            None,
323            CollectWeak::Ignore,
324        );
325    }
326
327    fn visit_variant(&mut self, variant: &'ast ast::Variant) {
328        self.check_for_lang(
329            Target::Variant,
330            self.resolver.owners[&self.parent_item.unwrap().id].node_id_to_def_id[&variant.id],
331            &variant.attrs,
332            variant.span,
333            None,
334            CollectWeak::Allowed,
335        );
336    }
337
338    fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) {
339        let (target, generics) = match &i.kind {
340            ast::AssocItemKind::Fn(..) | ast::AssocItemKind::Delegation(..) => {
341                let (body, generics) = if let ast::AssocItemKind::Fn(fun) = &i.kind {
342                    (fun.body.is_some(), Some(&fun.generics))
343                } else {
344                    (true, None)
345                };
346                (
347                    match &self.parent_item.unwrap().kind {
348                        ast::ItemKind::Impl(i) => {
349                            if i.of_trait.is_some() {
350                                Target::Method(MethodKind::TraitImpl)
351                            } else {
352                                Target::Method(MethodKind::Inherent)
353                            }
354                        }
355                        ast::ItemKind::Trait(_) => Target::Method(MethodKind::Trait { body }),
356                        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
357                    },
358                    generics,
359                )
360            }
361            ast::AssocItemKind::Const(ct) => (Target::AssocConst, Some(&ct.generics)),
362            ast::AssocItemKind::Type(ty) => (Target::AssocTy, Some(&ty.generics)),
363            ast::AssocItemKind::MacCall(_) | ast::AssocItemKind::DelegationMac(_) => {
364                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("macros should have been expanded")));
}unreachable!("macros should have been expanded")
365            }
366        };
367
368        self.check_for_lang(
369            target,
370            self.resolver.owners[&i.id].def_id,
371            &i.attrs,
372            i.span,
373            generics,
374            CollectWeak::Allowed,
375        );
376
377        visit::walk_assoc_item(self, i, ctxt);
378    }
379}
380
381/// Extracts the first `lang = "$name"` out of a list of attributes.
382/// The `#[panic_handler]` attribute is also extracted out when found.
383///
384/// This function is used for `ast::Attribute`, for `hir::Attribute` use the `find_attr!` macro with `AttributeKind::Lang`
385pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> {
386    attrs.iter().find_map(|attr| {
387        Some(match attr {
388            _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
389            _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
390            _ => return None,
391        })
392    })
393}
394
395pub(crate) fn provide(providers: &mut Providers) {
396    providers.get_lang_items = get_lang_items;
397}