• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*!
2 
3 # typeck: check phase
4 
5 Within 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
7 function). Inference is used to supply types wherever they are unknown.
8 
9 By far the most complex case is checking the body of a function. This
10 can 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 
39 While type checking a function, the intermediate types for the
40 expressions, blocks, and so forth contained within the function are
41 stored in `fcx.node_types` and `fcx.node_substs`. These types
42 may contain unresolved type variables. After type checking is
43 complete, the functions in the writeback module are used to take the
44 types from this table, resolve them, and then write them into their
45 permanent home in the type context `tcx`.
46 
47 This means that during inferencing you should use `fcx.write_ty()`
48 and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
49 nodes within the function.
50 
51 The types of top-level items, which never contain unbound type
52 variables, are stored directly into the `tcx` typeck_results.
53 
54 N.B., a type variable is not the same thing as a type parameter. A
55 type variable is an instance of a type parameter. That is,
56 given a generic function `fn foo<T>(t: T)`, while checking the
57 function `foo`, the type `ty_param(0)` refers to the type `T`, which
58 is treated in abstract. However, when `foo()` is called, `T` will be
59 substituted for a fresh type variable `N`. This variable will
60 eventually be resolved to some concrete type (which might itself be
61 a type parameter).
62 
63 */
64 
65 mod check;
66 mod compare_impl_item;
67 pub mod dropck;
68 pub mod intrinsic;
69 pub mod intrinsicck;
70 mod region;
71 pub mod wfcheck;
72 
73 pub use check::check_abi;
74 
75 use check::check_mod_item_types;
76 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
77 use rustc_errors::{pluralize, struct_span_err, Diagnostic, DiagnosticBuilder};
78 use rustc_hir::def_id::{DefId, LocalDefId};
79 use rustc_hir::intravisit::Visitor;
80 use rustc_index::bit_set::BitSet;
81 use rustc_middle::query::Providers;
82 use rustc_middle::ty::{self, Ty, TyCtxt};
83 use rustc_middle::ty::{InternalSubsts, SubstsRef};
84 use rustc_session::parse::feature_err;
85 use rustc_span::source_map::DUMMY_SP;
86 use rustc_span::symbol::{kw, Ident};
87 use rustc_span::{self, BytePos, Span, Symbol};
88 use rustc_target::abi::VariantIdx;
89 use rustc_target::spec::abi::Abi;
90 use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
91 use std::num::NonZeroU32;
92 
93 use crate::errors;
94 use crate::require_c_abi_if_c_variadic;
95 use crate::util::common::indenter;
96 
97 use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys;
98 use self::region::region_scope_tree;
99 
provide(providers: &mut Providers)100 pub fn provide(providers: &mut Providers) {
101     wfcheck::provide(providers);
102     *providers = Providers {
103         adt_destructor,
104         check_mod_item_types,
105         region_scope_tree,
106         collect_return_position_impl_trait_in_trait_tys,
107         compare_impl_const: compare_impl_item::compare_impl_const_raw,
108         check_generator_obligations: check::check_generator_obligations,
109         ..*providers
110     };
111 }
112 
adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor>113 fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
114     tcx.calculate_dtor(def_id.to_def_id(), dropck::check_drop_impl)
115 }
116 
117 /// Given a `DefId` for an opaque type in return position, find its parent item's return
118 /// expressions.
get_owner_return_paths( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> Option<(LocalDefId, ReturnsVisitor<'_>)>119 fn get_owner_return_paths(
120     tcx: TyCtxt<'_>,
121     def_id: LocalDefId,
122 ) -> Option<(LocalDefId, ReturnsVisitor<'_>)> {
123     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
124     let parent_id = tcx.hir().get_parent_item(hir_id).def_id;
125     tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| {
126         let body = tcx.hir().body(body_id);
127         let mut visitor = ReturnsVisitor::default();
128         visitor.visit_body(body);
129         (parent_id, visitor)
130     })
131 }
132 
133 /// Forbid defining intrinsics in Rust code,
134 /// as they must always be defined by the compiler.
135 // FIXME: Move this to a more appropriate place.
fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi)136 pub fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
137     if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
138         tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block");
139     }
140 }
141 
maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId)142 fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
143     // Only restricted on wasm target for now
144     if !tcx.sess.target.is_like_wasm {
145         return;
146     }
147 
148     // If `#[link_section]` is missing, then nothing to verify
149     let attrs = tcx.codegen_fn_attrs(id);
150     if attrs.link_section.is_none() {
151         return;
152     }
153 
154     // For the wasm32 target statics with `#[link_section]` are placed into custom
155     // sections of the final output file, but this isn't link custom sections of
156     // other executable formats. Namely we can only embed a list of bytes,
157     // nothing with provenance (pointers to anything else). If any provenance
158     // show up, reject it here.
159     // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
160     // the consumer's responsibility to ensure all bytes that have been read
161     // have defined values.
162     if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
163         && alloc.inner().provenance().ptrs().len() != 0
164     {
165         let msg = "statics with a custom `#[link_section]` must be a \
166                         simple list of bytes on the wasm target with no \
167                         extra levels of indirection such as references";
168         tcx.sess.span_err(tcx.def_span(id), msg);
169     }
170 }
171 
report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId)172 fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) {
173     let span = tcx.def_span(impl_item);
174     let ident = tcx.item_name(impl_item);
175 
176     let err = match tcx.span_of_impl(parent_impl) {
177         Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp },
178         Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname },
179     };
180 
181     tcx.sess.emit_err(err);
182 }
183 
missing_items_err( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem], full_impl_span: Span, )184 fn missing_items_err(
185     tcx: TyCtxt<'_>,
186     impl_def_id: LocalDefId,
187     missing_items: &[ty::AssocItem],
188     full_impl_span: Span,
189 ) {
190     let missing_items =
191         missing_items.iter().filter(|trait_item| trait_item.opt_rpitit_info.is_none());
192 
193     let missing_items_msg = missing_items
194         .clone()
195         .map(|trait_item| trait_item.name.to_string())
196         .collect::<Vec<_>>()
197         .join("`, `");
198 
199     // `Span` before impl block closing brace.
200     let hi = full_impl_span.hi() - BytePos(1);
201     // Point at the place right before the closing brace of the relevant `impl` to suggest
202     // adding the associated item at the end of its body.
203     let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
204     // Obtain the level of indentation ending in `sugg_sp`.
205     let padding =
206         tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
207     let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) =
208         (Vec::new(), Vec::new(), Vec::new());
209 
210     for &trait_item in missing_items {
211         let snippet = suggestion_signature(
212             tcx,
213             trait_item,
214             tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity(),
215         );
216         let code = format!("{}{}\n{}", padding, snippet, padding);
217         if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
218             missing_trait_item_label
219                 .push(errors::MissingTraitItemLabel { span, item: trait_item.name });
220             missing_trait_item.push(errors::MissingTraitItemSuggestion {
221                 span: sugg_sp,
222                 code,
223                 snippet,
224             });
225         } else {
226             missing_trait_item_none.push(errors::MissingTraitItemSuggestionNone {
227                 span: sugg_sp,
228                 code,
229                 snippet,
230             })
231         }
232     }
233 
234     tcx.sess.emit_err(errors::MissingTraitItem {
235         span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(),
236         missing_items_msg,
237         missing_trait_item_label,
238         missing_trait_item,
239         missing_trait_item_none,
240     });
241 }
242 
missing_items_must_implement_one_of_err( tcx: TyCtxt<'_>, impl_span: Span, missing_items: &[Ident], annotation_span: Option<Span>, )243 fn missing_items_must_implement_one_of_err(
244     tcx: TyCtxt<'_>,
245     impl_span: Span,
246     missing_items: &[Ident],
247     annotation_span: Option<Span>,
248 ) {
249     let missing_items_msg =
250         missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
251 
252     tcx.sess.emit_err(errors::MissingOneOfTraitItem {
253         span: impl_span,
254         note: annotation_span,
255         missing_items_msg,
256     });
257 }
258 
default_body_is_unstable( tcx: TyCtxt<'_>, impl_span: Span, item_did: DefId, feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, )259 fn default_body_is_unstable(
260     tcx: TyCtxt<'_>,
261     impl_span: Span,
262     item_did: DefId,
263     feature: Symbol,
264     reason: Option<Symbol>,
265     issue: Option<NonZeroU32>,
266 ) {
267     let missing_item_name = tcx.associated_item(item_did).name;
268     let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new());
269     match reason {
270         Some(r) => {
271             some_note = true;
272             reason_str = r.to_string();
273         }
274         None => none_note = true,
275     };
276 
277     let mut err = tcx.sess.create_err(errors::MissingTraitItemUnstable {
278         span: impl_span,
279         some_note,
280         none_note,
281         missing_item_name,
282         feature,
283         reason: reason_str,
284     });
285 
286     rustc_session::parse::add_feature_diagnostics_for_issue(
287         &mut err,
288         &tcx.sess.parse_sess,
289         feature,
290         rustc_feature::GateIssue::Library(issue),
291     );
292 
293     err.emit();
294 }
295 
296 /// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
bounds_from_generic_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, ) -> (String, String)297 fn bounds_from_generic_predicates<'tcx>(
298     tcx: TyCtxt<'tcx>,
299     predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
300 ) -> (String, String) {
301     let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default();
302     let mut projections = vec![];
303     for (predicate, _) in predicates {
304         debug!("predicate {:?}", predicate);
305         let bound_predicate = predicate.kind();
306         match bound_predicate.skip_binder() {
307             ty::ClauseKind::Trait(trait_predicate) => {
308                 let entry = types.entry(trait_predicate.self_ty()).or_default();
309                 let def_id = trait_predicate.def_id();
310                 if Some(def_id) != tcx.lang_items().sized_trait() {
311                     // Type params are `Sized` by default, do not add that restriction to the list
312                     // if it is a positive requirement.
313                     entry.push(trait_predicate.def_id());
314                 }
315             }
316             ty::ClauseKind::Projection(projection_pred) => {
317                 projections.push(bound_predicate.rebind(projection_pred));
318             }
319             _ => {}
320         }
321     }
322     let generics = if types.is_empty() {
323         "".to_string()
324     } else {
325         format!(
326             "<{}>",
327             types
328                 .keys()
329                 .filter_map(|t| match t.kind() {
330                     ty::Param(_) => Some(t.to_string()),
331                     // Avoid suggesting the following:
332                     // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
333                     _ => None,
334                 })
335                 .collect::<Vec<_>>()
336                 .join(", ")
337         )
338     };
339     let mut where_clauses = vec![];
340     for (ty, bounds) in types {
341         where_clauses
342             .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))));
343     }
344     for projection in &projections {
345         let p = projection.skip_binder();
346         // FIXME: this is not currently supported syntax, we should be looking at the `types` and
347         // insert the associated types where they correspond, but for now let's be "lazy" and
348         // propose this instead of the following valid resugaring:
349         // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
350         where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.def_id), p.term));
351     }
352     let where_clauses = if where_clauses.is_empty() {
353         String::new()
354     } else {
355         format!(" where {}", where_clauses.join(", "))
356     };
357     (generics, where_clauses)
358 }
359 
360 /// Return placeholder code for the given function.
fn_sig_suggestion<'tcx>( tcx: TyCtxt<'tcx>, sig: ty::FnSig<'tcx>, ident: Ident, predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, assoc: ty::AssocItem, ) -> String361 fn fn_sig_suggestion<'tcx>(
362     tcx: TyCtxt<'tcx>,
363     sig: ty::FnSig<'tcx>,
364     ident: Ident,
365     predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
366     assoc: ty::AssocItem,
367 ) -> String {
368     let args = sig
369         .inputs()
370         .iter()
371         .enumerate()
372         .map(|(i, ty)| {
373             Some(match ty.kind() {
374                 ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(),
375                 ty::Ref(reg, ref_ty, mutability) if i == 0 => {
376                     let reg = format!("{reg} ");
377                     let reg = match &reg[..] {
378                         "'_ " | " " => "",
379                         reg => reg,
380                     };
381                     if assoc.fn_has_self_parameter {
382                         match ref_ty.kind() {
383                             ty::Param(param) if param.name == kw::SelfUpper => {
384                                 format!("&{}{}self", reg, mutability.prefix_str())
385                             }
386 
387                             _ => format!("self: {ty}"),
388                         }
389                     } else {
390                         format!("_: {ty}")
391                     }
392                 }
393                 _ => {
394                     if assoc.fn_has_self_parameter && i == 0 {
395                         format!("self: {ty}")
396                     } else {
397                         format!("_: {ty}")
398                     }
399                 }
400             })
401         })
402         .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None }))
403         .flatten()
404         .collect::<Vec<String>>()
405         .join(", ");
406     let mut output = sig.output();
407 
408     let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
409         output = if let ty::Alias(_, alias_ty) = *output.kind() {
410             tcx.explicit_item_bounds(alias_ty.def_id)
411                 .subst_iter_copied(tcx, alias_ty.substs)
412                 .find_map(|(bound, _)| bound.as_projection_clause()?.no_bound_vars()?.term.ty())
413                 .unwrap_or_else(|| {
414                     span_bug!(
415                         ident.span,
416                         "expected async fn to have `impl Future` output, but it returns {output}"
417                     )
418                 })
419         } else {
420             span_bug!(
421                 ident.span,
422                 "expected async fn to have `impl Future` output, but it returns {output}"
423             )
424         };
425         "async "
426     } else {
427         ""
428     };
429 
430     let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
431 
432     let unsafety = sig.unsafety.prefix_str();
433     let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
434 
435     // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
436     // not be present in the `fn` definition, not will we account for renamed
437     // lifetimes between the `impl` and the `trait`, but this should be good enough to
438     // fill in a significant portion of the missing code, and other subsequent
439     // suggestions can help the user fix the code.
440     format!(
441         "{unsafety}{asyncness}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}"
442     )
443 }
444 
ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str>445 pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
446     Some(match ty.kind() {
447         ty::Bool => "true",
448         ty::Char => "'a'",
449         ty::Int(_) | ty::Uint(_) => "42",
450         ty::Float(_) => "3.14159",
451         ty::Error(_) | ty::Never => return None,
452         _ => "value",
453     })
454 }
455 
456 /// Return placeholder code for the given associated item.
457 /// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
458 /// structured suggestion.
suggestion_signature<'tcx>( tcx: TyCtxt<'tcx>, assoc: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) -> String459 fn suggestion_signature<'tcx>(
460     tcx: TyCtxt<'tcx>,
461     assoc: ty::AssocItem,
462     impl_trait_ref: ty::TraitRef<'tcx>,
463 ) -> String {
464     let substs = ty::InternalSubsts::identity_for_item(tcx, assoc.def_id).rebase_onto(
465         tcx,
466         assoc.container_id(tcx),
467         impl_trait_ref.with_self_ty(tcx, tcx.types.self_param).substs,
468     );
469 
470     match assoc.kind {
471         ty::AssocKind::Fn => fn_sig_suggestion(
472             tcx,
473             tcx.liberate_late_bound_regions(
474                 assoc.def_id,
475                 tcx.fn_sig(assoc.def_id).subst(tcx, substs),
476             ),
477             assoc.ident(tcx),
478             tcx.predicates_of(assoc.def_id).instantiate_own(tcx, substs),
479             assoc,
480         ),
481         ty::AssocKind::Type => {
482             let (generics, where_clauses) = bounds_from_generic_predicates(
483                 tcx,
484                 tcx.predicates_of(assoc.def_id).instantiate_own(tcx, substs),
485             );
486             format!("type {}{generics} = /* Type */{where_clauses};", assoc.name)
487         }
488         ty::AssocKind::Const => {
489             let ty = tcx.type_of(assoc.def_id).subst_identity();
490             let val = ty_kind_suggestion(ty).unwrap_or("todo!()");
491             format!("const {}: {} = {};", assoc.name, ty, val)
492         }
493     }
494 }
495 
496 /// Emit an error when encountering two or more variants in a transparent enum.
bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId)497 fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) {
498     let variant_spans: Vec<_> = adt
499         .variants()
500         .iter()
501         .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap())
502         .collect();
503     let (mut spans, mut many) = (Vec::new(), None);
504     if let [start @ .., end] = &*variant_spans {
505         spans = start.to_vec();
506         many = Some(*end);
507     }
508     tcx.sess.emit_err(errors::TransparentEnumVariant {
509         span: sp,
510         spans,
511         many,
512         number: adt.variants().len(),
513         path: tcx.def_path_str(did),
514     });
515 }
516 
517 /// Emit an error when encountering two or more non-zero-sized fields in a transparent
518 /// enum.
bad_non_zero_sized_fields<'tcx>( tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, field_count: usize, field_spans: impl Iterator<Item = Span>, sp: Span, )519 fn bad_non_zero_sized_fields<'tcx>(
520     tcx: TyCtxt<'tcx>,
521     adt: ty::AdtDef<'tcx>,
522     field_count: usize,
523     field_spans: impl Iterator<Item = Span>,
524     sp: Span,
525 ) {
526     if adt.is_enum() {
527         tcx.sess.emit_err(errors::TransparentNonZeroSizedEnum {
528             span: sp,
529             spans: field_spans.collect(),
530             field_count,
531             desc: adt.descr(),
532         });
533     } else {
534         tcx.sess.emit_err(errors::TransparentNonZeroSized {
535             span: sp,
536             spans: field_spans.collect(),
537             field_count,
538             desc: adt.descr(),
539         });
540     }
541 }
542 
543 // FIXME: Consider moving this method to a more fitting place.
potentially_plural_count(count: usize, word: &str) -> String544 pub fn potentially_plural_count(count: usize, word: &str) -> String {
545     format!("{} {}{}", count, word, pluralize!(count))
546 }
547