| /*! | |
| # typeck: check phase | |
| Within the check phase of type check, we check each item one at a time | |
| (bodies of function expressions are checked as part of the containing | |
| function). Inference is used to supply types wherever they are unknown. | |
| By far the most complex case is checking the body of a function. This | |
| can be broken down into several distinct phases: | |
| - gather: creates type variables to represent the type of each local | |
| variable and pattern binding. | |
| - main: the main pass does the lion's share of the work: it | |
| determines the types of all expressions, resolves | |
| methods, checks for most invalid conditions, and so forth. In | |
| some cases, where a type is unknown, it may create a type or region | |
| variable and use that as the type of an expression. | |
| In the process of checking, various constraints will be placed on | |
| these type variables through the subtyping relationships requested | |
| through the `demand` module. The `infer` module is in charge | |
| of resolving those constraints. | |
| - regionck: after main is complete, the regionck pass goes over all | |
| types looking for regions and making sure that they did not escape | |
| into places where they are not in scope. This may also influence the | |
| final assignments of the various region variables if there is some | |
| flexibility. | |
| - writeback: writes the final types within a function body, replacing | |
| type variables with their final inferred types. These final types | |
| are written into the `tcx.node_types` table, which should *never* contain | |
| any reference to a type variable. | |
| ## Intermediate types | |
| While type checking a function, the intermediate types for the | |
| expressions, blocks, and so forth contained within the function are | |
| stored in `fcx.node_types` and `fcx.node_args`. These types | |
| may contain unresolved type variables. After type checking is | |
| complete, the functions in the writeback module are used to take the | |
| types from this table, resolve them, and then write them into their | |
| permanent home in the type context `tcx`. | |
| This means that during inferencing you should use `fcx.write_ty()` | |
| and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of | |
| nodes within the function. | |
| The types of top-level items, which never contain unbound type | |
| variables, are stored directly into the `tcx` typeck_results. | |
| N.B., a type variable is not the same thing as a type parameter. A | |
| type variable is an instance of a type parameter. That is, | |
| given a generic function `fn foo<T>(t: T)`, while checking the | |
| function `foo`, the type `ty_param(0)` refers to the type `T`, which | |
| is treated in abstract. However, when `foo()` is called, `T` will be | |
| instantiated with a fresh type variable `N`. This variable will | |
| eventually be resolved to some concrete type (which might itself be | |
| a type parameter). | |
| */ | |
| pub mod always_applicable; | |
| mod check; | |
| mod compare_eii; | |
| mod compare_impl_item; | |
| mod entry; | |
| pub mod intrinsic; | |
| mod region; | |
| pub mod wfcheck; | |
| use std::borrow::Cow; | |
| use std::num::NonZero; | |
| pub use check::check_abi; | |
| use rustc_abi::VariantIdx; | |
| use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; | |
| use rustc_errors::{ErrorGuaranteed, pluralize, struct_span_code_err}; | |
| use rustc_hir::attrs::lang_items::LangItem; | |
| use rustc_hir::def_id::{DefId, LocalDefId}; | |
| use rustc_hir::intravisit::Visitor; | |
| use rustc_index::bit_set::DenseBitSet; | |
| use rustc_infer::infer::{self, TyCtxtInferExt as _}; | |
| use rustc_infer::traits::{ObligationCause, TraitErrors}; | |
| use rustc_middle::middle::stability::EvalResult; | |
| use rustc_middle::query::Providers; | |
| use rustc_middle::ty::error::{ExpectedFound, TypeError}; | |
| use rustc_middle::ty::print::with_types_for_signature; | |
| use rustc_middle::ty::{ | |
| self, GenericArgs, GenericArgsRef, OutlivesClause, Region, Ty, TyCtxt, TypingMode, | |
| }; | |
| use rustc_session::diagnostics::feature_err; | |
| use rustc_span::def_id::CRATE_DEF_ID; | |
| use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, bug, kw, span_bug}; | |
| use rustc_trait_selection::error_reporting::InferCtxtErrorExt; | |
| use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _; | |
| use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; | |
| use rustc_trait_selection::traits::ObligationCtxt; | |
| use tracing::debug; | |
| use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys; | |
| use self::region::region_scope_tree; | |
| use crate::diagnostics::{ | |
| MissingTraitItemLabel, MissingTraitItemSuggestion, MissingTraitItemSuggestionNone, | |
| MissingTraitItemSuggestionUnstable, | |
| }; | |
| use crate::{check_c_variadic_abi, diagnostics}; | |
| /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`] | |
| pub(super) fn provide(providers: &mut Providers) { | |
| *providers = Providers { | |
| adt_destructor, | |
| adt_async_destructor, | |
| region_scope_tree, | |
| collect_return_position_impl_trait_in_trait_tys, | |
| compare_impl_item: compare_impl_item::compare_impl_item, | |
| check_coroutine_obligations: check::check_coroutine_obligations, | |
| check_potentially_region_dependent_goals: check::check_potentially_region_dependent_goals, | |
| check_type_wf: wfcheck::check_type_wf, | |
| check_well_formed: wfcheck::check_well_formed, | |
| ..*providers | |
| }; | |
| } | |
| fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> { | |
| let dtor = tcx.calculate_dtor( | |
| def_id, | |
| always_applicable::check_drop_impl, | |
| always_applicable::is_impossible_self_ty, | |
| ); | |
| if dtor.is_none() && tcx.features().async_drop() { | |
| if let Some(async_dtor) = adt_async_destructor(tcx, def_id) { | |
| // When type has AsyncDrop impl, but doesn't have Drop impl, generate error | |
| let span = tcx.def_span(async_dtor.impl_did); | |
| tcx.dcx().emit_err(diagnostics::AsyncDropWithoutSyncDrop { span }); | |
| } | |
| } | |
| dtor | |
| } | |
| fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> { | |
| let result = tcx.calculate_async_dtor( | |
| def_id, | |
| always_applicable::check_drop_impl, | |
| always_applicable::is_impossible_self_ty, | |
| ); | |
| // Async drop in libstd/libcore would become insta-stable — catch that mistake. | |
| if result.is_some() && tcx.features().staged_api() { | |
| span_bug!(tcx.def_span(def_id), "don't use async drop in libstd, it becomes insta-stable"); | |
| } | |
| result | |
| } | |
| /// Given a `DefId` for an opaque type in return position, find its parent item's return | |
| /// expressions. | |
| fn get_owner_return_paths( | |
| tcx: TyCtxt<'_>, | |
| def_id: LocalDefId, | |
| ) -> Option<(LocalDefId, ReturnsVisitor<'_>)> { | |
| let hir_id = tcx.local_def_id_to_hir_id(def_id); | |
| let parent_id = tcx.hir_get_parent_item(hir_id).def_id; | |
| tcx.hir_node_by_def_id(parent_id).body_id().map(|body_id| { | |
| let body = tcx.hir_body(body_id); | |
| let mut visitor = ReturnsVisitor::default(); | |
| visitor.visit_body(body); | |
| (parent_id, visitor) | |
| }) | |
| } | |
| pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { | |
| // Only restricted on wasm target for now | |
| if !tcx.sess.target.is_like_wasm { | |
| return; | |
| } | |
| // If `#[link_section]` is missing, then nothing to verify | |
| let Some(link_section) = tcx.codegen_fn_attrs(id).link_section else { | |
| return; | |
| }; | |
| // For the wasm32 target statics with `#[link_section]` other than `.init_array` | |
| // are placed into custom sections of the final output file, but this isn't like | |
| // custom sections of other executable formats. Namely we can only embed a list | |
| // of bytes, nothing with provenance (pointers to anything else). If any | |
| // provenance show up, reject it here. | |
| // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is | |
| // the consumer's responsibility to ensure all bytes that have been read | |
| // have defined values. | |
| // | |
| // The `.init_array` section is left to go through the normal custom section code path. | |
| // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests | |
| // in workarounds in user-code. | |
| // | |
| // * The linker fails to merge multiple items in a crate into the .init_array section. | |
| // To work around this, a single array can be used placing multiple items in the array. | |
| // #[link_section = ".init_array"] | |
| // static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor]; | |
| // * Even symbols marked used get gc'd from dependant crates unless at least one symbol | |
| // in the crate is marked with an `#[export_name]` | |
| // | |
| // Once `.init_array` support in wasm-ld is complete, the user code workarounds should | |
| // continue to work, but would no longer be necessary. | |
| if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) | |
| && !alloc.inner().provenance().ptrs().is_empty() | |
| && !link_section.as_str().starts_with(".init_array") | |
| { | |
| let msg = "statics with a custom `#[link_section]` must be a \ | |
| simple list of bytes on the wasm target with no \ | |
| extra levels of indirection such as references"; | |
| tcx.dcx().span_err(tcx.def_span(id), msg); | |
| } | |
| } | |
| fn impl_suggestion_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { | |
| let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_def_id)); | |
| if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span) | |
| && snippet.ends_with("}") | |
| { | |
| // `Span` before impl block closing brace. | |
| let hi = full_impl_span.hi() - BytePos(1); | |
| // Point at the place right before the closing brace of the relevant `impl` to suggest | |
| // adding the associated item at the end of its body. | |
| full_impl_span.with_lo(hi).with_hi(hi) | |
| } else { | |
| full_impl_span.shrink_to_hi() | |
| } | |
| } | |
| fn missing_items_suggestions( | |
| tcx: TyCtxt<'_>, | |
| impl_def_id: LocalDefId, | |
| missing_items: &[ty::AssocItem], | |
| ) -> ( | |
| String, | |
| Vec<MissingTraitItemSuggestion>, | |
| Vec<MissingTraitItemSuggestionNone>, | |
| Vec<MissingTraitItemSuggestionUnstable>, | |
| Vec<MissingTraitItemLabel>, | |
| ) { | |
| let missing_items = | |
| missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait()); | |
| let missing_items_msg = missing_items | |
| .clone() | |
| .map(|trait_item| trait_item.name().to_string()) | |
| .collect::<Vec<_>>() | |
| .join("`, `"); | |
| let sugg_sp = impl_suggestion_span(tcx, impl_def_id); | |
| // Obtain the level of indentation ending in `sugg_sp`. | |
| let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new); | |
| let ( | |
| mut missing_trait_item, | |
| mut missing_trait_item_none, | |
| mut missing_trait_item_unstable, | |
| mut missing_trait_item_label, | |
| ) = (Vec::new(), Vec::new(), Vec::new(), Vec::new()); | |
| for &trait_item in missing_items { | |
| let snippet = with_types_for_signature!(suggestion_signature( | |
| tcx, | |
| trait_item, | |
| tcx.impl_trait_ref(impl_def_id).instantiate_identity().skip_norm_wip(), | |
| )); | |
| let code = format!("{padding}{snippet}\n{padding}"); | |
| if let Some(span) = tcx.hir_span_if_local(trait_item.def_id) { | |
| missing_trait_item_label | |
| .push(diagnostics::MissingTraitItemLabel { span, item: trait_item.name() }); | |
| missing_trait_item.push(diagnostics::MissingTraitItemSuggestion { | |
| span: sugg_sp, | |
| code, | |
| snippet, | |
| }); | |
| } else { | |
| if let EvalResult::Deny { feature, .. } = | |
| tcx.eval_stability(trait_item.def_id, None, sugg_sp, None) | |
| { | |
| missing_trait_item_unstable.push(diagnostics::MissingTraitItemSuggestionUnstable { | |
| span: sugg_sp, | |
| code, | |
| snippet, | |
| feature, | |
| }); | |
| } else { | |
| missing_trait_item_none.push(diagnostics::MissingTraitItemSuggestionNone { | |
| span: sugg_sp, | |
| code, | |
| snippet, | |
| }); | |
| } | |
| } | |
| } | |
| ( | |
| missing_items_msg, | |
| missing_trait_item, | |
| missing_trait_item_none, | |
| missing_trait_item_unstable, | |
| missing_trait_item_label, | |
| ) | |
| } | |
| fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem]) { | |
| let ( | |
| missing_items_msg, | |
| missing_trait_item, | |
| missing_trait_item_none, | |
| missing_trait_item_unstable, | |
| missing_trait_item_label, | |
| ) = missing_items_suggestions(tcx, impl_def_id, missing_items); | |
| tcx.dcx().emit_err(diagnostics::MissingTraitItem { | |
| span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(), | |
| missing_items_msg, | |
| missing_trait_item_label, | |
| missing_trait_item, | |
| missing_trait_item_none, | |
| missing_trait_item_unstable, | |
| }); | |
| } | |
| fn missing_items_must_implement_one_of_err( | |
| tcx: TyCtxt<'_>, | |
| impl_def_id: LocalDefId, | |
| missing_items: impl Iterator<Item = Symbol>, | |
| annotation_span: Option<Span>, | |
| ) -> ErrorGuaranteed { | |
| // Look up the associated items so we can use them to emit better errors. | |
| let trait_def_id = tcx.impl_trait_id(impl_def_id); | |
| let assoc_items = tcx.associated_items(trait_def_id); | |
| let missing_items = missing_items | |
| .flat_map(|s| assoc_items.filter_by_name_unhygienic_and_kind(s, ty::AssocTag::Fn)) | |
| .cloned() | |
| .collect::<Vec<_>>(); | |
| let ( | |
| missing_items_msg, | |
| missing_trait_item, | |
| missing_trait_item_none, | |
| missing_trait_item_unstable, | |
| missing_trait_item_label, | |
| ) = missing_items_suggestions(tcx, impl_def_id, &missing_items); | |
| tcx.dcx().emit_err(diagnostics::MissingOneOfTraitItem { | |
| span: tcx.def_span(impl_def_id), | |
| note: annotation_span, | |
| missing_items_msg, | |
| missing_trait_item_label, | |
| missing_trait_item, | |
| missing_trait_item_unstable, | |
| missing_trait_item_none, | |
| }) | |
| } | |
| fn default_body_is_unstable( | |
| tcx: TyCtxt<'_>, | |
| impl_span: Span, | |
| item_did: DefId, | |
| feature: Symbol, | |
| reason: Option<Symbol>, | |
| issue: Option<NonZero<u32>>, | |
| ) { | |
| let missing_item_name = tcx.item_ident(item_did); | |
| let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new()); | |
| match reason { | |
| Some(r) => { | |
| some_note = true; | |
| reason_str = r.to_string(); | |
| } | |
| None => none_note = true, | |
| }; | |
| let mut err = tcx.dcx().create_err(diagnostics::MissingTraitItemUnstable { | |
| span: impl_span, | |
| some_note, | |
| none_note, | |
| missing_item_name, | |
| feature, | |
| reason: reason_str, | |
| }); | |
| let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span()); | |
| rustc_session::diagnostics::add_feature_diagnostics_for_issue( | |
| &mut err, | |
| &tcx.sess, | |
| feature, | |
| rustc_feature::GateIssue::Library(issue), | |
| false, | |
| inject_span, | |
| ); | |
| err.emit(); | |
| } | |
| /// Re-sugar `ty::GenericClauses` in a way suitable to be used in structured suggestions. | |
| fn bounds_from_generic_clauses<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, | |
| assoc: ty::AssocItem, | |
| ) -> (String, String) { | |
| let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default(); | |
| let mut regions: FxIndexMap<Region<'tcx>, Vec<Region<'tcx>>> = FxIndexMap::default(); | |
| let mut projections = vec![]; | |
| for (clause, _) in clauses { | |
| debug!("clause {:?}", clause); | |
| let bound_clause = clause.kind(); | |
| match bound_clause.skip_binder() { | |
| ty::ClauseKind::Trait(trait_predicate) => { | |
| let entry = types.entry(trait_predicate.self_ty()).or_default(); | |
| let def_id = trait_predicate.def_id(); | |
| if !tcx.is_default_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) { | |
| // Do not add that restriction to the list if it is a positive requirement. | |
| entry.push(trait_predicate.def_id()); | |
| } | |
| } | |
| ty::ClauseKind::Projection(projection_pred) => { | |
| projections.push(bound_clause.rebind(projection_pred)); | |
| } | |
| ty::ClauseKind::RegionOutlives(OutlivesClause(a, b)) => { | |
| regions.entry(a).or_default().push(b); | |
| } | |
| _ => {} | |
| } | |
| } | |
| let mut where_clauses = vec![]; | |
| let generics = tcx.generics_of(assoc.def_id); | |
| let params = generics | |
| .own_params | |
| .iter() | |
| .filter(|p| !p.kind.is_synthetic()) | |
| .map(|p| match tcx.mk_param_from_def(p).kind() { | |
| ty::GenericArgKind::Type(ty) => { | |
| let bounds = | |
| types.get(&ty).map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(Vec::new())); | |
| let mut bounds_str = vec![]; | |
| for bound in bounds.iter().copied() { | |
| let mut projections_str = vec![]; | |
| for projection in &projections { | |
| let p = projection.skip_binder(); | |
| if bound == p.projection_term.trait_def_id(tcx) | |
| && p.projection_term.self_ty() == ty | |
| { | |
| let name = tcx.item_name(p.projection_term.expect_projection_def_id()); | |
| projections_str.push(format!("{} = {}", name, p.term)); | |
| } | |
| } | |
| let bound_def_path = if tcx.is_lang_item(bound, LangItem::MetaSized) { | |
| String::from("?Sized") | |
| } else { | |
| tcx.def_path_str(bound) | |
| }; | |
| if projections_str.is_empty() { | |
| where_clauses.push(format!("{}: {}", ty, bound_def_path)); | |
| } else { | |
| bounds_str.push(format!( | |
| "{}<{}>", | |
| bound_def_path, | |
| projections_str.join(", ") | |
| )); | |
| } | |
| } | |
| if bounds_str.is_empty() { | |
| ty.to_string() | |
| } else { | |
| format!("{}: {}", ty, bounds_str.join(" + ")) | |
| } | |
| } | |
| ty::GenericArgKind::Const(ct) => { | |
| format!("const {ct}: {}", tcx.type_of(p.def_id).skip_binder()) | |
| } | |
| ty::GenericArgKind::Lifetime(region) => { | |
| if let Some(v) = regions.get(®ion) | |
| && !v.is_empty() | |
| { | |
| format!( | |
| "{region}: {}", | |
| v.into_iter().map(Region::to_string).collect::<Vec<_>>().join(" + ") | |
| ) | |
| } else { | |
| region.to_string() | |
| } | |
| } | |
| }) | |
| .collect::<Vec<_>>(); | |
| for (ty, bounds) in types.into_iter() { | |
| if !matches!(ty.kind(), ty::Param(_)) { | |
| // Avoid suggesting the following: | |
| // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {} | |
| where_clauses.extend( | |
| bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))), | |
| ); | |
| } | |
| } | |
| let generics = | |
| if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(", ")) }; | |
| let where_clauses = if where_clauses.is_empty() { | |
| "".to_string() | |
| } else { | |
| format!(" where {}", where_clauses.join(", ")) | |
| }; | |
| (generics, where_clauses) | |
| } | |
| /// Return placeholder code for the given function. | |
| fn fn_sig_suggestion<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| sig: ty::FnSig<'tcx>, | |
| ident: Ident, | |
| clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, | |
| assoc: ty::AssocItem, | |
| ) -> String { | |
| let splatted_arg_index = sig.splatted().map(usize::from); | |
| let args = sig | |
| .inputs() | |
| .iter() | |
| .enumerate() | |
| .map(|(i, ty)| { | |
| let splat = if splatted_arg_index == Some(i) { "#[rustc_splat] " } else { "" }; | |
| let arg_ty = match ty.kind() { | |
| ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(), | |
| ty::Ref(reg, ref_ty, mutability) if i == 0 => { | |
| let reg = format!("{reg} "); | |
| let reg = match ®[..] { | |
| "'_ " | " " => "", | |
| reg => reg, | |
| }; | |
| if assoc.is_method() { | |
| match ref_ty.kind() { | |
| ty::Param(param) if param.name == kw::SelfUpper => { | |
| format!("&{}{}self", reg, mutability.prefix_str()) | |
| } | |
| _ => format!("self: {ty}"), | |
| } | |
| } else { | |
| format!("_: {ty}") | |
| } | |
| } | |
| _ => { | |
| if assoc.is_method() && i == 0 { | |
| format!("self: {ty}") | |
| } else { | |
| format!("_: {ty}") | |
| } | |
| } | |
| }; | |
| format!("{splat}{arg_ty}") | |
| }) | |
| .chain(if sig.c_variadic() { Some("...".to_string()) } else { None }) | |
| .collect::<Vec<String>>() | |
| .join(", "); | |
| let mut output = sig.output(); | |
| let asyncness = if tcx.asyncness(assoc.def_id).is_async() { | |
| output = tcx.get_impl_future_output_ty(output).unwrap_or_else(|| { | |
| span_bug!( | |
| ident.span, | |
| "expected async fn to have `impl Future` output, but it returns {output}" | |
| ) | |
| }); | |
| "async " | |
| } else { | |
| "" | |
| }; | |
| let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() }; | |
| let safety = sig.safety().prefix_str(); | |
| let (generics, where_clauses) = bounds_from_generic_clauses(tcx, clauses, assoc); | |
| // FIXME: this is not entirely correct, as the lifetimes from borrowed params will | |
| // not be present in the `fn` definition, nor will we account for renamed | |
| // lifetimes between the `impl` and the `trait`, but this should be good enough to | |
| // fill in a significant portion of the missing code, and other subsequent | |
| // suggestions can help the user fix the code. | |
| // ignore-tidy-todo | |
| format!("{safety}{asyncness}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}") | |
| } | |
| /// Return placeholder code for the given associated item. | |
| /// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a | |
| /// structured suggestion. | |
| fn suggestion_signature<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| assoc: ty::AssocItem, | |
| impl_trait_ref: ty::TraitRef<'tcx>, | |
| ) -> String { | |
| let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id).rebase_onto( | |
| tcx, | |
| assoc.container_id(tcx), | |
| impl_trait_ref.with_replaced_self_ty(tcx, tcx.types.self_param).args, | |
| ); | |
| match assoc.kind { | |
| ty::AssocKind::Fn { .. } => fn_sig_suggestion( | |
| tcx, | |
| tcx.liberate_late_bound_regions( | |
| assoc.def_id, | |
| tcx.fn_sig(assoc.def_id).instantiate(tcx, args).skip_norm_wip(), | |
| ), | |
| assoc.ident(tcx), | |
| tcx.clauses_of(assoc.def_id) | |
| .instantiate_own(tcx, args) | |
| .map(|(c, s)| (c.skip_norm_wip(), s)), | |
| assoc, | |
| ), | |
| ty::AssocKind::Type { .. } => { | |
| let (generics, where_clauses) = bounds_from_generic_clauses( | |
| tcx, | |
| tcx.clauses_of(assoc.def_id) | |
| .instantiate_own(tcx, args) | |
| .map(|(c, s)| (c.skip_norm_wip(), s)), | |
| assoc, | |
| ); | |
| format!("type {}{generics} = /* Type */{where_clauses};", assoc.name()) | |
| } | |
| ty::AssocKind::Const { name, .. } => { | |
| let ty = tcx.type_of(assoc.def_id).instantiate_identity().skip_norm_wip(); | |
| let val = tcx | |
| .infer_ctxt() | |
| .build(TypingMode::non_body_analysis()) | |
| .err_ctxt() | |
| .ty_kind_suggestion(tcx.param_env(assoc.def_id), ty) | |
| .unwrap_or_else(|| "value".to_string()); | |
| format!("const {}: {} = {};", name, ty, val) | |
| } | |
| } | |
| } | |
| /// Emit an error when encountering two or more variants in a transparent enum. | |
| fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) { | |
| let variant_spans: Vec<_> = adt | |
| .variants() | |
| .iter() | |
| .map(|variant| tcx.hir_span_if_local(variant.def_id).unwrap()) | |
| .collect(); | |
| let (mut spans, mut many) = (Vec::new(), None); | |
| if let [start @ .., end] = &*variant_spans { | |
| spans = start.to_vec(); | |
| many = Some(*end); | |
| } | |
| tcx.dcx().emit_err(diagnostics::TransparentEnumVariant { | |
| span: sp, | |
| spans, | |
| many, | |
| number: adt.variants().len(), | |
| path: tcx.def_path_str(did), | |
| }); | |
| } | |
| // FIXME: Consider moving this method to a more fitting place. | |
| pub fn potentially_plural_count(count: usize, word: &str) -> String { | |
| format!("{} {}{}", count, word, pluralize!(count)) | |
| } | |
| pub fn check_function_signature<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| mut cause: ObligationCause<'tcx>, | |
| fn_id: DefId, | |
| expected_sig: ty::PolyFnSig<'tcx>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| fn extract_span_for_error_reporting<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| err: TypeError<'_>, | |
| cause: &ObligationCause<'tcx>, | |
| fn_id: LocalDefId, | |
| ) -> rustc_span::Span { | |
| let mut args = { | |
| let node = tcx.expect_hir_owner_node(fn_id); | |
| let decl = node.fn_decl().unwrap_or_else(|| bug!("expected fn decl, found {:?}", node)); | |
| decl.inputs.iter().map(|t| t.span).chain(std::iter::once(decl.output.span())) | |
| }; | |
| match err { | |
| TypeError::ArgumentMutability(i) | |
| | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => args.nth(i).unwrap(), | |
| _ => cause.span, | |
| } | |
| } | |
| let local_id = fn_id.as_local().unwrap_or(CRATE_DEF_ID); | |
| let param_env = ty::ParamEnv::empty(); | |
| let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); | |
| let ocx = ObligationCtxt::new_with_diagnostics(infcx); | |
| let actual_sig = tcx.fn_sig(fn_id).instantiate_identity(); | |
| let norm_cause = ObligationCause::misc(cause.span, local_id); | |
| let actual_sig = ocx.normalize(&norm_cause, param_env, actual_sig); | |
| match ocx.eq(&cause, param_env, expected_sig, actual_sig) { | |
| Ok(()) => { | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if let TraitErrors::HasErrors(errors) = errors { | |
| return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); | |
| } | |
| } | |
| Err(err) => { | |
| let err_ctxt = infcx.err_ctxt(); | |
| if fn_id.is_local() { | |
| cause.span = extract_span_for_error_reporting(tcx, err, &cause, local_id); | |
| } | |
| let failure_code = cause.as_failure_code_diag(err, cause.span, vec![]); | |
| let mut diag = tcx.dcx().create_err(failure_code); | |
| err_ctxt.note_type_err( | |
| &mut diag, | |
| &cause, | |
| None, | |
| Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound { | |
| expected: expected_sig, | |
| found: actual_sig, | |
| }))), | |
| err, | |
| false, | |
| None, | |
| ); | |
| return Err(diag.emit_err()); | |
| } | |
| } | |
| if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, param_env, []) { | |
| return Err(e); | |
| } | |
| Ok(()) | |
| } |