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_crate_store::ExternCrate;
13use rustc_hir::Target;
14use rustc_hir::attrs::lang_items::{GenericRequirement, LangItem, LanguageItems};
15use rustc_hir::def_id::{DefId, LocalDefId};
16use rustc_middle::middle::resolve::ResolverAstLowering;
17use rustc_middle::query::Providers;
18use rustc_middle::ty::TyCtxt;
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
30struct LanguageItemCollector<'ast, 'tcx> {
31    items: LanguageItems,
32    tcx: TyCtxt<'tcx>,
33    resolver: &'ast ResolverAstLowering<'tcx>,
34    parent_item: Option<&'ast ast::Item>,
35}
36
37impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
38    fn new(
39        tcx: TyCtxt<'tcx>,
40        resolver: &'ast ResolverAstLowering<'tcx>,
41    ) -> LanguageItemCollector<'ast, 'tcx> {
42        LanguageItemCollector { tcx, resolver, items: LanguageItems::new(), parent_item: None }
43    }
44
45    fn check_for_lang(
46        &mut self,
47        actual_target: Target,
48        def_id: LocalDefId,
49        attrs: &'ast [ast::Attribute],
50        item_span: Span,
51        generics: Option<&'ast ast::Generics>,
52    ) {
53        if let Some((name, attr_span)) = extract_ast(attrs) {
54            match LangItem::from_name(name) {
55                // Known lang item
56                Some(lang_item) => {
57                    if actual_target != lang_item.target() {
58                        // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast
59                        // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`.
60                        if !(lang_item.is_weak()
61                            && actual_target == Target::Fn
62                            && lang_item.target() == Target::ForeignFn
63                            && #[allow(non_exhaustive_omitted_patterns)] match lang_item {
    LangItem::PanicImpl => true,
    _ => false,
}matches!(lang_item, LangItem::PanicImpl))
64                        {
65                            self.tcx
66                            .dcx()
67                            .delayed_bug(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("lang item target is checked in attribute parser: {0:?} has {1} but expected {2}",
                def_id, actual_target, lang_item.target()))
    })format!("lang item target is checked in attribute parser: {:?} has {} but expected {}", def_id, actual_target, lang_item.target()));
68                            return;
69                        }
70                    }
71                    // Weak lang items are handled separately
72                    if lang_item.is_weak() && actual_target == Target::ForeignFn {
73                        self.items.missing.push(lang_item);
74                    } else {
75                        // Weak only lang items are always handled here
76                        self.collect_item_extended(
77                            lang_item,
78                            def_id,
79                            item_span,
80                            attr_span,
81                            generics,
82                            actual_target,
83                        );
84                    }
85                }
86                // Unknown lang item.
87                _ => {
88                    self.tcx.dcx().delayed_bug("unknown lang item");
89                }
90            }
91        }
92    }
93
94    fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option<Span>) {
95        // Check for duplicates.
96        if let Some(original_def_id) = self.items.get(lang_item)
97            && original_def_id != item_def_id
98        {
99            let lang_item_name = lang_item.name();
100            let crate_name = self.tcx.crate_name(item_def_id.krate);
101            let mut dependency_of = None;
102            let is_local = item_def_id.is_local();
103            let path = if is_local {
104                String::new()
105            } else {
106                self.tcx
107                    .crate_extern_paths(item_def_id.krate)
108                    .iter()
109                    .map(|p| p.display().to_string())
110                    .collect::<Vec<_>>()
111                    .join(", ")
112            };
113
114            let mut orig_crate_name = None;
115            let mut orig_dependency_of = None;
116            let orig_is_local = original_def_id.is_local();
117            let orig_path = if orig_is_local {
118                String::new()
119            } else {
120                self.tcx
121                    .crate_extern_paths(original_def_id.krate)
122                    .iter()
123                    .map(|p| p.display().to_string())
124                    .collect::<Vec<_>>()
125                    .join(", ")
126            };
127
128            if !original_def_id.is_local() {
129                orig_crate_name = Some(self.tcx.crate_name(original_def_id.krate));
130                if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
131                    self.tcx.extern_crate(original_def_id.krate)
132                {
133                    orig_dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
134                }
135            }
136
137            let duplicate = if item_span.is_some() {
138                Duplicate::Plain
139            } else {
140                match self.tcx.extern_crate(item_def_id.krate) {
141                    Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
142                        dependency_of = Some(self.tcx.crate_name(*inner_dependency_of));
143                        Duplicate::CrateDepends
144                    }
145                    _ => Duplicate::Crate,
146                }
147            };
148
149            // When there's a duplicate lang item, something went very wrong and there's no value
150            // in recovering or doing anything. Give the user the one message to let them debug the
151            // mess they created and then wish them farewell.
152            self.tcx.dcx().emit_fatal(DuplicateLangItem {
153                local_span: item_span,
154                lang_item_name,
155                crate_name,
156                dependency_of,
157                is_local,
158                path,
159                first_defined_span: original_def_id.as_local().map(|did| self.tcx.source_span(did)),
160                orig_crate_name,
161                orig_dependency_of,
162                orig_is_local,
163                orig_path,
164                duplicate,
165            });
166        } else {
167            // Matched.
168            self.items.set(lang_item, item_def_id);
169        }
170    }
171
172    // Like collect_item() above, but also checks whether the lang item is declared
173    // with the right number of generic arguments.
174    fn collect_item_extended(
175        &mut self,
176        lang_item: LangItem,
177        item_def_id: LocalDefId,
178        item_span: Span,
179        attr_span: Span,
180        generics: Option<&'ast ast::Generics>,
181        target: Target,
182    ) {
183        let name = lang_item.name();
184
185        if let Some(generics) = generics {
186            // Now check whether the lang_item has the expected number of generic
187            // arguments. Generally speaking, binary and indexing operations have
188            // one (for the RHS/index), unary operations have none, the closure
189            // traits have one for the argument list, coroutines have one for the
190            // resume argument, and ordering/equality relations have one for the RHS
191            // Some other types like Box and various unsizing-related traits
192            // have minimum requirements.
193
194            // FIXME: This still doesn't count, e.g., elided lifetimes and APITs.
195            let mut actual_num = generics.params.len();
196            if target.is_associated_item() {
197                actual_num += self
198                    .parent_item
199                    .unwrap()
200                    .opt_generics()
201                    .map_or(0, |generics| generics.params.len());
202            }
203
204            let mut at_least = false;
205            let required = match lang_item.required_generics() {
206                GenericRequirement::Exact(num) if num != actual_num => Some(num),
207                GenericRequirement::Minimum(num) if actual_num < num => {
208                    at_least = true;
209                    Some(num)
210                }
211                // If the number matches, or there is no requirement, handle it normally
212                _ => None,
213            };
214
215            if let Some(num) = required {
216                // We are issuing E0718 "incorrect target" here, because while the
217                // item kind of the target is correct, the target is still wrong
218                // because of the wrong number of generic arguments.
219                self.tcx.dcx().emit_err(IncorrectTarget {
220                    span: attr_span,
221                    generics_span: generics.span,
222                    name: name.as_str(),
223                    kind: target.name(),
224                    num,
225                    actual_num,
226                    at_least,
227                });
228
229                // return early to not collect the lang item
230                return;
231            }
232        }
233
234        if self.tcx.crate_types().contains(&rustc_structures::CrateType::Sdylib) {
235            self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span });
236        }
237
238        self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span));
239    }
240}
241
242/// Traverses and collects all the lang items in all crates.
243fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
244    let (resolver, krate) = tcx.resolver_for_lowering();
245    let resolver = &*resolver.borrow();
246    let krate = &*krate.borrow();
247
248    // Initialize the collector.
249    let mut collector = LanguageItemCollector::new(tcx, resolver);
250
251    // Collect lang items in other crates.
252    for &cnum in tcx.used_crates(()).iter() {
253        for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
254            collector.collect_item(lang_item, def_id, None);
255        }
256    }
257
258    // Collect lang items local to this crate.
259    visit::Visitor::visit_crate(&mut collector, krate);
260
261    // Find all required but not-yet-defined lang items.
262    weak_lang_items::check_crate(tcx, &mut collector.items);
263
264    // Return all the lang items that were found.
265    collector.items
266}
267
268impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
269    fn visit_item(&mut self, i: &'ast ast::Item) {
270        let target = Target::from_ast_item(i);
271
272        self.check_for_lang(
273            target,
274            self.resolver.owners[&i.id].def_id,
275            &i.attrs,
276            i.span,
277            i.opt_generics(),
278        );
279
280        let parent_item = self.parent_item.replace(i);
281        visit::walk_item(self, i);
282        self.parent_item = parent_item;
283    }
284
285    fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) {
286        self.check_for_lang(
287            Target::from_foreign_item_kind(&i.kind),
288            self.resolver.owners[&i.id].def_id,
289            &i.attrs,
290            i.span,
291            None,
292        );
293    }
294
295    fn visit_variant(&mut self, variant: &'ast ast::Variant) {
296        self.check_for_lang(
297            Target::Variant,
298            self.resolver.owners[&self.parent_item.unwrap().id].node_id_to_def_id[&variant.id],
299            &variant.attrs,
300            variant.span,
301            None,
302        );
303    }
304
305    fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) {
306        let target = Target::from_assoc_item_kind(&i.kind, ctxt);
307        let generics = i.opt_generics();
308
309        self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics);
310
311        visit::walk_assoc_item(self, i, ctxt);
312    }
313}
314
315/// Extracts the first `lang = "$name"` out of a list of attributes.
316/// The `#[panic_handler]` attribute is also extracted out when found.
317///
318/// This function is used for `ast::Attribute`, for `hir::Attribute` use the `find_attr!` macro with `AttributeKind::Lang`
319pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> {
320    attrs.iter().find_map(|attr| {
321        Some(match attr {
322            _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
323            _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
324            _ => return None,
325        })
326    })
327}
328
329pub(crate) fn provide(providers: &mut Providers) {
330    providers.get_lang_items = get_lang_items;
331}