rustc_hir_analysis/check/
mod.rs

1/*!
2
3# typeck: check phase
4
5Within the check phase of type check, we check each item one at a time
6(bodies of function expressions are checked as part of the containing
7function). Inference is used to supply types wherever they are unknown.
8
9By far the most complex case is checking the body of a function. This
10can be broken down into several distinct phases:
11
12- gather: creates type variables to represent the type of each local
13  variable and pattern binding.
14
15- main: the main pass does the lion's share of the work: it
16  determines the types of all expressions, resolves
17  methods, checks for most invalid conditions, and so forth. In
18  some cases, where a type is unknown, it may create a type or region
19  variable and use that as the type of an expression.
20
21  In the process of checking, various constraints will be placed on
22  these type variables through the subtyping relationships requested
23  through the `demand` module. The `infer` module is in charge
24  of resolving those constraints.
25
26- regionck: after main is complete, the regionck pass goes over all
27  types looking for regions and making sure that they did not escape
28  into places where they are not in scope. This may also influence the
29  final assignments of the various region variables if there is some
30  flexibility.
31
32- writeback: writes the final types within a function body, replacing
33  type variables with their final inferred types. These final types
34  are written into the `tcx.node_types` table, which should *never* contain
35  any reference to a type variable.
36
37## Intermediate types
38
39While type checking a function, the intermediate types for the
40expressions, blocks, and so forth contained within the function are
41stored in `fcx.node_types` and `fcx.node_args`. These types
42may contain unresolved type variables. After type checking is
43complete, the functions in the writeback module are used to take the
44types from this table, resolve them, and then write them into their
45permanent home in the type context `tcx`.
46
47This means that during inferencing you should use `fcx.write_ty()`
48and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
49nodes within the function.
50
51The types of top-level items, which never contain unbound type
52variables, are stored directly into the `tcx` typeck_results.
53
54N.B., a type variable is not the same thing as a type parameter. A
55type variable is an instance of a type parameter. That is,
56given a generic function `fn foo<T>(t: T)`, while checking the
57function `foo`, the type `ty_param(0)` refers to the type `T`, which
58is treated in abstract. However, when `foo()` is called, `T` will be
59instantiated with a fresh type variable `N`. This variable will
60eventually be resolved to some concrete type (which might itself be
61a type parameter).
62
63*/
64
65pub mod always_applicable;
66mod check;
67mod compare_impl_item;
68mod entry;
69pub mod intrinsic;
70mod region;
71pub mod wfcheck;
72
73use std::borrow::Cow;
74use std::num::NonZero;
75
76pub use check::{check_abi, check_custom_abi};
77use rustc_abi::VariantIdx;
78use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
79use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
80use rustc_hir::LangItem;
81use rustc_hir::def_id::{DefId, LocalDefId};
82use rustc_hir::intravisit::Visitor;
83use rustc_index::bit_set::DenseBitSet;
84use rustc_infer::infer::{self, TyCtxtInferExt as _};
85use rustc_infer::traits::ObligationCause;
86use rustc_middle::query::Providers;
87use rustc_middle::ty::error::{ExpectedFound, TypeError};
88use rustc_middle::ty::print::with_types_for_signature;
89use rustc_middle::ty::{
90    self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, Ty, TyCtxt, TypingMode,
91};
92use rustc_middle::{bug, span_bug};
93use rustc_session::parse::feature_err;
94use rustc_span::def_id::CRATE_DEF_ID;
95use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym};
96use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
97use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _;
98use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor;
99use rustc_trait_selection::traits::ObligationCtxt;
100use tracing::debug;
101
102use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys;
103use self::region::region_scope_tree;
104use crate::{check_c_variadic_abi, errors};
105
106/// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
107pub(super) fn provide(providers: &mut Providers) {
108    *providers = Providers {
109        adt_destructor,
110        adt_async_destructor,
111        region_scope_tree,
112        collect_return_position_impl_trait_in_trait_tys,
113        compare_impl_item: compare_impl_item::compare_impl_item,
114        check_coroutine_obligations: check::check_coroutine_obligations,
115        check_potentially_region_dependent_goals: check::check_potentially_region_dependent_goals,
116        check_type_wf: wfcheck::check_type_wf,
117        check_well_formed: wfcheck::check_well_formed,
118        ..*providers
119    };
120}
121
122fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
123    let dtor = tcx.calculate_dtor(def_id, always_applicable::check_drop_impl);
124    if dtor.is_none() && tcx.features().async_drop() {
125        if let Some(async_dtor) = adt_async_destructor(tcx, def_id) {
126            // When type has AsyncDrop impl, but doesn't have Drop impl, generate error
127            let span = tcx.def_span(async_dtor.impl_did);
128            tcx.dcx().emit_err(errors::AsyncDropWithoutSyncDrop { span });
129        }
130    }
131    dtor
132}
133
134fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
135    tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl)
136}
137
138/// Given a `DefId` for an opaque type in return position, find its parent item's return
139/// expressions.
140fn get_owner_return_paths(
141    tcx: TyCtxt<'_>,
142    def_id: LocalDefId,
143) -> Option<(LocalDefId, ReturnsVisitor<'_>)> {
144    let hir_id = tcx.local_def_id_to_hir_id(def_id);
145    let parent_id = tcx.hir_get_parent_item(hir_id).def_id;
146    tcx.hir_node_by_def_id(parent_id).body_id().map(|body_id| {
147        let body = tcx.hir_body(body_id);
148        let mut visitor = ReturnsVisitor::default();
149        visitor.visit_body(body);
150        (parent_id, visitor)
151    })
152}
153
154pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
155    // Only restricted on wasm target for now
156    if !tcx.sess.target.is_like_wasm {
157        return;
158    }
159
160    // If `#[link_section]` is missing, then nothing to verify
161    let Some(link_section) = tcx.codegen_fn_attrs(id).link_section else {
162        return;
163    };
164
165    // For the wasm32 target statics with `#[link_section]` other than `.init_array`
166    // are placed into custom sections of the final output file, but this isn't like
167    // custom sections of other executable formats. Namely we can only embed a list
168    // of bytes, nothing with provenance (pointers to anything else). If any
169    // provenance show up, reject it here.
170    // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
171    // the consumer's responsibility to ensure all bytes that have been read
172    // have defined values.
173    //
174    // The `.init_array` section is left to go through the normal custom section code path.
175    // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests
176    // in workarounds in user-code.
177    //
178    //   * The linker fails to merge multiple items in a crate into the .init_array section.
179    //     To work around this, a single array can be used placing multiple items in the array.
180    //     #[link_section = ".init_array"]
181    //     static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor];
182    //   * Even symbols marked used get gc'd from dependant crates unless at least one symbol
183    //     in the crate is marked with an `#[export_name]`
184    //
185    //  Once `.init_array` support in wasm-ld is complete, the user code workarounds should
186    //  continue to work, but would no longer be necessary.
187
188    if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
189        && !alloc.inner().provenance().ptrs().is_empty()
190        && !link_section.as_str().starts_with(".init_array")
191    {
192        let msg = "statics with a custom `#[link_section]` must be a \
193                        simple list of bytes on the wasm target with no \
194                        extra levels of indirection such as references";
195        tcx.dcx().span_err(tcx.def_span(id), msg);
196    }
197}
198
199fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) {
200    let span = tcx.def_span(impl_item);
201    let ident = tcx.item_ident(impl_item);
202
203    let err = match tcx.span_of_impl(parent_impl) {
204        Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp },
205        Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname },
206    };
207
208    tcx.dcx().emit_err(err);
209}
210
211fn missing_items_err(
212    tcx: TyCtxt<'_>,
213    impl_def_id: LocalDefId,
214    missing_items: &[ty::AssocItem],
215    full_impl_span: Span,
216) {
217    let missing_items =
218        missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait());
219
220    let missing_items_msg = missing_items
221        .clone()
222        .map(|trait_item| trait_item.name().to_string())
223        .collect::<Vec<_>>()
224        .join("`, `");
225
226    let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span)
227        && snippet.ends_with("}")
228    {
229        // `Span` before impl block closing brace.
230        let hi = full_impl_span.hi() - BytePos(1);
231        // Point at the place right before the closing brace of the relevant `impl` to suggest
232        // adding the associated item at the end of its body.
233        full_impl_span.with_lo(hi).with_hi(hi)
234    } else {
235        full_impl_span.shrink_to_hi()
236    };
237
238    // Obtain the level of indentation ending in `sugg_sp`.
239    let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new);
240    let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) =
241        (Vec::new(), Vec::new(), Vec::new());
242
243    for &trait_item in missing_items {
244        let snippet = with_types_for_signature!(suggestion_signature(
245            tcx,
246            trait_item,
247            tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(),
248        ));
249        let code = format!("{padding}{snippet}\n{padding}");
250        if let Some(span) = tcx.hir_span_if_local(trait_item.def_id) {
251            missing_trait_item_label
252                .push(errors::MissingTraitItemLabel { span, item: trait_item.name() });
253            missing_trait_item.push(errors::MissingTraitItemSuggestion {
254                span: sugg_sp,
255                code,
256                snippet,
257            });
258        } else {
259            missing_trait_item_none.push(errors::MissingTraitItemSuggestionNone {
260                span: sugg_sp,
261                code,
262                snippet,
263            })
264        }
265    }
266
267    tcx.dcx().emit_err(errors::MissingTraitItem {
268        span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(),
269        missing_items_msg,
270        missing_trait_item_label,
271        missing_trait_item,
272        missing_trait_item_none,
273    });
274}
275
276fn missing_items_must_implement_one_of_err(
277    tcx: TyCtxt<'_>,
278    impl_span: Span,
279    missing_items: &[Ident],
280    annotation_span: Option<Span>,
281) {
282    let missing_items_msg =
283        missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
284
285    tcx.dcx().emit_err(errors::MissingOneOfTraitItem {
286        span: impl_span,
287        note: annotation_span,
288        missing_items_msg,
289    });
290}
291
292fn default_body_is_unstable(
293    tcx: TyCtxt<'_>,
294    impl_span: Span,
295    item_did: DefId,
296    feature: Symbol,
297    reason: Option<Symbol>,
298    issue: Option<NonZero<u32>>,
299) {
300    let missing_item_name = tcx.item_ident(item_did);
301    let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new());
302    match reason {
303        Some(r) => {
304            some_note = true;
305            reason_str = r.to_string();
306        }
307        None => none_note = true,
308    };
309
310    let mut err = tcx.dcx().create_err(errors::MissingTraitItemUnstable {
311        span: impl_span,
312        some_note,
313        none_note,
314        missing_item_name,
315        feature,
316        reason: reason_str,
317    });
318
319    let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span());
320    rustc_session::parse::add_feature_diagnostics_for_issue(
321        &mut err,
322        &tcx.sess,
323        feature,
324        rustc_feature::GateIssue::Library(issue),
325        false,
326        inject_span,
327    );
328
329    err.emit();
330}
331
332/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
333fn bounds_from_generic_predicates<'tcx>(
334    tcx: TyCtxt<'tcx>,
335    predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
336    assoc: ty::AssocItem,
337) -> (String, String) {
338    let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default();
339    let mut regions: FxIndexMap<Region<'tcx>, Vec<Region<'tcx>>> = FxIndexMap::default();
340    let mut projections = vec![];
341    for (predicate, _) in predicates {
342        debug!("predicate {:?}", predicate);
343        let bound_predicate = predicate.kind();
344        match bound_predicate.skip_binder() {
345            ty::ClauseKind::Trait(trait_predicate) => {
346                let entry = types.entry(trait_predicate.self_ty()).or_default();
347                let def_id = trait_predicate.def_id();
348                if !tcx.is_default_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) {
349                    // Do not add that restriction to the list if it is a positive requirement.
350                    entry.push(trait_predicate.def_id());
351                }
352            }
353            ty::ClauseKind::Projection(projection_pred) => {
354                projections.push(bound_predicate.rebind(projection_pred));
355            }
356            ty::ClauseKind::RegionOutlives(OutlivesPredicate(a, b)) => {
357                regions.entry(a).or_default().push(b);
358            }
359            _ => {}
360        }
361    }
362
363    let mut where_clauses = vec![];
364    let generics = tcx.generics_of(assoc.def_id);
365    let params = generics
366        .own_params
367        .iter()
368        .filter(|p| !p.kind.is_synthetic())
369        .map(|p| match tcx.mk_param_from_def(p).kind() {
370            ty::GenericArgKind::Type(ty) => {
371                let bounds =
372                    types.get(&ty).map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(Vec::new()));
373                let mut bounds_str = vec![];
374                for bound in bounds.iter().copied() {
375                    let mut projections_str = vec![];
376                    for projection in &projections {
377                        let p = projection.skip_binder();
378                        if bound == tcx.parent(p.projection_term.def_id)
379                            && p.projection_term.self_ty() == ty
380                        {
381                            let name = tcx.item_name(p.projection_term.def_id);
382                            projections_str.push(format!("{} = {}", name, p.term));
383                        }
384                    }
385                    let bound_def_path = if tcx.is_lang_item(bound, LangItem::MetaSized) {
386                        String::from("?Sized")
387                    } else {
388                        tcx.def_path_str(bound)
389                    };
390                    if projections_str.is_empty() {
391                        where_clauses.push(format!("{}: {}", ty, bound_def_path));
392                    } else {
393                        bounds_str.push(format!(
394                            "{}<{}>",
395                            bound_def_path,
396                            projections_str.join(", ")
397                        ));
398                    }
399                }
400                if bounds_str.is_empty() {
401                    ty.to_string()
402                } else {
403                    format!("{}: {}", ty, bounds_str.join(" + "))
404                }
405            }
406            ty::GenericArgKind::Const(ct) => {
407                format!("const {ct}: {}", tcx.type_of(p.def_id).skip_binder())
408            }
409            ty::GenericArgKind::Lifetime(region) => {
410                if let Some(v) = regions.get(&region)
411                    && !v.is_empty()
412                {
413                    format!(
414                        "{region}: {}",
415                        v.into_iter().map(Region::to_string).collect::<Vec<_>>().join(" + ")
416                    )
417                } else {
418                    region.to_string()
419                }
420            }
421        })
422        .collect::<Vec<_>>();
423    for (ty, bounds) in types.into_iter() {
424        if !matches!(ty.kind(), ty::Param(_)) {
425            // Avoid suggesting the following:
426            // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
427            where_clauses.extend(
428                bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))),
429            );
430        }
431    }
432
433    let generics =
434        if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(", ")) };
435
436    let where_clauses = if where_clauses.is_empty() {
437        "".to_string()
438    } else {
439        format!(" where {}", where_clauses.join(", "))
440    };
441
442    (generics, where_clauses)
443}
444
445/// Return placeholder code for the given function.
446fn fn_sig_suggestion<'tcx>(
447    tcx: TyCtxt<'tcx>,
448    sig: ty::FnSig<'tcx>,
449    ident: Ident,
450    predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
451    assoc: ty::AssocItem,
452) -> String {
453    let args = sig
454        .inputs()
455        .iter()
456        .enumerate()
457        .map(|(i, ty)| {
458            Some(match ty.kind() {
459                ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(),
460                ty::Ref(reg, ref_ty, mutability) if i == 0 => {
461                    let reg = format!("{reg} ");
462                    let reg = match &reg[..] {
463                        "'_ " | " " => "",
464                        reg => reg,
465                    };
466                    if assoc.is_method() {
467                        match ref_ty.kind() {
468                            ty::Param(param) if param.name == kw::SelfUpper => {
469                                format!("&{}{}self", reg, mutability.prefix_str())
470                            }
471
472                            _ => format!("self: {ty}"),
473                        }
474                    } else {
475                        format!("_: {ty}")
476                    }
477                }
478                _ => {
479                    if assoc.is_method() && i == 0 {
480                        format!("self: {ty}")
481                    } else {
482                        format!("_: {ty}")
483                    }
484                }
485            })
486        })
487        .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None }))
488        .flatten()
489        .collect::<Vec<String>>()
490        .join(", ");
491    let mut output = sig.output();
492
493    let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
494        output = if let ty::Alias(_, alias_ty) = *output.kind()
495            && let Some(output) = tcx
496                .explicit_item_self_bounds(alias_ty.def_id)
497                .iter_instantiated_copied(tcx, alias_ty.args)
498                .find_map(|(bound, _)| {
499                    bound.as_projection_clause()?.no_bound_vars()?.term.as_type()
500                }) {
501            output
502        } else {
503            span_bug!(
504                ident.span,
505                "expected async fn to have `impl Future` output, but it returns {output}"
506            )
507        };
508        "async "
509    } else {
510        ""
511    };
512
513    let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
514
515    let safety = sig.safety.prefix_str();
516    let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates, assoc);
517
518    // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
519    // not be present in the `fn` definition, nor will we account for renamed
520    // lifetimes between the `impl` and the `trait`, but this should be good enough to
521    // fill in a significant portion of the missing code, and other subsequent
522    // suggestions can help the user fix the code.
523    format!("{safety}{asyncness}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
524}
525
526/// Return placeholder code for the given associated item.
527/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
528/// structured suggestion.
529fn suggestion_signature<'tcx>(
530    tcx: TyCtxt<'tcx>,
531    assoc: ty::AssocItem,
532    impl_trait_ref: ty::TraitRef<'tcx>,
533) -> String {
534    let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id).rebase_onto(
535        tcx,
536        assoc.container_id(tcx),
537        impl_trait_ref.with_replaced_self_ty(tcx, tcx.types.self_param).args,
538    );
539
540    match assoc.kind {
541        ty::AssocKind::Fn { .. } => fn_sig_suggestion(
542            tcx,
543            tcx.liberate_late_bound_regions(
544                assoc.def_id,
545                tcx.fn_sig(assoc.def_id).instantiate(tcx, args),
546            ),
547            assoc.ident(tcx),
548            tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args),
549            assoc,
550        ),
551        ty::AssocKind::Type { .. } => {
552            let (generics, where_clauses) = bounds_from_generic_predicates(
553                tcx,
554                tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args),
555                assoc,
556            );
557            format!("type {}{generics} = /* Type */{where_clauses};", assoc.name())
558        }
559        ty::AssocKind::Const { name } => {
560            let ty = tcx.type_of(assoc.def_id).instantiate_identity();
561            let val = tcx
562                .infer_ctxt()
563                .build(TypingMode::non_body_analysis())
564                .err_ctxt()
565                .ty_kind_suggestion(tcx.param_env(assoc.def_id), ty)
566                .unwrap_or_else(|| "value".to_string());
567            format!("const {}: {} = {};", name, ty, val)
568        }
569    }
570}
571
572/// Emit an error when encountering two or more variants in a transparent enum.
573fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) {
574    let variant_spans: Vec<_> = adt
575        .variants()
576        .iter()
577        .map(|variant| tcx.hir_span_if_local(variant.def_id).unwrap())
578        .collect();
579    let (mut spans, mut many) = (Vec::new(), None);
580    if let [start @ .., end] = &*variant_spans {
581        spans = start.to_vec();
582        many = Some(*end);
583    }
584    tcx.dcx().emit_err(errors::TransparentEnumVariant {
585        span: sp,
586        spans,
587        many,
588        number: adt.variants().len(),
589        path: tcx.def_path_str(did),
590    });
591}
592
593/// Emit an error when encountering two or more non-zero-sized fields in a transparent
594/// enum.
595fn bad_non_zero_sized_fields<'tcx>(
596    tcx: TyCtxt<'tcx>,
597    adt: ty::AdtDef<'tcx>,
598    field_count: usize,
599    field_spans: impl Iterator<Item = Span>,
600    sp: Span,
601) {
602    if adt.is_enum() {
603        tcx.dcx().emit_err(errors::TransparentNonZeroSizedEnum {
604            span: sp,
605            spans: field_spans.collect(),
606            field_count,
607            desc: adt.descr(),
608        });
609    } else {
610        tcx.dcx().emit_err(errors::TransparentNonZeroSized {
611            span: sp,
612            spans: field_spans.collect(),
613            field_count,
614            desc: adt.descr(),
615        });
616    }
617}
618
619// FIXME: Consider moving this method to a more fitting place.
620pub fn potentially_plural_count(count: usize, word: &str) -> String {
621    format!("{} {}{}", count, word, pluralize!(count))
622}
623
624pub fn check_function_signature<'tcx>(
625    tcx: TyCtxt<'tcx>,
626    mut cause: ObligationCause<'tcx>,
627    fn_id: DefId,
628    expected_sig: ty::PolyFnSig<'tcx>,
629) -> Result<(), ErrorGuaranteed> {
630    fn extract_span_for_error_reporting<'tcx>(
631        tcx: TyCtxt<'tcx>,
632        err: TypeError<'_>,
633        cause: &ObligationCause<'tcx>,
634        fn_id: LocalDefId,
635    ) -> rustc_span::Span {
636        let mut args = {
637            let node = tcx.expect_hir_owner_node(fn_id);
638            let decl = node.fn_decl().unwrap_or_else(|| bug!("expected fn decl, found {:?}", node));
639            decl.inputs.iter().map(|t| t.span).chain(std::iter::once(decl.output.span()))
640        };
641
642        match err {
643            TypeError::ArgumentMutability(i)
644            | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => args.nth(i).unwrap(),
645            _ => cause.span,
646        }
647    }
648
649    let local_id = fn_id.as_local().unwrap_or(CRATE_DEF_ID);
650
651    let param_env = ty::ParamEnv::empty();
652
653    let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
654    let ocx = ObligationCtxt::new_with_diagnostics(infcx);
655
656    let actual_sig = tcx.fn_sig(fn_id).instantiate_identity();
657
658    let norm_cause = ObligationCause::misc(cause.span, local_id);
659    let actual_sig = ocx.normalize(&norm_cause, param_env, actual_sig);
660
661    match ocx.eq(&cause, param_env, expected_sig, actual_sig) {
662        Ok(()) => {
663            let errors = ocx.select_all_or_error();
664            if !errors.is_empty() {
665                return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
666            }
667        }
668        Err(err) => {
669            let err_ctxt = infcx.err_ctxt();
670            if fn_id.is_local() {
671                cause.span = extract_span_for_error_reporting(tcx, err, &cause, local_id);
672            }
673            let failure_code = cause.as_failure_code_diag(err, cause.span, vec![]);
674            let mut diag = tcx.dcx().create_err(failure_code);
675            err_ctxt.note_type_err(
676                &mut diag,
677                &cause,
678                None,
679                Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound {
680                    expected: expected_sig,
681                    found: actual_sig,
682                }))),
683                err,
684                false,
685                None,
686            );
687            return Err(diag.emit());
688        }
689    }
690
691    if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, param_env, []) {
692        return Err(e);
693    }
694
695    Ok(())
696}