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::num::NonZero;
74
75pub use check::{check_abi, check_abi_fn_ptr};
76use rustc_abi::{ExternAbi, VariantIdx};
77use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
78use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
79use rustc_hir::def_id::{DefId, LocalDefId};
80use rustc_hir::intravisit::Visitor;
81use rustc_index::bit_set::DenseBitSet;
82use rustc_infer::infer::{self, TyCtxtInferExt as _};
83use rustc_infer::traits::ObligationCause;
84use rustc_middle::query::Providers;
85use rustc_middle::ty::error::{ExpectedFound, TypeError};
86use rustc_middle::ty::print::with_types_for_signature;
87use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode};
88use rustc_middle::{bug, span_bug};
89use rustc_session::parse::feature_err;
90use rustc_span::def_id::CRATE_DEF_ID;
91use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym};
92use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
93use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _;
94use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor;
95use rustc_trait_selection::traits::ObligationCtxt;
96use tracing::debug;
97
98use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys;
99use self::region::region_scope_tree;
100use crate::{errors, require_c_abi_if_c_variadic};
101
102pub fn provide(providers: &mut Providers) {
103    wfcheck::provide(providers);
104    *providers = Providers {
105        adt_destructor,
106        adt_async_destructor,
107        region_scope_tree,
108        collect_return_position_impl_trait_in_trait_tys,
109        compare_impl_item: compare_impl_item::compare_impl_item,
110        check_coroutine_obligations: check::check_coroutine_obligations,
111        ..*providers
112    };
113}
114
115fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
116    tcx.calculate_dtor(def_id, always_applicable::check_drop_impl)
117}
118
119fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
120    tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl)
121}
122
123/// Given a `DefId` for an opaque type in return position, find its parent item's return
124/// expressions.
125fn get_owner_return_paths(
126    tcx: TyCtxt<'_>,
127    def_id: LocalDefId,
128) -> Option<(LocalDefId, ReturnsVisitor<'_>)> {
129    let hir_id = tcx.local_def_id_to_hir_id(def_id);
130    let parent_id = tcx.hir_get_parent_item(hir_id).def_id;
131    tcx.hir_node_by_def_id(parent_id).body_id().map(|body_id| {
132        let body = tcx.hir_body(body_id);
133        let mut visitor = ReturnsVisitor::default();
134        visitor.visit_body(body);
135        (parent_id, visitor)
136    })
137}
138
139pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
140    // Only restricted on wasm target for now
141    if !tcx.sess.target.is_like_wasm {
142        return;
143    }
144
145    // If `#[link_section]` is missing, then nothing to verify
146    let Some(link_section) = tcx.codegen_fn_attrs(id).link_section else {
147        return;
148    };
149
150    // For the wasm32 target statics with `#[link_section]` other than `.init_array`
151    // are placed into custom sections of the final output file, but this isn't like
152    // custom sections of other executable formats. Namely we can only embed a list
153    // of bytes, nothing with provenance (pointers to anything else). If any
154    // provenance show up, reject it here.
155    // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
156    // the consumer's responsibility to ensure all bytes that have been read
157    // have defined values.
158    //
159    // The `.init_array` section is left to go through the normal custom section code path.
160    // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests
161    // in workarounds in user-code.
162    //
163    //   * The linker fails to merge multiple items in a crate into the .init_array section.
164    //     To work around this, a single array can be used placing multiple items in the array.
165    //     #[link_section = ".init_array"]
166    //     static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor];
167    //   * Even symbols marked used get gc'd from dependant crates unless at least one symbol
168    //     in the crate is marked with an `#[export_name]`
169    //
170    //  Once `.init_array` support in wasm-ld is complete, the user code workarounds should
171    //  continue to work, but would no longer be necessary.
172
173    if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
174        && !alloc.inner().provenance().ptrs().is_empty()
175        && !link_section.as_str().starts_with(".init_array")
176    {
177        let msg = "statics with a custom `#[link_section]` must be a \
178                        simple list of bytes on the wasm target with no \
179                        extra levels of indirection such as references";
180        tcx.dcx().span_err(tcx.def_span(id), msg);
181    }
182}
183
184fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) {
185    let span = tcx.def_span(impl_item);
186    let ident = tcx.item_ident(impl_item);
187
188    let err = match tcx.span_of_impl(parent_impl) {
189        Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp },
190        Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname },
191    };
192
193    tcx.dcx().emit_err(err);
194}
195
196fn missing_items_err(
197    tcx: TyCtxt<'_>,
198    impl_def_id: LocalDefId,
199    missing_items: &[ty::AssocItem],
200    full_impl_span: Span,
201) {
202    let missing_items =
203        missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait());
204
205    let missing_items_msg = missing_items
206        .clone()
207        .map(|trait_item| trait_item.name().to_string())
208        .collect::<Vec<_>>()
209        .join("`, `");
210
211    let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span)
212        && snippet.ends_with("}")
213    {
214        // `Span` before impl block closing brace.
215        let hi = full_impl_span.hi() - BytePos(1);
216        // Point at the place right before the closing brace of the relevant `impl` to suggest
217        // adding the associated item at the end of its body.
218        full_impl_span.with_lo(hi).with_hi(hi)
219    } else {
220        full_impl_span.shrink_to_hi()
221    };
222
223    // Obtain the level of indentation ending in `sugg_sp`.
224    let padding =
225        tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
226    let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) =
227        (Vec::new(), Vec::new(), Vec::new());
228
229    for &trait_item in missing_items {
230        let snippet = with_types_for_signature!(suggestion_signature(
231            tcx,
232            trait_item,
233            tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(),
234        ));
235        let code = format!("{padding}{snippet}\n{padding}");
236        if let Some(span) = tcx.hir_span_if_local(trait_item.def_id) {
237            missing_trait_item_label
238                .push(errors::MissingTraitItemLabel { span, item: trait_item.name() });
239            missing_trait_item.push(errors::MissingTraitItemSuggestion {
240                span: sugg_sp,
241                code,
242                snippet,
243            });
244        } else {
245            missing_trait_item_none.push(errors::MissingTraitItemSuggestionNone {
246                span: sugg_sp,
247                code,
248                snippet,
249            })
250        }
251    }
252
253    tcx.dcx().emit_err(errors::MissingTraitItem {
254        span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(),
255        missing_items_msg,
256        missing_trait_item_label,
257        missing_trait_item,
258        missing_trait_item_none,
259    });
260}
261
262fn missing_items_must_implement_one_of_err(
263    tcx: TyCtxt<'_>,
264    impl_span: Span,
265    missing_items: &[Ident],
266    annotation_span: Option<Span>,
267) {
268    let missing_items_msg =
269        missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
270
271    tcx.dcx().emit_err(errors::MissingOneOfTraitItem {
272        span: impl_span,
273        note: annotation_span,
274        missing_items_msg,
275    });
276}
277
278fn default_body_is_unstable(
279    tcx: TyCtxt<'_>,
280    impl_span: Span,
281    item_did: DefId,
282    feature: Symbol,
283    reason: Option<Symbol>,
284    issue: Option<NonZero<u32>>,
285) {
286    let missing_item_name = tcx.item_ident(item_did);
287    let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new());
288    match reason {
289        Some(r) => {
290            some_note = true;
291            reason_str = r.to_string();
292        }
293        None => none_note = true,
294    };
295
296    let mut err = tcx.dcx().create_err(errors::MissingTraitItemUnstable {
297        span: impl_span,
298        some_note,
299        none_note,
300        missing_item_name,
301        feature,
302        reason: reason_str,
303    });
304
305    let inject_span = item_did
306        .as_local()
307        .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
308    rustc_session::parse::add_feature_diagnostics_for_issue(
309        &mut err,
310        &tcx.sess,
311        feature,
312        rustc_feature::GateIssue::Library(issue),
313        false,
314        inject_span,
315    );
316
317    err.emit();
318}
319
320/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
321fn bounds_from_generic_predicates<'tcx>(
322    tcx: TyCtxt<'tcx>,
323    predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
324) -> (String, String) {
325    let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default();
326    let mut projections = vec![];
327    for (predicate, _) in predicates {
328        debug!("predicate {:?}", predicate);
329        let bound_predicate = predicate.kind();
330        match bound_predicate.skip_binder() {
331            ty::ClauseKind::Trait(trait_predicate) => {
332                let entry = types.entry(trait_predicate.self_ty()).or_default();
333                let def_id = trait_predicate.def_id();
334                if !tcx.is_default_trait(def_id) {
335                    // Do not add that restriction to the list if it is a positive requirement.
336                    entry.push(trait_predicate.def_id());
337                }
338            }
339            ty::ClauseKind::Projection(projection_pred) => {
340                projections.push(bound_predicate.rebind(projection_pred));
341            }
342            _ => {}
343        }
344    }
345
346    let mut where_clauses = vec![];
347    let mut types_str = vec![];
348    for (ty, bounds) in types {
349        if let ty::Param(_) = ty.kind() {
350            let mut bounds_str = vec![];
351            for bound in bounds {
352                let mut projections_str = vec![];
353                for projection in &projections {
354                    let p = projection.skip_binder();
355                    if bound == tcx.parent(p.projection_term.def_id)
356                        && p.projection_term.self_ty() == ty
357                    {
358                        let name = tcx.item_name(p.projection_term.def_id);
359                        projections_str.push(format!("{} = {}", name, p.term));
360                    }
361                }
362                let bound_def_path = tcx.def_path_str(bound);
363                if projections_str.is_empty() {
364                    where_clauses.push(format!("{}: {}", ty, bound_def_path));
365                } else {
366                    bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", ")));
367                }
368            }
369            if bounds_str.is_empty() {
370                types_str.push(ty.to_string());
371            } else {
372                types_str.push(format!("{}: {}", ty, bounds_str.join(" + ")));
373            }
374        } else {
375            // Avoid suggesting the following:
376            // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
377            where_clauses.extend(
378                bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))),
379            );
380        }
381    }
382
383    let generics =
384        if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) };
385
386    let where_clauses = if where_clauses.is_empty() {
387        "".to_string()
388    } else {
389        format!(" where {}", where_clauses.join(", "))
390    };
391
392    (generics, where_clauses)
393}
394
395/// Return placeholder code for the given function.
396fn fn_sig_suggestion<'tcx>(
397    tcx: TyCtxt<'tcx>,
398    sig: ty::FnSig<'tcx>,
399    ident: Ident,
400    predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
401    assoc: ty::AssocItem,
402) -> String {
403    let args = sig
404        .inputs()
405        .iter()
406        .enumerate()
407        .map(|(i, ty)| {
408            Some(match ty.kind() {
409                ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(),
410                ty::Ref(reg, ref_ty, mutability) if i == 0 => {
411                    let reg = format!("{reg} ");
412                    let reg = match &reg[..] {
413                        "'_ " | " " => "",
414                        reg => reg,
415                    };
416                    if assoc.is_method() {
417                        match ref_ty.kind() {
418                            ty::Param(param) if param.name == kw::SelfUpper => {
419                                format!("&{}{}self", reg, mutability.prefix_str())
420                            }
421
422                            _ => format!("self: {ty}"),
423                        }
424                    } else {
425                        format!("_: {ty}")
426                    }
427                }
428                _ => {
429                    if assoc.is_method() && i == 0 {
430                        format!("self: {ty}")
431                    } else {
432                        format!("_: {ty}")
433                    }
434                }
435            })
436        })
437        .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None }))
438        .flatten()
439        .collect::<Vec<String>>()
440        .join(", ");
441    let mut output = sig.output();
442
443    let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
444        output = if let ty::Alias(_, alias_ty) = *output.kind()
445            && let Some(output) = tcx
446                .explicit_item_self_bounds(alias_ty.def_id)
447                .iter_instantiated_copied(tcx, alias_ty.args)
448                .find_map(|(bound, _)| {
449                    bound.as_projection_clause()?.no_bound_vars()?.term.as_type()
450                }) {
451            output
452        } else {
453            span_bug!(
454                ident.span,
455                "expected async fn to have `impl Future` output, but it returns {output}"
456            )
457        };
458        "async "
459    } else {
460        ""
461    };
462
463    let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
464
465    let safety = sig.safety.prefix_str();
466    let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
467
468    // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
469    // not be present in the `fn` definition, not will we account for renamed
470    // lifetimes between the `impl` and the `trait`, but this should be good enough to
471    // fill in a significant portion of the missing code, and other subsequent
472    // suggestions can help the user fix the code.
473    format!("{safety}{asyncness}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
474}
475
476/// Return placeholder code for the given associated item.
477/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
478/// structured suggestion.
479fn suggestion_signature<'tcx>(
480    tcx: TyCtxt<'tcx>,
481    assoc: ty::AssocItem,
482    impl_trait_ref: ty::TraitRef<'tcx>,
483) -> String {
484    let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id).rebase_onto(
485        tcx,
486        assoc.container_id(tcx),
487        impl_trait_ref.with_self_ty(tcx, tcx.types.self_param).args,
488    );
489
490    match assoc.kind {
491        ty::AssocKind::Fn { .. } => fn_sig_suggestion(
492            tcx,
493            tcx.liberate_late_bound_regions(
494                assoc.def_id,
495                tcx.fn_sig(assoc.def_id).instantiate(tcx, args),
496            ),
497            assoc.ident(tcx),
498            tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args),
499            assoc,
500        ),
501        ty::AssocKind::Type { .. } => {
502            let (generics, where_clauses) = bounds_from_generic_predicates(
503                tcx,
504                tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args),
505            );
506            format!("type {}{generics} = /* Type */{where_clauses};", assoc.name())
507        }
508        ty::AssocKind::Const { name } => {
509            let ty = tcx.type_of(assoc.def_id).instantiate_identity();
510            let val = tcx
511                .infer_ctxt()
512                .build(TypingMode::non_body_analysis())
513                .err_ctxt()
514                .ty_kind_suggestion(tcx.param_env(assoc.def_id), ty)
515                .unwrap_or_else(|| "value".to_string());
516            format!("const {}: {} = {};", name, ty, val)
517        }
518    }
519}
520
521/// Emit an error when encountering two or more variants in a transparent enum.
522fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) {
523    let variant_spans: Vec<_> = adt
524        .variants()
525        .iter()
526        .map(|variant| tcx.hir_span_if_local(variant.def_id).unwrap())
527        .collect();
528    let (mut spans, mut many) = (Vec::new(), None);
529    if let [start @ .., end] = &*variant_spans {
530        spans = start.to_vec();
531        many = Some(*end);
532    }
533    tcx.dcx().emit_err(errors::TransparentEnumVariant {
534        span: sp,
535        spans,
536        many,
537        number: adt.variants().len(),
538        path: tcx.def_path_str(did),
539    });
540}
541
542/// Emit an error when encountering two or more non-zero-sized fields in a transparent
543/// enum.
544fn bad_non_zero_sized_fields<'tcx>(
545    tcx: TyCtxt<'tcx>,
546    adt: ty::AdtDef<'tcx>,
547    field_count: usize,
548    field_spans: impl Iterator<Item = Span>,
549    sp: Span,
550) {
551    if adt.is_enum() {
552        tcx.dcx().emit_err(errors::TransparentNonZeroSizedEnum {
553            span: sp,
554            spans: field_spans.collect(),
555            field_count,
556            desc: adt.descr(),
557        });
558    } else {
559        tcx.dcx().emit_err(errors::TransparentNonZeroSized {
560            span: sp,
561            spans: field_spans.collect(),
562            field_count,
563            desc: adt.descr(),
564        });
565    }
566}
567
568// FIXME: Consider moving this method to a more fitting place.
569pub fn potentially_plural_count(count: usize, word: &str) -> String {
570    format!("{} {}{}", count, word, pluralize!(count))
571}
572
573pub fn check_function_signature<'tcx>(
574    tcx: TyCtxt<'tcx>,
575    mut cause: ObligationCause<'tcx>,
576    fn_id: DefId,
577    expected_sig: ty::PolyFnSig<'tcx>,
578) -> Result<(), ErrorGuaranteed> {
579    fn extract_span_for_error_reporting<'tcx>(
580        tcx: TyCtxt<'tcx>,
581        err: TypeError<'_>,
582        cause: &ObligationCause<'tcx>,
583        fn_id: LocalDefId,
584    ) -> rustc_span::Span {
585        let mut args = {
586            let node = tcx.expect_hir_owner_node(fn_id);
587            let decl = node.fn_decl().unwrap_or_else(|| bug!("expected fn decl, found {:?}", node));
588            decl.inputs.iter().map(|t| t.span).chain(std::iter::once(decl.output.span()))
589        };
590
591        match err {
592            TypeError::ArgumentMutability(i)
593            | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => args.nth(i).unwrap(),
594            _ => cause.span,
595        }
596    }
597
598    let local_id = fn_id.as_local().unwrap_or(CRATE_DEF_ID);
599
600    let param_env = ty::ParamEnv::empty();
601
602    let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
603    let ocx = ObligationCtxt::new_with_diagnostics(infcx);
604
605    let actual_sig = tcx.fn_sig(fn_id).instantiate_identity();
606
607    let norm_cause = ObligationCause::misc(cause.span, local_id);
608    let actual_sig = ocx.normalize(&norm_cause, param_env, actual_sig);
609
610    match ocx.eq(&cause, param_env, expected_sig, actual_sig) {
611        Ok(()) => {
612            let errors = ocx.select_all_or_error();
613            if !errors.is_empty() {
614                return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
615            }
616        }
617        Err(err) => {
618            let err_ctxt = infcx.err_ctxt();
619            if fn_id.is_local() {
620                cause.span = extract_span_for_error_reporting(tcx, err, &cause, local_id);
621            }
622            let failure_code = cause.as_failure_code_diag(err, cause.span, vec![]);
623            let mut diag = tcx.dcx().create_err(failure_code);
624            err_ctxt.note_type_err(
625                &mut diag,
626                &cause,
627                None,
628                Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound {
629                    expected: expected_sig,
630                    found: actual_sig,
631                }))),
632                err,
633                false,
634                None,
635            );
636            return Err(diag.emit());
637        }
638    }
639
640    if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, param_env, []) {
641        return Err(e);
642    }
643
644    Ok(())
645}