Skip to main content

rustc_resolve/late/
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_attr_ir::target::Target;
12use rustc_attr_ir::{GenericRequirement, LangItem, LanguageItems};
13use rustc_crate_store::ExternCrate;
14use rustc_hir::def_id::{DefId, LocalDefId};
15use rustc_middle::ty::TyCtxt;
16use rustc_span::{Span, sym};
17use rustc_structures::CrateType;
18
19use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget};
20use crate::late::{LateResolutionVisitor, RibKind};
21
22pub(crate) enum Duplicate {
23    Plain,
24    Crate,
25    CrateDepends,
26}
27
28impl<'tcx> LateResolutionVisitor<'_, '_, '_, 'tcx> {
29    pub(super) fn check_for_lang(
30        &mut self,
31        actual_target: Target,
32        def_id: LocalDefId,
33        attrs: &[ast::Attribute],
34        item_span: Span,
35        generics: Option<&ast::Generics>,
36    ) {
37        let attrs = attrs.iter().find_map(|attr| {
38            Some(match attr {
39                _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span),
40                _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span),
41                _ => return None,
42            })
43        });
44        if let Some((name, attr_span)) = attrs {
45            match LangItem::from_name(name) {
46                // Known lang item
47                Some(lang_item) => {
48                    if actual_target != lang_item.target() {
49                        // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast
50                        // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`.
51                        if !(lang_item.is_weak()
52                            && actual_target == Target::Fn
53                            && lang_item.target() == Target::ForeignFn
54                            && #[allow(non_exhaustive_omitted_patterns)] match lang_item {
    LangItem::PanicImpl => true,
    _ => false,
}matches!(lang_item, LangItem::PanicImpl))
55                        {
56                            self.r.tcx
57                            .dcx()
58                            .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()));
59                            return;
60                        }
61                    }
62                    // Weak lang items are handled separately
63                    if lang_item.is_weak() && actual_target == Target::ForeignFn {
64                        self.r.lang_items.missing.push(lang_item);
65                    } else {
66                        // Weak only lang items are always handled here
67                        self.collect_item_extended(
68                            lang_item,
69                            def_id,
70                            item_span,
71                            attr_span,
72                            generics,
73                            actual_target,
74                        );
75                    }
76                }
77                // Unknown lang item.
78                _ => {
79                    self.r.tcx.dcx().delayed_bug("unknown lang item");
80                }
81            }
82        }
83    }
84
85    // Like collect_item() above, but also checks whether the lang item is declared
86    // with the right number of generic arguments.
87    fn collect_item_extended(
88        &mut self,
89        lang_item: LangItem,
90        item_def_id: LocalDefId,
91        item_span: Span,
92        attr_span: Span,
93        generics: Option<&ast::Generics>,
94        target: Target,
95    ) {
96        let name = lang_item.name();
97
98        if let Some(generics) = generics {
99            // Now check whether the lang_item has the expected number of generic
100            // arguments. Generally speaking, binary and indexing operations have
101            // one (for the RHS/index), unary operations have none, the closure
102            // traits have one for the argument list, coroutines have one for the
103            // resume argument, and ordering/equality relations have one for the RHS
104            // Some other types like Box and various unsizing-related traits
105            // have minimum requirements.
106
107            // FIXME: This still doesn't count, e.g., elided lifetimes and APITs.
108            let mut actual_num = generics.params.len();
109            if target.is_associated_item() {
110                for rib in self.ribs.type_ns.iter().rev() {
111                    match rib.kind {
112                        RibKind::Item(_, _) => {
113                            actual_num += rib.bindings.len();
114                            break;
115                        }
116                        _ => {}
117                    }
118                }
119            }
120
121            let mut at_least = false;
122            let required = match lang_item.required_generics() {
123                GenericRequirement::Exact(num) if num != actual_num => Some(num),
124                GenericRequirement::Minimum(num) if actual_num < num => {
125                    at_least = true;
126                    Some(num)
127                }
128                // If the number matches, or there is no requirement, handle it normally
129                _ => None,
130            };
131
132            if let Some(num) = required {
133                // We are issuing E0718 "incorrect target" here, because while the
134                // item kind of the target is correct, the target is still wrong
135                // because of the wrong number of generic arguments.
136                self.r.tcx.dcx().emit_err(IncorrectTarget {
137                    span: attr_span,
138                    generics_span: generics.span,
139                    name: name.as_str(),
140                    kind: target.name(),
141                    num,
142                    actual_num,
143                    at_least,
144                });
145
146                // return early to not collect the lang item
147                return;
148            }
149        }
150
151        if self.r.tcx.crate_types().contains(&CrateType::Sdylib) {
152            self.r.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span });
153        }
154
155        collect_item(
156            self.r.tcx,
157            &mut self.r.lang_items,
158            lang_item,
159            item_def_id.to_def_id(),
160            Some(item_span),
161        );
162    }
163}
164
165pub(crate) fn collect_item(
166    tcx: TyCtxt<'_>,
167    lang_items: &mut LanguageItems,
168    lang_item: LangItem,
169    item_def_id: DefId,
170    item_span: Option<Span>,
171) {
172    // Check for duplicates.
173    if let Some(original_def_id) = lang_items.get(lang_item)
174        && original_def_id != item_def_id
175    {
176        let (item_def_id, original_def_id, item_span) = if let Some(original_def_id) =
177            original_def_id.as_local()
178            && !item_def_id.is_local()
179        {
180            // The crate-local definitions are loaded before the upstream crate definitions,
181            // because we only know which crates are being loaded after the resolver is fully done.
182            // In that case we swap the order of the items to report it meaningfully to the user.
183            (original_def_id.to_def_id(), item_def_id, Some(tcx.source_span(original_def_id)))
184        } else {
185            (item_def_id, original_def_id, item_span)
186        };
187        let lang_item_name = lang_item.name();
188        let crate_name = tcx.crate_name(item_def_id.krate);
189        let mut dependency_of = None;
190        let is_local = item_def_id.is_local();
191        let path = if is_local {
192            String::new()
193        } else {
194            tcx.crate_extern_paths(item_def_id.krate)
195                .iter()
196                .map(|p| p.display().to_string())
197                .collect::<Vec<_>>()
198                .join(", ")
199        };
200
201        let mut orig_crate_name = None;
202        let mut orig_dependency_of = None;
203        let orig_is_local = original_def_id.is_local();
204        let orig_path = if orig_is_local {
205            String::new()
206        } else {
207            tcx.crate_extern_paths(original_def_id.krate)
208                .iter()
209                .map(|p| p.display().to_string())
210                .collect::<Vec<_>>()
211                .join(", ")
212        };
213
214        if !original_def_id.is_local() {
215            orig_crate_name = Some(tcx.crate_name(original_def_id.krate));
216            if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) =
217                tcx.extern_crate(original_def_id.krate)
218            {
219                orig_dependency_of = Some(tcx.crate_name(*inner_dependency_of));
220            }
221        }
222
223        let duplicate = if item_span.is_some() {
224            Duplicate::Plain
225        } else {
226            match tcx.extern_crate(item_def_id.krate) {
227                Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => {
228                    dependency_of = Some(tcx.crate_name(*inner_dependency_of));
229                    Duplicate::CrateDepends
230                }
231                _ => Duplicate::Crate,
232            }
233        };
234
235        // When there's a duplicate lang item, something went very wrong and there's no value
236        // in recovering or doing anything. Give the user the one message to let them debug the
237        // mess they created and then wish them farewell.
238        tcx.dcx().emit_fatal(DuplicateLangItem {
239            local_span: item_span,
240            lang_item_name,
241            crate_name,
242            dependency_of,
243            is_local,
244            path,
245            first_defined_span: original_def_id.as_local().map(|did| tcx.source_span(did)),
246            orig_crate_name,
247            orig_dependency_of,
248            orig_is_local,
249            orig_path,
250            duplicate,
251        });
252    } else {
253        // Matched.
254        lang_items.set(lang_item, item_def_id);
255    }
256}