summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_typeck/src/check/fn_ctxt
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:11:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:12:43 +0000
commitcf94bdc0742c13e2a0cac864c478b8626b266e1b (patch)
tree044670aa50cc5e2b4229aa0b6b3df6676730c0a6 /compiler/rustc_typeck/src/check/fn_ctxt
parentAdding debian version 1.65.0+dfsg1-2. (diff)
downloadrustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.tar.xz
rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.zip
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_typeck/src/check/fn_ctxt')
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs1512
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs383
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs2222
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/mod.rs304
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs1139
5 files changed, 0 insertions, 5560 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
deleted file mode 100644
index a40478db9..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ /dev/null
@@ -1,1512 +0,0 @@
-use crate::astconv::{
- AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
- GenericArgCountResult, IsMethodCall, PathSeg,
-};
-use crate::check::callee::{self, DeferredCallResolution};
-use crate::check::method::{self, MethodCallee, SelfSource};
-use crate::check::rvalue_scopes;
-use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
-
-use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, GenericArg, Node, QPath};
-use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
-use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::{InferOk, InferResult};
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::subst::{
- self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
-};
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef,
- ToPredicate, Ty, UserType,
-};
-use rustc_session::lint;
-use rustc_span::def_id::LocalDefId;
-use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{Span, DUMMY_SP};
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::{
- self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
-};
-
-use std::collections::hash_map::Entry;
-use std::slice;
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- /// Produces warning on the given node, if the current point in the
- /// function is unreachable, and there hasn't been another warning.
- pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
- // FIXME: Combine these two 'if' expressions into one once
- // let chains are implemented
- if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
- // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
- // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
- // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
- if !span.is_desugaring(DesugaringKind::CondTemporary)
- && !span.is_desugaring(DesugaringKind::Async)
- && !orig_span.is_desugaring(DesugaringKind::Await)
- {
- self.diverges.set(Diverges::WarnedAlways);
-
- debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
-
- self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
- let msg = format!("unreachable {}", kind);
- lint.build(&msg)
- .span_label(span, &msg)
- .span_label(
- orig_span,
- custom_note
- .unwrap_or("any code following this expression is unreachable"),
- )
- .emit();
- })
- }
- }
- }
-
- /// Resolves type and const variables in `ty` if possible. Unlike the infcx
- /// version (resolve_vars_if_possible), this version will
- /// also select obligations if it seems useful, in an effort
- /// to get more type information.
- pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
- }
-
- #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)]
- pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
- &self,
- mut ty: Ty<'tcx>,
- mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
- ) -> Ty<'tcx> {
- // No Infer()? Nothing needs doing.
- if !ty.has_infer_types_or_consts() {
- debug!("no inference var, nothing needs doing");
- return ty;
- }
-
- // If `ty` is a type variable, see whether we already know what it is.
- ty = self.resolve_vars_if_possible(ty);
- if !ty.has_infer_types_or_consts() {
- debug!(?ty);
- return ty;
- }
-
- // If not, try resolving pending obligations as much as
- // possible. This can help substantially when there are
- // indirect dependencies that don't seem worth tracking
- // precisely.
- self.select_obligations_where_possible(false, mutate_fulfillment_errors);
- self.resolve_vars_if_possible(ty)
- }
-
- pub(in super::super) fn record_deferred_call_resolution(
- &self,
- closure_def_id: LocalDefId,
- r: DeferredCallResolution<'tcx>,
- ) {
- let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
- deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
- }
-
- pub(in super::super) fn remove_deferred_call_resolutions(
- &self,
- closure_def_id: LocalDefId,
- ) -> Vec<DeferredCallResolution<'tcx>> {
- let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
- deferred_call_resolutions.remove(&closure_def_id).unwrap_or_default()
- }
-
- pub fn tag(&self) -> String {
- format!("{:p}", self)
- }
-
- pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
- self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| {
- span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid))
- })
- }
-
- #[inline]
- pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
- debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag());
- self.typeck_results.borrow_mut().node_types_mut().insert(id, ty);
-
- if ty.references_error() {
- self.has_errors.set(true);
- self.set_tainted_by_errors();
- }
- }
-
- pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
- self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
- }
-
- #[instrument(level = "debug", skip(self))]
- pub(in super::super) fn write_resolution(
- &self,
- hir_id: hir::HirId,
- r: Result<(DefKind, DefId), ErrorGuaranteed>,
- ) {
- self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
- }
-
- #[instrument(level = "debug", skip(self))]
- pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) {
- self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id)));
- self.write_substs(hir_id, method.substs);
-
- // When the method is confirmed, the `method.substs` includes
- // parameters from not just the method, but also the impl of
- // the method -- in particular, the `Self` type will be fully
- // resolved. However, those are not something that the "user
- // specified" -- i.e., those types come from the inferred type
- // of the receiver, not something the user wrote. So when we
- // create the user-substs, we want to replace those earlier
- // types with just the types that the user actually wrote --
- // that is, those that appear on the *method itself*.
- //
- // As an example, if the user wrote something like
- // `foo.bar::<u32>(...)` -- the `Self` type here will be the
- // type of `foo` (possibly adjusted), but we don't want to
- // include that. We want just the `[_, u32]` part.
- if !method.substs.is_empty() {
- let method_generics = self.tcx.generics_of(method.def_id);
- if !method_generics.params.is_empty() {
- let user_type_annotation = self.probe(|_| {
- let user_substs = UserSubsts {
- substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
- let i = param.index as usize;
- if i < method_generics.parent_count {
- self.var_for_def(DUMMY_SP, param)
- } else {
- method.substs[i]
- }
- }),
- user_self_ty: None, // not relevant here
- };
-
- self.canonicalize_user_type_annotation(UserType::TypeOf(
- method.def_id,
- user_substs,
- ))
- });
-
- debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
- self.write_user_type_annotation(hir_id, user_type_annotation);
- }
- }
- }
-
- pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
- if !substs.is_empty() {
- debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag());
-
- self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs);
- }
- }
-
- /// Given the substs that we just converted from the HIR, try to
- /// canonicalize them and store them as user-given substitutions
- /// (i.e., substitutions that must be respected by the NLL check).
- ///
- /// This should be invoked **before any unifications have
- /// occurred**, so that annotations like `Vec<_>` are preserved
- /// properly.
- #[instrument(skip(self), level = "debug")]
- pub fn write_user_type_annotation_from_substs(
- &self,
- hir_id: hir::HirId,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- user_self_ty: Option<UserSelfTy<'tcx>>,
- ) {
- debug!("fcx {}", self.tag());
-
- if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
- let canonicalized = self.canonicalize_user_type_annotation(UserType::TypeOf(
- def_id,
- UserSubsts { substs, user_self_ty },
- ));
- debug!(?canonicalized);
- self.write_user_type_annotation(hir_id, canonicalized);
- }
- }
-
- #[instrument(skip(self), level = "debug")]
- pub fn write_user_type_annotation(
- &self,
- hir_id: hir::HirId,
- canonical_user_type_annotation: CanonicalUserType<'tcx>,
- ) {
- debug!("fcx {}", self.tag());
-
- if !canonical_user_type_annotation.is_identity() {
- self.typeck_results
- .borrow_mut()
- .user_provided_types_mut()
- .insert(hir_id, canonical_user_type_annotation);
- } else {
- debug!("skipping identity substs");
- }
- }
-
- #[instrument(skip(self, expr), level = "debug")]
- pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
- debug!("expr = {:#?}", expr);
-
- if adj.is_empty() {
- return;
- }
-
- for a in &adj {
- if let Adjust::NeverToAny = a.kind {
- if a.target.is_ty_var() {
- self.diverging_type_vars.borrow_mut().insert(a.target);
- debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
- }
- }
- }
-
- let autoborrow_mut = adj.iter().any(|adj| {
- matches!(
- adj,
- &Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
- ..
- }
- )
- });
-
- match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
- Entry::Vacant(entry) => {
- entry.insert(adj);
- }
- Entry::Occupied(mut entry) => {
- debug!(" - composing on top of {:?}", entry.get());
- match (&entry.get()[..], &adj[..]) {
- // Applying any adjustment on top of a NeverToAny
- // is a valid NeverToAny adjustment, because it can't
- // be reached.
- (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
- (
- &[
- Adjustment { kind: Adjust::Deref(_), .. },
- Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
- ],
- &[
- Adjustment { kind: Adjust::Deref(_), .. },
- .., // Any following adjustments are allowed.
- ],
- ) => {
- // A reborrow has no effect before a dereference.
- }
- // FIXME: currently we never try to compose autoderefs
- // and ReifyFnPointer/UnsafeFnPointer, but we could.
- _ => {
- self.tcx.sess.delay_span_bug(
- expr.span,
- &format!(
- "while adjusting {:?}, can't compose {:?} and {:?}",
- expr,
- entry.get(),
- adj
- ),
- );
- }
- }
- *entry.get_mut() = adj;
- }
- }
-
- // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
- // In this case implicit use of `Deref` and `Index` within `<expr>` should
- // instead be `DerefMut` and `IndexMut`, so fix those up.
- if autoborrow_mut {
- self.convert_place_derefs_to_mutable(expr);
- }
- }
-
- /// Basically whenever we are converting from a type scheme into
- /// the fn body space, we always want to normalize associated
- /// types as well. This function combines the two.
- fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs);
- let value = EarlyBinder(value).subst(self.tcx, substs);
- let result = self.normalize_associated_types_in(span, value);
- debug!("instantiate_type_scheme = {:?}", result);
- result
- }
-
- /// As `instantiate_type_scheme`, but for the bounds found in a
- /// generic type scheme.
- pub(in super::super) fn instantiate_bounds(
- &self,
- span: Span,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
- let bounds = self.tcx.predicates_of(def_id);
- let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
- let result = bounds.instantiate(self.tcx, substs);
- let result = self.normalize_associated_types_in(span, result);
- debug!(
- "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
- bounds, substs, result, spans,
- );
- (result, spans)
- }
-
- pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
- }
-
- pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>(
- &self,
- span: Span,
- value: T,
- ) -> InferOk<'tcx, T>
- where
- T: TypeFoldable<'tcx>,
- {
- self.inh.partially_normalize_associated_types_in(
- ObligationCause::misc(span, self.body_id),
- self.param_env,
- value,
- )
- }
-
- pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>(
- &self,
- span: Span,
- value: T,
- opt_input_expr: Option<&hir::Expr<'_>>,
- ) -> InferOk<'tcx, T>
- where
- T: TypeFoldable<'tcx>,
- {
- self.inh.partially_normalize_associated_types_in(
- ObligationCause::new(
- span,
- self.body_id,
- traits::BinOp {
- rhs_span: opt_input_expr.map(|expr| expr.span),
- is_lit: opt_input_expr
- .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
- output_ty: None,
- },
- ),
- self.param_env,
- value,
- )
- }
-
- pub fn require_type_meets(
- &self,
- ty: Ty<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- def_id: DefId,
- ) {
- self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code));
- }
-
- pub fn require_type_is_sized(
- &self,
- ty: Ty<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- ) {
- if !ty.references_error() {
- let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
- self.require_type_meets(ty, span, code, lang_item);
- }
- }
-
- pub fn require_type_is_sized_deferred(
- &self,
- ty: Ty<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- ) {
- if !ty.references_error() {
- self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
- }
- }
-
- pub fn register_bound(
- &self,
- ty: Ty<'tcx>,
- def_id: DefId,
- cause: traits::ObligationCause<'tcx>,
- ) {
- if !ty.references_error() {
- self.fulfillment_cx.borrow_mut().register_bound(
- self,
- self.param_env,
- ty,
- def_id,
- cause,
- );
- }
- }
-
- pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
- let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t);
- self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None));
- t
- }
-
- pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
- let ty = self.to_ty(ast_ty);
- debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
-
- if Self::can_contain_user_lifetime_bounds(ty) {
- let c_ty = self.canonicalize_response(UserType::Ty(ty));
- debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
- self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
- }
-
- ty
- }
-
- pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
- match length {
- &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span),
- hir::ArrayLen::Body(anon_const) => self.to_const(anon_const),
- }
- }
-
- pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> {
- let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
- let span = self.tcx.hir().span(ast_c.hir_id);
- let c = ty::Const::from_anon_const(self.tcx, const_def_id);
- self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
- self.normalize_associated_types_in(span, c)
- }
-
- pub fn const_arg_to_const(
- &self,
- ast_c: &hir::AnonConst,
- param_def_id: DefId,
- ) -> ty::Const<'tcx> {
- let const_def = ty::WithOptConstParam {
- did: self.tcx.hir().local_def_id(ast_c.hir_id),
- const_param_did: Some(param_def_id),
- };
- let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
- self.register_wf_obligation(
- c.into(),
- self.tcx.hir().span(ast_c.hir_id),
- ObligationCauseCode::WellFormed(None),
- );
- c
- }
-
- // If the type given by the user has free regions, save it for later, since
- // NLL would like to enforce those. Also pass in types that involve
- // projections, since those can resolve to `'static` bounds (modulo #54940,
- // which hopefully will be fixed by the time you see this comment, dear
- // reader, although I have my doubts). Also pass in types with inference
- // types, because they may be repeated. Other sorts of things are already
- // sufficiently enforced with erased regions. =)
- fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
- where
- T: TypeVisitable<'tcx>,
- {
- t.has_free_regions() || t.has_projections() || t.has_infer_types()
- }
-
- pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
- match self.typeck_results.borrow().node_types().get(id) {
- Some(&t) => t,
- None if self.is_tainted_by_errors() => self.tcx.ty_error(),
- None => {
- bug!(
- "no type for node {}: {} in fcx {}",
- id,
- self.tcx.hir().node_to_string(id),
- self.tag()
- );
- }
- }
- }
-
- pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> {
- match self.typeck_results.borrow().node_types().get(id) {
- Some(&t) => Some(t),
- None if self.is_tainted_by_errors() => Some(self.tcx.ty_error()),
- None => None,
- }
- }
-
- /// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
- pub fn register_wf_obligation(
- &self,
- arg: subst::GenericArg<'tcx>,
- span: Span,
- code: traits::ObligationCauseCode<'tcx>,
- ) {
- // WF obligations never themselves fail, so no real need to give a detailed cause:
- let cause = traits::ObligationCause::new(span, self.body_id, code);
- self.register_predicate(traits::Obligation::new(
- cause,
- self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx),
- ));
- }
-
- /// Registers obligations that all `substs` are well-formed.
- pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
- for arg in substs.iter().filter(|arg| {
- matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
- }) {
- self.register_wf_obligation(arg, expr.span, traits::WellFormed(None));
- }
- }
-
- // FIXME(arielb1): use this instead of field.ty everywhere
- // Only for fields! Returns <none> for methods>
- // Indifferent to privacy flags
- pub fn field_ty(
- &self,
- span: Span,
- field: &'tcx ty::FieldDef,
- substs: SubstsRef<'tcx>,
- ) -> Ty<'tcx> {
- self.normalize_associated_types_in(span, field.ty(self.tcx, substs))
- }
-
- pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) {
- let scope_tree = self.tcx.region_scope_tree(def_id);
- let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, &scope_tree, def_id) };
- let mut typeck_results = self.inh.typeck_results.borrow_mut();
- typeck_results.rvalue_scopes = rvalue_scopes;
- }
-
- pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
- let mut generators = self.deferred_generator_interiors.borrow_mut();
- for (body_id, interior, kind) in generators.drain(..) {
- self.select_obligations_where_possible(false, |_| {});
- crate::check::generator_interior::resolve_interior(
- self, def_id, body_id, interior, kind,
- );
- }
- }
-
- #[instrument(skip(self), level = "debug")]
- pub(in super::super) fn select_all_obligations_or_error(&self) {
- let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);
-
- if !errors.is_empty() {
- self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
- self.report_fulfillment_errors(&errors, self.inh.body_id, false);
- }
- }
-
- /// Select as many obligations as we can at present.
- pub(in super::super) fn select_obligations_where_possible(
- &self,
- fallback_has_occurred: bool,
- mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
- ) {
- let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
- if !result.is_empty() {
- mutate_fulfillment_errors(&mut result);
- self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
- self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred);
- }
- }
-
- /// For the overloaded place expressions (`*x`, `x[3]`), the trait
- /// returns a type of `&T`, but the actual type we assign to the
- /// *expression* is `T`. So this function just peels off the return
- /// type by one layer to yield `T`.
- pub(in super::super) fn make_overloaded_place_return_type(
- &self,
- method: MethodCallee<'tcx>,
- ) -> ty::TypeAndMut<'tcx> {
- // extract method return type, which will be &T;
- let ret_ty = method.sig.output();
-
- // method returns &T, but the type as visible to user is T, so deref
- ret_ty.builtin_deref(true).unwrap()
- }
-
- #[instrument(skip(self), level = "debug")]
- fn self_type_matches_expected_vid(
- &self,
- trait_ref: ty::PolyTraitRef<'tcx>,
- expected_vid: ty::TyVid,
- ) -> bool {
- let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
- debug!(?self_ty);
-
- match *self_ty.kind() {
- ty::Infer(ty::TyVar(found_vid)) => {
- // FIXME: consider using `sub_root_var` here so we
- // can see through subtyping.
- let found_vid = self.root_var(found_vid);
- debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
- expected_vid == found_vid
- }
- _ => false,
- }
- }
-
- #[instrument(skip(self), level = "debug")]
- pub(in super::super) fn obligations_for_self_ty<'b>(
- &'b self,
- self_ty: ty::TyVid,
- ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
- + Captures<'tcx>
- + 'b {
- // FIXME: consider using `sub_root_var` here so we
- // can see through subtyping.
- let ty_var_root = self.root_var(self_ty);
- trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations());
-
- self.fulfillment_cx
- .borrow()
- .pending_obligations()
- .into_iter()
- .filter_map(move |obligation| {
- let bound_predicate = obligation.predicate.kind();
- match bound_predicate.skip_binder() {
- ty::PredicateKind::Projection(data) => Some((
- bound_predicate.rebind(data).required_poly_trait_ref(self.tcx),
- obligation,
- )),
- ty::PredicateKind::Trait(data) => {
- Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation))
- }
- ty::PredicateKind::Subtype(..) => None,
- ty::PredicateKind::Coerce(..) => None,
- ty::PredicateKind::RegionOutlives(..) => None,
- ty::PredicateKind::TypeOutlives(..) => None,
- ty::PredicateKind::WellFormed(..) => None,
- ty::PredicateKind::ObjectSafe(..) => None,
- ty::PredicateKind::ConstEvaluatable(..) => None,
- ty::PredicateKind::ConstEquate(..) => None,
- // N.B., this predicate is created by breaking down a
- // `ClosureType: FnFoo()` predicate, where
- // `ClosureType` represents some `Closure`. It can't
- // possibly be referring to the current closure,
- // because we haven't produced the `Closure` for
- // this closure yet; this is exactly why the other
- // code is looking for a self type of an unresolved
- // inference variable.
- ty::PredicateKind::ClosureKind(..) => None,
- ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
- }
- })
- .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
- }
-
- pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
- self.obligations_for_self_ty(self_ty)
- .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait())
- }
-
- pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
- vec![self.tcx.ty_error(); len]
- }
-
- /// Unifies the output type with the expected type early, for more coercions
- /// and forward type information on the input expressions.
- #[instrument(skip(self, call_span), level = "debug")]
- pub(in super::super) fn expected_inputs_for_expected_output(
- &self,
- call_span: Span,
- expected_ret: Expectation<'tcx>,
- formal_ret: Ty<'tcx>,
- formal_args: &[Ty<'tcx>],
- ) -> Option<Vec<Ty<'tcx>>> {
- let formal_ret = self.resolve_vars_with_obligations(formal_ret);
- let ret_ty = expected_ret.only_has_type(self)?;
-
- // HACK(oli-obk): This is a hack to keep RPIT and TAIT in sync wrt their behaviour.
- // Without it, the inference
- // variable will get instantiated with the opaque type. The inference variable often
- // has various helpful obligations registered for it that help closures figure out their
- // signature. If we infer the inference var to the opaque type, the closure won't be able
- // to find those obligations anymore, and it can't necessarily find them from the opaque
- // type itself. We could be more powerful with inference if we *combined* the obligations
- // so that we got both the obligations from the opaque type and the ones from the inference
- // variable. That will accept more code than we do right now, so we need to carefully consider
- // the implications.
- // Note: this check is pessimistic, as the inference type could be matched with something other
- // than the opaque type, but then we need a new `TypeRelation` just for this specific case and
- // can't re-use `sup` below.
- // See src/test/ui/impl-trait/hidden-type-is-opaque.rs and
- // src/test/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path.
- if formal_ret.has_infer_types() {
- for ty in ret_ty.walk() {
- if let ty::subst::GenericArgKind::Type(ty) = ty.unpack()
- && let ty::Opaque(def_id, _) = *ty.kind()
- && let Some(def_id) = def_id.as_local()
- && self.opaque_type_origin(def_id, DUMMY_SP).is_some() {
- return None;
- }
- }
- }
-
- let expect_args = self
- .fudge_inference_if_ok(|| {
- // Attempt to apply a subtyping relationship between the formal
- // return type (likely containing type variables if the function
- // is polymorphic) and the expected return type.
- // No argument expectations are produced if unification fails.
- let origin = self.misc(call_span);
- let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret);
-
- // FIXME(#27336) can't use ? here, Try::from_error doesn't default
- // to identity so the resulting type is not constrained.
- match ures {
- Ok(ok) => {
- // Process any obligations locally as much as
- // we can. We don't care if some things turn
- // out unconstrained or ambiguous, as we're
- // just trying to get hints here.
- let errors = self.save_and_restore_in_snapshot_flag(|_| {
- let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
- for obligation in ok.obligations {
- fulfill.register_predicate_obligation(self, obligation);
- }
- fulfill.select_where_possible(self)
- });
-
- if !errors.is_empty() {
- return Err(());
- }
- }
- Err(_) => return Err(()),
- }
-
- // Record all the argument types, with the substitutions
- // produced from the above subtyping unification.
- Ok(Some(formal_args.iter().map(|&ty| self.resolve_vars_if_possible(ty)).collect()))
- })
- .unwrap_or_default();
- debug!(?formal_args, ?formal_ret, ?expect_args, ?expected_ret);
- expect_args
- }
-
- pub(in super::super) fn resolve_lang_item_path(
- &self,
- lang_item: hir::LangItem,
- span: Span,
- hir_id: hir::HirId,
- expr_hir_id: Option<hir::HirId>,
- ) -> (Res, Ty<'tcx>) {
- let def_id = self.tcx.require_lang_item(lang_item, Some(span));
- let def_kind = self.tcx.def_kind(def_id);
-
- let item_ty = if let DefKind::Variant = def_kind {
- self.tcx.bound_type_of(self.tcx.parent(def_id))
- } else {
- self.tcx.bound_type_of(def_id)
- };
- let substs = self.fresh_substs_for_item(span, def_id);
- let ty = item_ty.subst(self.tcx, substs);
-
- self.write_resolution(hir_id, Ok((def_kind, def_id)));
-
- let code = match lang_item {
- hir::LangItem::IntoFutureIntoFuture => {
- Some(ObligationCauseCode::AwaitableExpr(expr_hir_id))
- }
- hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => {
- Some(ObligationCauseCode::ForLoopIterator)
- }
- hir::LangItem::TryTraitFromOutput
- | hir::LangItem::TryTraitFromResidual
- | hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark),
- _ => None,
- };
- if let Some(code) = code {
- self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone());
- } else {
- self.add_required_obligations_for_hir(span, def_id, substs, hir_id);
- }
-
- (Res::Def(def_kind, def_id), ty)
- }
-
- /// Resolves an associated value path into a base type and associated constant, or method
- /// resolution. The newly resolved definition is written into `type_dependent_defs`.
- pub fn resolve_ty_and_res_fully_qualified_call(
- &self,
- qpath: &'tcx QPath<'tcx>,
- hir_id: hir::HirId,
- span: Span,
- ) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
- debug!(
- "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
- qpath, hir_id, span
- );
- let (ty, qself, item_segment) = match *qpath {
- QPath::Resolved(ref opt_qself, ref path) => {
- return (
- path.res,
- opt_qself.as_ref().map(|qself| self.to_ty(qself)),
- path.segments,
- );
- }
- QPath::TypeRelative(ref qself, ref segment) => {
- // Don't use `self.to_ty`, since this will register a WF obligation.
- // If we're trying to call a non-existent method on a trait
- // (e.g. `MyTrait::missing_method`), then resolution will
- // give us a `QPath::TypeRelative` with a trait object as
- // `qself`. In that case, we want to avoid registering a WF obligation
- // for `dyn MyTrait`, since we don't actually need the trait
- // to be object-safe.
- // We manually call `register_wf_obligation` in the success path
- // below.
- (<dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself), qself, segment)
- }
- QPath::LangItem(..) => {
- bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`")
- }
- };
- if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
- {
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
- // Return directly on cache hit. This is useful to avoid doubly reporting
- // errors with default match binding modes. See #44614.
- let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id));
- return (def, Some(ty), slice::from_ref(&**item_segment));
- }
- let item_name = item_segment.ident;
- let result = self
- .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id)
- .or_else(|error| {
- let result = match error {
- method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
- _ => Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()),
- };
-
- // If we have a path like `MyTrait::missing_method`, then don't register
- // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise,
- // register a WF obligation so that we can detect any additional
- // errors in the self type.
- if !(matches!(error, method::MethodError::NoMatch(_)) && ty.is_trait()) {
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
- }
- if item_name.name != kw::Empty {
- if let Some(mut e) = self.report_method_error(
- span,
- ty,
- item_name,
- SelfSource::QPath(qself),
- error,
- None,
- ) {
- e.emit();
- }
- }
- result
- });
-
- if result.is_ok() {
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
- }
-
- // Write back the new resolution.
- self.write_resolution(hir_id, result);
- (
- result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
- Some(ty),
- slice::from_ref(&**item_segment),
- )
- }
-
- /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
- pub(in super::super) fn get_node_fn_decl(
- &self,
- node: Node<'tcx>,
- ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> {
- match node {
- Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => {
- // This is less than ideal, it will not suggest a return type span on any
- // method called `main`, regardless of whether it is actually the entry point,
- // but it will still present it as the reason for the expected type.
- Some((&sig.decl, ident, ident.name != sym::main))
- }
- Node::TraitItem(&hir::TraitItem {
- ident,
- kind: hir::TraitItemKind::Fn(ref sig, ..),
- ..
- }) => Some((&sig.decl, ident, true)),
- Node::ImplItem(&hir::ImplItem {
- ident,
- kind: hir::ImplItemKind::Fn(ref sig, ..),
- ..
- }) => Some((&sig.decl, ident, false)),
- _ => None,
- }
- }
-
- /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
- /// suggestion can be made, `None` otherwise.
- pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> {
- // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
- // `while` before reaching it, as block tail returns are not available in them.
- self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
- let parent = self.tcx.hir().get(blk_id);
- self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
- })
- }
-
- pub(in super::super) fn note_internal_mutation_in_method(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if found != self.tcx.types.unit {
- return;
- }
- if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
- if self
- .typeck_results
- .borrow()
- .expr_ty_adjusted_opt(rcvr)
- .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
- {
- return;
- }
- let mut sp = MultiSpan::from_span(path_segment.ident.span);
- sp.push_span_label(
- path_segment.ident.span,
- format!(
- "this call modifies {} in-place",
- match rcvr.kind {
- ExprKind::Path(QPath::Resolved(
- None,
- hir::Path { segments: [segment], .. },
- )) => format!("`{}`", segment.ident),
- _ => "its receiver".to_string(),
- }
- ),
- );
- sp.push_span_label(
- rcvr.span,
- "you probably want to use this value after calling the method...",
- );
- err.span_note(
- sp,
- &format!("method `{}` modifies its receiver in-place", path_segment.ident),
- );
- err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
- }
- }
-
- pub(in super::super) fn note_need_for_fn_pointer(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
- (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
- if sig1 != sig2 {
- return;
- }
- err.note(
- "different `fn` items always have unique types, even if their signatures are \
- the same",
- );
- (sig1, *did1, substs1)
- }
- (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
- if sig1 != *sig2 {
- return;
- }
- (sig1, *did, substs)
- }
- _ => return,
- };
- err.help(&format!("change the expected type to be function pointer `{}`", sig));
- err.help(&format!(
- "if the expected type is due to type inference, cast the expected `fn` to a function \
- pointer: `{} as {}`",
- self.tcx.def_path_str_with_substs(did, substs),
- sig
- ));
- }
-
- // Instantiates the given path, which must refer to an item with the given
- // number of type parameters and type.
- #[instrument(skip(self, span), level = "debug")]
- pub fn instantiate_value_path(
- &self,
- segments: &[hir::PathSegment<'_>],
- self_ty: Option<Ty<'tcx>>,
- res: Res,
- span: Span,
- hir_id: hir::HirId,
- ) -> (Ty<'tcx>, Res) {
- let tcx = self.tcx;
-
- let path_segs = match res {
- Res::Local(_) | Res::SelfCtor(_) => vec![],
- Res::Def(kind, def_id) => <dyn AstConv<'_>>::def_ids_for_value_path_segments(
- self, segments, self_ty, kind, def_id,
- ),
- _ => bug!("instantiate_value_path on {:?}", res),
- };
-
- let mut user_self_ty = None;
- let mut is_alias_variant_ctor = false;
- match res {
- Res::Def(DefKind::Ctor(CtorOf::Variant, _), _)
- if let Some(self_ty) = self_ty =>
- {
- let adt_def = self_ty.ty_adt_def().unwrap();
- user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty });
- is_alias_variant_ctor = true;
- }
- Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
- let assoc_item = tcx.associated_item(def_id);
- let container = assoc_item.container;
- let container_id = assoc_item.container_id(tcx);
- debug!(?def_id, ?container, ?container_id);
- match container {
- ty::TraitContainer => {
- callee::check_legal_trait_for_method_call(tcx, span, None, span, container_id)
- }
- ty::ImplContainer => {
- if segments.len() == 1 {
- // `<T>::assoc` will end up here, and so
- // can `T::assoc`. It this came from an
- // inherent impl, we need to record the
- // `T` for posterity (see `UserSelfTy` for
- // details).
- let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
- user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty });
- }
- }
- }
- }
- _ => {}
- }
-
- // Now that we have categorized what space the parameters for each
- // segment belong to, let's sort out the parameters that the user
- // provided (if any) into their appropriate spaces. We'll also report
- // errors if type parameters are provided in an inappropriate place.
-
- let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
- let generics_has_err = <dyn AstConv<'_>>::prohibit_generics(
- self,
- segments.iter().enumerate().filter_map(|(index, seg)| {
- if !generic_segs.contains(&index) || is_alias_variant_ctor {
- Some(seg)
- } else {
- None
- }
- }),
- |_| {},
- );
-
- if let Res::Local(hid) = res {
- let ty = self.local_ty(span, hid).decl_ty;
- let ty = self.normalize_associated_types_in(span, ty);
- self.write_ty(hir_id, ty);
- return (ty, res);
- }
-
- if generics_has_err {
- // Don't try to infer type parameters when prohibited generic arguments were given.
- user_self_ty = None;
- }
-
- // Now we have to compare the types that the user *actually*
- // provided against the types that were *expected*. If the user
- // did not provide any types, then we want to substitute inference
- // variables. If the user provided some types, we may still need
- // to add defaults. If the user provided *too many* types, that's
- // a problem.
-
- let mut infer_args_for_err = FxHashSet::default();
-
- let mut explicit_late_bound = ExplicitLateBound::No;
- for &PathSeg(def_id, index) in &path_segs {
- let seg = &segments[index];
- let generics = tcx.generics_of(def_id);
-
- // Argument-position `impl Trait` is treated as a normal generic
- // parameter internally, but we don't allow users to specify the
- // parameter's value explicitly, so we have to do some error-
- // checking here.
- let arg_count = <dyn AstConv<'_>>::check_generic_arg_count_for_call(
- tcx,
- span,
- def_id,
- &generics,
- seg,
- IsMethodCall::No,
- );
-
- if let ExplicitLateBound::Yes = arg_count.explicit_late_bound {
- explicit_late_bound = ExplicitLateBound::Yes;
- }
-
- if let Err(GenericArgCountMismatch { reported: Some(_), .. }) = arg_count.correct {
- infer_args_for_err.insert(index);
- self.set_tainted_by_errors(); // See issue #53251.
- }
- }
-
- let has_self = path_segs
- .last()
- .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self)
- .unwrap_or(false);
-
- let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
- let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id));
- match *ty.kind() {
- ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
- let variant = adt_def.non_enum_variant();
- let ctor_def_id = variant.ctor_def_id.unwrap();
- (
- Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
- Some(substs),
- )
- }
- _ => {
- let mut err = tcx.sess.struct_span_err(
- span,
- "the `Self` constructor can only be used with tuple or unit structs",
- );
- if let Some(adt_def) = ty.ty_adt_def() {
- match adt_def.adt_kind() {
- AdtKind::Enum => {
- err.help("did you mean to use one of the enum's variants?");
- }
- AdtKind::Struct | AdtKind::Union => {
- err.span_suggestion(
- span,
- "use curly brackets",
- "Self { /* fields */ }",
- Applicability::HasPlaceholders,
- );
- }
- }
- }
- err.emit();
-
- return (tcx.ty_error(), res);
- }
- }
- } else {
- (res, None)
- };
- let def_id = res.def_id();
-
- // The things we are substituting into the type should not contain
- // escaping late-bound regions, and nor should the base type scheme.
- let ty = tcx.type_of(def_id);
-
- let arg_count = GenericArgCountResult {
- explicit_late_bound,
- correct: if infer_args_for_err.is_empty() {
- Ok(())
- } else {
- Err(GenericArgCountMismatch::default())
- },
- };
-
- struct CreateCtorSubstsContext<'a, 'tcx> {
- fcx: &'a FnCtxt<'a, 'tcx>,
- span: Span,
- path_segs: &'a [PathSeg],
- infer_args_for_err: &'a FxHashSet<usize>,
- segments: &'a [hir::PathSegment<'a>],
- }
- impl<'tcx, 'a> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for CreateCtorSubstsContext<'a, 'tcx> {
- fn args_for_def_id(
- &mut self,
- def_id: DefId,
- ) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
- if let Some(&PathSeg(_, index)) =
- self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
- {
- // If we've encountered an `impl Trait`-related error, we're just
- // going to infer the arguments for better error messages.
- if !self.infer_args_for_err.contains(&index) {
- // Check whether the user has provided generic arguments.
- if let Some(ref data) = self.segments[index].args {
- return (Some(data), self.segments[index].infer_args);
- }
- }
- return (None, self.segments[index].infer_args);
- }
-
- (None, true)
- }
-
- fn provided_kind(
- &mut self,
- param: &ty::GenericParamDef,
- arg: &GenericArg<'_>,
- ) -> subst::GenericArg<'tcx> {
- match (&param.kind, arg) {
- (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
- <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into()
- }
- (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
- self.fcx.to_ty(ty).into()
- }
- (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
- self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
- }
- (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
- self.fcx.ty_infer(Some(param), inf.span).into()
- }
- (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
- let tcx = self.fcx.tcx();
- self.fcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into()
- }
- _ => unreachable!(),
- }
- }
-
- fn inferred_kind(
- &mut self,
- substs: Option<&[subst::GenericArg<'tcx>]>,
- param: &ty::GenericParamDef,
- infer_args: bool,
- ) -> subst::GenericArg<'tcx> {
- let tcx = self.fcx.tcx();
- match param.kind {
- GenericParamDefKind::Lifetime => {
- self.fcx.re_infer(Some(param), self.span).unwrap().into()
- }
- GenericParamDefKind::Type { has_default, .. } => {
- if !infer_args && has_default {
- // If we have a default, then we it doesn't matter that we're not
- // inferring the type arguments: we provide the default where any
- // is missing.
- let default = tcx.bound_type_of(param.def_id);
- self.fcx
- .normalize_ty(self.span, default.subst(tcx, substs.unwrap()))
- .into()
- } else {
- // If no type arguments were provided, we have to infer them.
- // This case also occurs as a result of some malformed input, e.g.
- // a lifetime argument being given instead of a type parameter.
- // Using inference instead of `Error` gives better error messages.
- self.fcx.var_for_def(self.span, param)
- }
- }
- GenericParamDefKind::Const { has_default } => {
- if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
- } else {
- self.fcx.var_for_def(self.span, param)
- }
- }
- }
- }
- }
-
- let substs = self_ctor_substs.unwrap_or_else(|| {
- <dyn AstConv<'_>>::create_substs_for_generic_args(
- tcx,
- def_id,
- &[],
- has_self,
- self_ty,
- &arg_count,
- &mut CreateCtorSubstsContext {
- fcx: self,
- span,
- path_segs: &path_segs,
- infer_args_for_err: &infer_args_for_err,
- segments,
- },
- )
- });
- assert!(!substs.has_escaping_bound_vars());
- assert!(!ty.has_escaping_bound_vars());
-
- // First, store the "user substs" for later.
- self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
-
- self.add_required_obligations_for_hir(span, def_id, &substs, hir_id);
-
- // Substitute the values for the type parameters into the type of
- // the referenced item.
- let ty_substituted = self.instantiate_type_scheme(span, &substs, ty);
-
- if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
- // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
- // is inherent, there is no `Self` parameter; instead, the impl needs
- // type parameters, which we can infer by unifying the provided `Self`
- // with the substituted impl type.
- // This also occurs for an enum variant on a type alias.
- let ty = tcx.type_of(impl_def_id);
-
- let impl_ty = self.instantiate_type_scheme(span, &substs, ty);
- match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) {
- Ok(ok) => self.register_infer_ok_obligations(ok),
- Err(_) => {
- self.tcx.sess.delay_span_bug(
- span,
- &format!(
- "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
- self_ty,
- impl_ty,
- ),
- );
- }
- }
- }
-
- debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
- self.write_substs(hir_id, substs);
-
- (ty_substituted, res)
- }
-
- /// Add all the obligations that are required, substituting and normalized appropriately.
- pub(crate) fn add_required_obligations_for_hir(
- &self,
- span: Span,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- hir_id: hir::HirId,
- ) {
- self.add_required_obligations_with_code(span, def_id, substs, |idx, span| {
- if span.is_dummy() {
- ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx)
- } else {
- ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx)
- }
- })
- }
-
- #[instrument(level = "debug", skip(self, code, span, def_id, substs))]
- fn add_required_obligations_with_code(
- &self,
- span: Span,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
- ) {
- let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
-
- for obligation in traits::predicates_for_generics(
- |idx, predicate_span| {
- traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span))
- },
- self.param_env,
- bounds,
- ) {
- self.register_predicate(obligation);
- }
- }
-
- /// Resolves `typ` by a single level if `typ` is a type variable.
- /// If no resolution is possible, then an error is reported.
- /// Numeric inference variables may be left unresolved.
- pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- let ty = self.resolve_vars_with_obligations(ty);
- if !ty.is_ty_var() {
- ty
- } else {
- if !self.is_tainted_by_errors() {
- self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
- .emit();
- }
- let err = self.tcx.ty_error();
- self.demand_suptype(sp, err, ty);
- err
- }
- }
-
- pub(in super::super) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
- &self,
- id: hir::HirId,
- ctxt: BreakableCtxt<'tcx>,
- f: F,
- ) -> (BreakableCtxt<'tcx>, R) {
- let index;
- {
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- index = enclosing_breakables.stack.len();
- enclosing_breakables.by_id.insert(id, index);
- enclosing_breakables.stack.push(ctxt);
- }
- let result = f();
- let ctxt = {
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- debug_assert!(enclosing_breakables.stack.len() == index + 1);
- enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
- enclosing_breakables.stack.pop().expect("missing breakable context")
- };
- (ctxt, result)
- }
-
- /// Instantiate a QueryResponse in a probe context, without a
- /// good ObligationCause.
- pub(in super::super) fn probe_instantiate_query_response(
- &self,
- span: Span,
- original_values: &OriginalQueryValues<'tcx>,
- query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
- ) -> InferResult<'tcx, Ty<'tcx>> {
- self.instantiate_query_response_and_region_obligations(
- &traits::ObligationCause::misc(span, self.body_id),
- self.param_env,
- original_values,
- query_result,
- )
- }
-
- /// Returns `true` if an expression is contained inside the LHS of an assignment expression.
- pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
- let mut contained_in_place = false;
-
- while let hir::Node::Expr(parent_expr) =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
- {
- match &parent_expr.kind {
- hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
- if lhs.hir_id == expr_id {
- contained_in_place = true;
- break;
- }
- }
- _ => (),
- }
- expr_id = parent_expr.hir_id;
- }
-
- contained_in_place
- }
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs
deleted file mode 100644
index fc83994ca..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs
+++ /dev/null
@@ -1,383 +0,0 @@
-use std::cmp;
-
-use rustc_index::vec::IndexVec;
-use rustc_middle::ty::error::TypeError;
-
-rustc_index::newtype_index! {
- pub(crate) struct ExpectedIdx {
- DEBUG_FORMAT = "ExpectedIdx({})",
- }
-}
-
-rustc_index::newtype_index! {
- pub(crate) struct ProvidedIdx {
- DEBUG_FORMAT = "ProvidedIdx({})",
- }
-}
-
-impl ExpectedIdx {
- pub fn to_provided_idx(self) -> ProvidedIdx {
- ProvidedIdx::from_usize(self.as_usize())
- }
-}
-
-// An issue that might be found in the compatibility matrix
-#[derive(Debug)]
-enum Issue {
- /// The given argument is the invalid type for the input
- Invalid(usize),
- /// There is a missing input
- Missing(usize),
- /// There's a superfluous argument
- Extra(usize),
- /// Two arguments should be swapped
- Swap(usize, usize),
- /// Several arguments should be reordered
- Permutation(Vec<Option<usize>>),
-}
-
-#[derive(Clone, Debug)]
-pub(crate) enum Compatibility<'tcx> {
- Compatible,
- Incompatible(Option<TypeError<'tcx>>),
-}
-
-/// Similar to `Issue`, but contains some extra information
-#[derive(Debug)]
-pub(crate) enum Error<'tcx> {
- /// The provided argument is the invalid type for the expected input
- Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>),
- /// There is a missing input
- Missing(ExpectedIdx),
- /// There's a superfluous argument
- Extra(ProvidedIdx),
- /// Two arguments should be swapped
- Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx),
- /// Several arguments should be reordered
- Permutation(Vec<(ExpectedIdx, ProvidedIdx)>),
-}
-
-pub(crate) struct ArgMatrix<'tcx> {
- /// Maps the indices in the `compatibility_matrix` rows to the indices of
- /// the *user provided* inputs
- provided_indices: Vec<ProvidedIdx>,
- /// Maps the indices in the `compatibility_matrix` columns to the indices
- /// of the *expected* args
- expected_indices: Vec<ExpectedIdx>,
- /// The first dimension (rows) are the remaining user provided inputs to
- /// match and the second dimension (cols) are the remaining expected args
- /// to match
- compatibility_matrix: Vec<Vec<Compatibility<'tcx>>>,
-}
-
-impl<'tcx> ArgMatrix<'tcx> {
- pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>(
- provided_count: usize,
- expected_input_count: usize,
- mut is_compatible: F,
- ) -> Self {
- let compatibility_matrix = (0..provided_count)
- .map(|i| {
- (0..expected_input_count)
- .map(|j| is_compatible(ProvidedIdx::from_usize(i), ExpectedIdx::from_usize(j)))
- .collect()
- })
- .collect();
- ArgMatrix {
- provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(),
- expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(),
- compatibility_matrix,
- }
- }
-
- /// Remove a given input from consideration
- fn eliminate_provided(&mut self, idx: usize) {
- self.provided_indices.remove(idx);
- self.compatibility_matrix.remove(idx);
- }
-
- /// Remove a given argument from consideration
- fn eliminate_expected(&mut self, idx: usize) {
- self.expected_indices.remove(idx);
- for row in &mut self.compatibility_matrix {
- row.remove(idx);
- }
- }
-
- /// "satisfy" an input with a given arg, removing both from consideration
- fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) {
- self.eliminate_provided(provided_idx);
- self.eliminate_expected(expected_idx);
- }
-
- // Returns a `Vec` of (user input, expected arg) of matched arguments. These
- // are inputs on the remaining diagonal that match.
- fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> {
- let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len());
- let mut eliminated = vec![];
- for i in (0..num_args).rev() {
- if matches!(self.compatibility_matrix[i][i], Compatibility::Compatible) {
- eliminated.push((self.provided_indices[i], self.expected_indices[i]));
- self.satisfy_input(i, i);
- }
- }
- eliminated
- }
-
- // Find some issue in the compatibility matrix
- fn find_issue(&self) -> Option<Issue> {
- let mat = &self.compatibility_matrix;
- let ai = &self.expected_indices;
- let ii = &self.provided_indices;
-
- // Issue: 100478, when we end the iteration,
- // `next_unmatched_idx` will point to the index of the first unmatched
- let mut next_unmatched_idx = 0;
- for i in 0..cmp::max(ai.len(), ii.len()) {
- // If we eliminate the last row, any left-over arguments are considered missing
- if i >= mat.len() {
- return Some(Issue::Missing(next_unmatched_idx));
- }
- // If we eliminate the last column, any left-over inputs are extra
- if mat[i].len() == 0 {
- return Some(Issue::Extra(next_unmatched_idx));
- }
-
- // Make sure we don't pass the bounds of our matrix
- let is_arg = i < ai.len();
- let is_input = i < ii.len();
- if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) {
- // This is a satisfied input, so move along
- next_unmatched_idx += 1;
- continue;
- }
-
- let mut useless = true;
- let mut unsatisfiable = true;
- if is_arg {
- for j in 0..ii.len() {
- // If we find at least one input this argument could satisfy
- // this argument isn't unsatisfiable
- if matches!(mat[j][i], Compatibility::Compatible) {
- unsatisfiable = false;
- break;
- }
- }
- }
- if is_input {
- for j in 0..ai.len() {
- // If we find at least one argument that could satisfy this input
- // this input isn't useless
- if matches!(mat[i][j], Compatibility::Compatible) {
- useless = false;
- break;
- }
- }
- }
-
- match (is_input, is_arg, useless, unsatisfiable) {
- // If an argument is unsatisfied, and the input in its position is useless
- // then the most likely explanation is that we just got the types wrong
- (true, true, true, true) => return Some(Issue::Invalid(i)),
- // Otherwise, if an input is useless, then indicate that this is an extra argument
- (true, _, true, _) => return Some(Issue::Extra(i)),
- // Otherwise, if an argument is unsatisfiable, indicate that it's missing
- (_, true, _, true) => return Some(Issue::Missing(i)),
- (true, true, _, _) => {
- // The argument isn't useless, and the input isn't unsatisfied,
- // so look for a parameter we might swap it with
- // We look for swaps explicitly, instead of just falling back on permutations
- // so that cases like (A,B,C,D) given (B,A,D,C) show up as two swaps,
- // instead of a large permutation of 4 elements.
- for j in 0..cmp::min(ai.len(), ii.len()) {
- if i == j || matches!(mat[j][j], Compatibility::Compatible) {
- continue;
- }
- if matches!(mat[i][j], Compatibility::Compatible)
- && matches!(mat[j][i], Compatibility::Compatible)
- {
- return Some(Issue::Swap(i, j));
- }
- }
- }
- _ => {
- continue;
- }
- }
- }
-
- // We didn't find any of the individual issues above, but
- // there might be a larger permutation of parameters, so we now check for that
- // by checking for cycles
- // We use a double option at position i in this vec to represent:
- // - None: We haven't computed anything about this argument yet
- // - Some(None): This argument definitely doesn't participate in a cycle
- // - Some(Some(x)): the i-th argument could permute to the x-th position
- let mut permutation: Vec<Option<Option<usize>>> = vec![None; mat.len()];
- let mut permutation_found = false;
- for i in 0..mat.len() {
- if permutation[i].is_some() {
- // We've already decided whether this argument is or is not in a loop
- continue;
- }
-
- let mut stack = vec![];
- let mut j = i;
- let mut last = i;
- let mut is_cycle = true;
- loop {
- stack.push(j);
- // Look for params this one could slot into
- let compat: Vec<_> =
- mat[j]
- .iter()
- .enumerate()
- .filter_map(|(i, c)| {
- if matches!(c, Compatibility::Compatible) { Some(i) } else { None }
- })
- .collect();
- if compat.len() < 1 {
- // try to find a cycle even when this could go into multiple slots, see #101097
- is_cycle = false;
- break;
- }
- j = compat[0];
- if stack.contains(&j) {
- last = j;
- break;
- }
- }
- if stack.len() <= 2 {
- // If we encounter a cycle of 1 or 2 elements, we'll let the
- // "satisfy" and "swap" code above handle those
- is_cycle = false;
- }
- // We've built up some chain, some of which might be a cycle
- // ex: [1,2,3,4]; last = 2; j = 2;
- // So, we want to mark 4, 3, and 2 as part of a permutation
- permutation_found = is_cycle;
- while let Some(x) = stack.pop() {
- if is_cycle {
- permutation[x] = Some(Some(j));
- j = x;
- if j == last {
- // From here on out, we're a tail leading into a cycle,
- // not the cycle itself
- is_cycle = false;
- }
- } else {
- // Some(None) ensures we save time by skipping this argument again
- permutation[x] = Some(None);
- }
- }
- }
-
- if permutation_found {
- // Map unwrap to remove the first layer of Some
- let final_permutation: Vec<Option<usize>> =
- permutation.into_iter().map(|x| x.unwrap()).collect();
- return Some(Issue::Permutation(final_permutation));
- }
- return None;
- }
-
- // Obviously, detecting exact user intention is impossible, so the goal here is to
- // come up with as likely of a story as we can to be helpful.
- //
- // We'll iteratively removed "satisfied" input/argument pairs,
- // then check for the cases above, until we've eliminated the entire grid
- //
- // We'll want to know which arguments and inputs these rows and columns correspond to
- // even after we delete them.
- pub(crate) fn find_errors(
- mut self,
- ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) {
- let provided_arg_count = self.provided_indices.len();
-
- let mut errors: Vec<Error<'tcx>> = vec![];
- // For each expected argument, the matched *actual* input
- let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> =
- IndexVec::from_elem_n(None, self.expected_indices.len());
-
- // Before we start looking for issues, eliminate any arguments that are already satisfied,
- // so that an argument which is already spoken for by the input it's in doesn't
- // spill over into another similarly typed input
- // ex:
- // fn some_func(_a: i32, _b: i32) {}
- // some_func(1, "");
- // Without this elimination, the first argument causes the second argument
- // to show up as both a missing input and extra argument, rather than
- // just an invalid type.
- for (provided, expected) in self.eliminate_satisfied() {
- matched_inputs[expected] = Some(provided);
- }
-
- while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() {
- let res = self.find_issue();
- match res {
- Some(Issue::Invalid(idx)) => {
- let compatibility = self.compatibility_matrix[idx][idx].clone();
- let input_idx = self.provided_indices[idx];
- let arg_idx = self.expected_indices[idx];
- self.satisfy_input(idx, idx);
- errors.push(Error::Invalid(input_idx, arg_idx, compatibility));
- }
- Some(Issue::Extra(idx)) => {
- let input_idx = self.provided_indices[idx];
- self.eliminate_provided(idx);
- errors.push(Error::Extra(input_idx));
- }
- Some(Issue::Missing(idx)) => {
- let arg_idx = self.expected_indices[idx];
- self.eliminate_expected(idx);
- errors.push(Error::Missing(arg_idx));
- }
- Some(Issue::Swap(idx, other)) => {
- let input_idx = self.provided_indices[idx];
- let other_input_idx = self.provided_indices[other];
- let arg_idx = self.expected_indices[idx];
- let other_arg_idx = self.expected_indices[other];
- let (min, max) = (cmp::min(idx, other), cmp::max(idx, other));
- self.satisfy_input(min, max);
- // Subtract 1 because we already removed the "min" row
- self.satisfy_input(max - 1, min);
- errors.push(Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx));
- matched_inputs[other_arg_idx] = Some(input_idx);
- matched_inputs[arg_idx] = Some(other_input_idx);
- }
- Some(Issue::Permutation(args)) => {
- let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect();
-
- let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> =
- IndexVec::from_elem_n(None, provided_arg_count);
- for (src, dst) in
- args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst)))
- {
- let src_input_idx = self.provided_indices[src];
- let dst_input_idx = self.provided_indices[dst];
- let dest_arg_idx = self.expected_indices[dst];
- real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx));
- matched_inputs[dest_arg_idx] = Some(src_input_idx);
- }
- idxs.sort();
- idxs.reverse();
- for i in idxs {
- self.satisfy_input(i, i);
- }
- errors.push(Error::Permutation(real_idxs.into_iter().flatten().collect()));
- }
- None => {
- // We didn't find any issues, so we need to push the algorithm forward
- // First, eliminate any arguments that currently satisfy their inputs
- let eliminated = self.eliminate_satisfied();
- assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round");
- for (inp, arg) in eliminated {
- matched_inputs[arg] = Some(inp);
- }
- }
- };
- }
-
- return (errors, matched_inputs);
- }
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
deleted file mode 100644
index 311fcaada..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ /dev/null
@@ -1,2222 +0,0 @@
-use crate::astconv::AstConv;
-use crate::check::coercion::CoerceMany;
-use crate::check::fn_ctxt::arg_matrix::{
- ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx,
-};
-use crate::check::gather_locals::Declaration;
-use crate::check::intrinsicck::InlineAsmCtxt;
-use crate::check::method::MethodCallee;
-use crate::check::Expectation::*;
-use crate::check::TupleArgumentsFlag::*;
-use crate::check::{
- potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt,
- LocalTy, Needs, TupleArgumentsFlag,
-};
-use crate::structured_errors::StructuredDiagnostic;
-
-use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::{ExprKind, Node, QPath};
-use rustc_index::vec::IndexVec;
-use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::InferOk;
-use rustc_infer::infer::TypeTrace;
-use rustc_middle::ty::adjustment::AllowTwoPhase;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
-use rustc_session::Session;
-use rustc_span::symbol::Ident;
-use rustc_span::{self, sym, Span};
-use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
-
-use std::iter;
-use std::ops::ControlFlow;
-use std::slice;
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub(in super::super) fn check_casts(&self) {
- let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
- debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len());
- for cast in deferred_cast_checks.drain(..) {
- cast.check(self);
- }
- }
-
- pub(in super::super) fn check_transmutes(&self) {
- let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut();
- debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len());
- for (from, to, span) in deferred_transmute_checks.drain(..) {
- self.check_transmute(span, from, to);
- }
- }
-
- pub(in super::super) fn check_asms(&self) {
- let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut();
- debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());
- for (asm, hir_id) in deferred_asm_checks.drain(..) {
- let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id);
- let get_operand_ty = |expr| {
- let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
- let ty = self.resolve_vars_if_possible(ty);
- if ty.has_infer_types_or_consts() {
- assert!(self.is_tainted_by_errors());
- self.tcx.ty_error()
- } else {
- self.tcx.erase_regions(ty)
- }
- };
- InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty)
- .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id));
- }
- }
-
- pub(in super::super) fn check_method_argument_types(
- &self,
- sp: Span,
- expr: &'tcx hir::Expr<'tcx>,
- method: Result<MethodCallee<'tcx>, ()>,
- args_no_rcvr: &'tcx [hir::Expr<'tcx>],
- tuple_arguments: TupleArgumentsFlag,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let has_error = match method {
- Ok(method) => method.substs.references_error() || method.sig.references_error(),
- Err(_) => true,
- };
- if has_error {
- let err_inputs = self.err_args(args_no_rcvr.len());
-
- let err_inputs = match tuple_arguments {
- DontTupleArguments => err_inputs,
- TupleArguments => vec![self.tcx.intern_tup(&err_inputs)],
- };
-
- self.check_argument_types(
- sp,
- expr,
- &err_inputs,
- None,
- args_no_rcvr,
- false,
- tuple_arguments,
- method.ok().map(|method| method.def_id),
- );
- return self.tcx.ty_error();
- }
-
- let method = method.unwrap();
- // HACK(eddyb) ignore self in the definition (see above).
- let expected_input_tys = self.expected_inputs_for_expected_output(
- sp,
- expected,
- method.sig.output(),
- &method.sig.inputs()[1..],
- );
- self.check_argument_types(
- sp,
- expr,
- &method.sig.inputs()[1..],
- expected_input_tys,
- args_no_rcvr,
- method.sig.c_variadic,
- tuple_arguments,
- Some(method.def_id),
- );
- method.sig.output()
- }
-
- /// Generic function that factors out common logic from function calls,
- /// method calls and overloaded operators.
- pub(in super::super) fn check_argument_types(
- &self,
- // Span enclosing the call site
- call_span: Span,
- // Expression of the call site
- call_expr: &'tcx hir::Expr<'tcx>,
- // Types (as defined in the *signature* of the target function)
- formal_input_tys: &[Ty<'tcx>],
- // More specific expected types, after unifying with caller output types
- expected_input_tys: Option<Vec<Ty<'tcx>>>,
- // The expressions for each provided argument
- provided_args: &'tcx [hir::Expr<'tcx>],
- // Whether the function is variadic, for example when imported from C
- c_variadic: bool,
- // Whether the arguments have been bundled in a tuple (ex: closures)
- tuple_arguments: TupleArgumentsFlag,
- // The DefId for the function being called, for better error messages
- fn_def_id: Option<DefId>,
- ) {
- let tcx = self.tcx;
-
- // Conceptually, we've got some number of expected inputs, and some number of provided arguments
- // and we can form a grid of whether each argument could satisfy a given input:
- // in1 | in2 | in3 | ...
- // arg1 ? | | |
- // arg2 | ? | |
- // arg3 | | ? |
- // ...
- // Initially, we just check the diagonal, because in the case of correct code
- // these are the only checks that matter
- // However, in the unhappy path, we'll fill in this whole grid to attempt to provide
- // better error messages about invalid method calls.
-
- // All the input types from the fn signature must outlive the call
- // so as to validate implied bounds.
- for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) {
- self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
- }
-
- let mut err_code = "E0061";
-
- // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
- let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
- let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
- match tuple_type.kind() {
- // We expected a tuple and got a tuple
- ty::Tuple(arg_types) => {
- // Argument length differs
- if arg_types.len() != provided_args.len() {
- err_code = "E0057";
- }
- let expected_input_tys = match expected_input_tys {
- Some(expected_input_tys) => match expected_input_tys.get(0) {
- Some(ty) => match ty.kind() {
- ty::Tuple(tys) => Some(tys.iter().collect()),
- _ => None,
- },
- None => None,
- },
- None => None,
- };
- (arg_types.iter().collect(), expected_input_tys)
- }
- _ => {
- // Otherwise, there's a mismatch, so clear out what we're expecting, and set
- // our input types to err_args so we don't blow up the error messages
- struct_span_err!(
- tcx.sess,
- call_span,
- E0059,
- "cannot use call notation; the first type parameter \
- for the function trait is neither a tuple nor unit"
- )
- .emit();
- (self.err_args(provided_args.len()), None)
- }
- }
- } else {
- (formal_input_tys.to_vec(), expected_input_tys)
- };
-
- // If there are no external expectations at the call site, just use the types from the function defn
- let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys {
- assert_eq!(expected_input_tys.len(), formal_input_tys.len());
- expected_input_tys
- } else {
- formal_input_tys.clone()
- };
-
- let minimum_input_count = expected_input_tys.len();
- let provided_arg_count = provided_args.len();
-
- let is_const_eval_select = matches!(fn_def_id, Some(def_id) if
- self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
- && self.tcx.is_intrinsic(def_id)
- && self.tcx.item_name(def_id) == sym::const_eval_select);
-
- // We introduce a helper function to demand that a given argument satisfy a given input
- // This is more complicated than just checking type equality, as arguments could be coerced
- // This version writes those types back so further type checking uses the narrowed types
- let demand_compatible = |idx| {
- let formal_input_ty: Ty<'tcx> = formal_input_tys[idx];
- let expected_input_ty: Ty<'tcx> = expected_input_tys[idx];
- let provided_arg = &provided_args[idx];
-
- debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty);
-
- // We're on the happy path here, so we'll do a more involved check and write back types
- // To check compatibility, we'll do 3 things:
- // 1. Unify the provided argument with the expected type
- let expectation = Expectation::rvalue_hint(self, expected_input_ty);
-
- let checked_ty = self.check_expr_with_expectation(provided_arg, expectation);
-
- // 2. Coerce to the most detailed type that could be coerced
- // to, which is `expected_ty` if `rvalue_hint` returns an
- // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
- let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
-
- // Cause selection errors caused by resolving a single argument to point at the
- // argument and not the call. This lets us customize the span pointed to in the
- // fulfillment error to be more accurate.
- let coerced_ty = self.resolve_vars_with_obligations(coerced_ty);
-
- let coerce_error = self
- .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None)
- .err();
-
- if coerce_error.is_some() {
- return Compatibility::Incompatible(coerce_error);
- }
-
- // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
- // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
- // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
- //
- // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
- if is_const_eval_select && (1..=2).contains(&idx) {
- if let ty::FnDef(def_id, _) = checked_ty.kind() {
- if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) {
- self.tcx
- .sess
- .struct_span_err(provided_arg.span, "this argument must be a `const fn`")
- .help("consult the documentation on `const_eval_select` for more information")
- .emit();
- }
- } else {
- self.tcx
- .sess
- .struct_span_err(provided_arg.span, "this argument must be a function item")
- .note(format!("expected a function item, found {checked_ty}"))
- .help(
- "consult the documentation on `const_eval_select` for more information",
- )
- .emit();
- }
- }
-
- // 3. Check if the formal type is a supertype of the checked one
- // and register any such obligations for future type checks
- let supertype_error = self
- .at(&self.misc(provided_arg.span), self.param_env)
- .sup(formal_input_ty, coerced_ty);
- let subtyping_error = match supertype_error {
- Ok(InferOk { obligations, value: () }) => {
- self.register_predicates(obligations);
- None
- }
- Err(err) => Some(err),
- };
-
- // If neither check failed, the types are compatible
- match subtyping_error {
- None => Compatibility::Compatible,
- Some(_) => Compatibility::Incompatible(subtyping_error),
- }
- };
-
- // To start, we only care "along the diagonal", where we expect every
- // provided arg to be in the right spot
- let mut compatibility_diagonal =
- vec![Compatibility::Incompatible(None); provided_args.len()];
-
- // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path
- // if the wrong number of arguments were supplied, we CAN'T be satisfied,
- // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function
- // otherwise, they need to be identical, because rust doesn't currently support variadic functions
- let mut call_appears_satisfied = if c_variadic {
- provided_arg_count >= minimum_input_count
- } else {
- provided_arg_count == minimum_input_count
- };
-
- // Check the arguments.
- // We do this in a pretty awful way: first we type-check any arguments
- // that are not closures, then we type-check the closures. This is so
- // that we have more information about the types of arguments when we
- // type-check the functions. This isn't really the right way to do this.
- for check_closures in [false, true] {
- // More awful hacks: before we check argument types, try to do
- // an "opportunistic" trait resolution of any trait bounds on
- // the call. This helps coercions.
- if check_closures {
- self.select_obligations_where_possible(false, |_| {})
- }
-
- // Check each argument, to satisfy the input it was provided for
- // Visually, we're traveling down the diagonal of the compatibility matrix
- for (idx, arg) in provided_args.iter().enumerate() {
- // Warn only for the first loop (the "no closures" one).
- // Closure arguments themselves can't be diverging, but
- // a previous argument can, e.g., `foo(panic!(), || {})`.
- if !check_closures {
- self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
- }
-
- // For C-variadic functions, we don't have a declared type for all of
- // the arguments hence we only do our usual type checking with
- // the arguments who's types we do know. However, we *can* check
- // for unreachable expressions (see above).
- // FIXME: unreachable warning current isn't emitted
- if idx >= minimum_input_count {
- continue;
- }
-
- let is_closure = matches!(arg.kind, ExprKind::Closure { .. });
- if is_closure != check_closures {
- continue;
- }
-
- let compatible = demand_compatible(idx);
- let is_compatible = matches!(compatible, Compatibility::Compatible);
- compatibility_diagonal[idx] = compatible;
-
- if !is_compatible {
- call_appears_satisfied = false;
- }
- }
- }
-
- if c_variadic && provided_arg_count < minimum_input_count {
- err_code = "E0060";
- }
-
- for arg in provided_args.iter().skip(minimum_input_count) {
- // Make sure we've checked this expr at least once.
- let arg_ty = self.check_expr(&arg);
-
- // If the function is c-style variadic, we skipped a bunch of arguments
- // so we need to check those, and write out the types
- // Ideally this would be folded into the above, for uniform style
- // but c-variadic is already a corner case
- if c_variadic {
- fn variadic_error<'tcx>(
- sess: &'tcx Session,
- span: Span,
- ty: Ty<'tcx>,
- cast_ty: &str,
- ) {
- use crate::structured_errors::MissingCastForVariadicArg;
-
- MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
- }
-
- // There are a few types which get autopromoted when passed via varargs
- // in C but we just error out instead and require explicit casts.
- let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
- match arg_ty.kind() {
- ty::Float(ty::FloatTy::F32) => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
- }
- ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
- }
- ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
- }
- ty::FnDef(..) => {
- let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
- let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
- variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
- }
- _ => {}
- }
- }
- }
-
- if !call_appears_satisfied {
- let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal);
- let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic {
- minimum_input_count
- } else {
- provided_arg_count
- }));
- debug_assert_eq!(
- formal_input_tys.len(),
- expected_input_tys.len(),
- "expected formal_input_tys to be the same size as expected_input_tys"
- );
- let formal_and_expected_inputs = IndexVec::from_iter(
- formal_input_tys
- .iter()
- .copied()
- .zip(expected_input_tys.iter().copied())
- .map(|vars| self.resolve_vars_if_possible(vars)),
- );
-
- self.report_arg_errors(
- compatibility_diagonal,
- formal_and_expected_inputs,
- provided_args,
- c_variadic,
- err_code,
- fn_def_id,
- call_span,
- call_expr,
- );
- }
- }
-
- fn report_arg_errors(
- &self,
- compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
- formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
- provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
- c_variadic: bool,
- err_code: &str,
- fn_def_id: Option<DefId>,
- call_span: Span,
- call_expr: &hir::Expr<'tcx>,
- ) {
- // Next, let's construct the error
- let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind {
- hir::ExprKind::Call(
- hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
- _,
- ) => {
- if let Res::Def(DefKind::Ctor(of, _), _) =
- self.typeck_results.borrow().qpath_res(qpath, *hir_id)
- {
- (call_span, *span, Some(of), false)
- } else {
- (call_span, *span, None, false)
- }
- }
- hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false),
- hir::ExprKind::MethodCall(path_segment, _, _, span) => {
- let ident_span = path_segment.ident.span;
- let ident_span = if let Some(args) = path_segment.args {
- ident_span.with_hi(args.span_ext.hi())
- } else {
- ident_span
- };
- // methods are never ctors
- (*span, ident_span, None, true)
- }
- k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
- };
- let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
- let call_name = match ctor_of {
- Some(CtorOf::Struct) => "struct",
- Some(CtorOf::Variant) => "enum variant",
- None => "function",
- };
-
- // Don't print if it has error types or is just plain `_`
- fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
- tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
- }
-
- self.set_tainted_by_errors();
- let tcx = self.tcx;
-
- // Get the argument span in the context of the call span so that
- // suggestions and labels are (more) correct when an arg is a
- // macro invocation.
- let normalize_span = |span: Span| -> Span {
- let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span);
- // Sometimes macros mess up the spans, so do not normalize the
- // arg span to equal the error span, because that's less useful
- // than pointing out the arg expr in the wrong context.
- if normalized_span.source_equal(error_span) { span } else { normalized_span }
- };
-
- // Precompute the provided types and spans, since that's all we typically need for below
- let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
- .iter()
- .map(|expr| {
- let ty = self
- .typeck_results
- .borrow()
- .expr_ty_adjusted_opt(*expr)
- .unwrap_or_else(|| tcx.ty_error());
- (self.resolve_vars_if_possible(ty), normalize_span(expr.span))
- })
- .collect();
- let callee_expr = match &call_expr.peel_blocks().kind {
- hir::ExprKind::Call(callee, _) => Some(*callee),
- hir::ExprKind::MethodCall(_, receiver, ..) => {
- if let Some((DefKind::AssocFn, def_id)) =
- self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
- && let Some(assoc) = tcx.opt_associated_item(def_id)
- && assoc.fn_has_self_parameter
- {
- Some(*receiver)
- } else {
- None
- }
- }
- _ => None,
- };
- let callee_ty = callee_expr
- .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
-
- // A "softer" version of the `demand_compatible`, which checks types without persisting them,
- // and treats error types differently
- // This will allow us to "probe" for other argument orders that would likely have been correct
- let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| {
- if provided_idx.as_usize() == expected_idx.as_usize() {
- return compatibility_diagonal[provided_idx].clone();
- }
-
- let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx];
- // If either is an error type, we defy the usual convention and consider them to *not* be
- // coercible. This prevents our error message heuristic from trying to pass errors into
- // every argument.
- if (formal_input_ty, expected_input_ty).references_error() {
- return Compatibility::Incompatible(None);
- }
-
- let (arg_ty, arg_span) = provided_arg_tys[provided_idx];
-
- let expectation = Expectation::rvalue_hint(self, expected_input_ty);
- let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
- let can_coerce = self.can_coerce(arg_ty, coerced_ty);
- if !can_coerce {
- return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts(
- ty::error::ExpectedFound::new(true, coerced_ty, arg_ty),
- )));
- }
-
- // Using probe here, since we don't want this subtyping to affect inference.
- let subtyping_error = self.probe(|_| {
- self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err()
- });
-
- // Same as above: if either the coerce type or the checked type is an error type,
- // consider them *not* compatible.
- let references_error = (coerced_ty, arg_ty).references_error();
- match (references_error, subtyping_error) {
- (false, None) => Compatibility::Compatible,
- (_, subtyping_error) => Compatibility::Incompatible(subtyping_error),
- }
- };
-
- // The algorithm here is inspired by levenshtein distance and longest common subsequence.
- // We'll try to detect 4 different types of mistakes:
- // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
- // - An input is missing, which isn't satisfied by *any* of the other arguments
- // - Some number of arguments have been provided in the wrong order
- // - A type is straight up invalid
-
- // First, let's find the errors
- let (mut errors, matched_inputs) =
- ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible)
- .find_errors();
-
- // First, check if we just need to wrap some arguments in a tuple.
- if let Some((mismatch_idx, terr)) =
- compatibility_diagonal.iter().enumerate().find_map(|(i, c)| {
- if let Compatibility::Incompatible(Some(terr)) = c {
- Some((i, *terr))
- } else {
- None
- }
- })
- {
- // Is the first bad expected argument a tuple?
- // Do we have as many extra provided arguments as the tuple's length?
- // If so, we might have just forgotten to wrap some args in a tuple.
- if let Some(ty::Tuple(tys)) =
- formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
- // If the tuple is unit, we're not actually wrapping any arguments.
- && !tys.is_empty()
- && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
- {
- // Wrap up the N provided arguments starting at this position in a tuple.
- let provided_as_tuple = tcx.mk_tup(
- provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
- );
-
- let mut satisfied = true;
- // Check if the newly wrapped tuple + rest of the arguments are compatible.
- for ((_, expected_ty), provided_ty) in std::iter::zip(
- formal_and_expected_inputs.iter().skip(mismatch_idx),
- [provided_as_tuple].into_iter().chain(
- provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
- ),
- ) {
- if !self.can_coerce(provided_ty, *expected_ty) {
- satisfied = false;
- break;
- }
- }
-
- // If they're compatible, suggest wrapping in an arg, and we're done!
- // Take some care with spans, so we don't suggest wrapping a macro's
- // innards in parenthesis, for example.
- if satisfied
- && let Some((_, lo)) =
- provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
- && let Some((_, hi)) =
- provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
- {
- let mut err;
- if tys.len() == 1 {
- // A tuple wrap suggestion actually occurs within,
- // so don't do anything special here.
- err = self.report_and_explain_type_error(
- TypeTrace::types(
- &self.misc(*lo),
- true,
- formal_and_expected_inputs[mismatch_idx.into()].1,
- provided_arg_tys[mismatch_idx.into()].0,
- ),
- terr,
- );
- err.span_label(
- full_call_span,
- format!("arguments to this {} are incorrect", call_name),
- );
- } else {
- err = tcx.sess.struct_span_err_with_code(
- full_call_span,
- &format!(
- "this {} takes {}{} but {} {} supplied",
- call_name,
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(
- formal_and_expected_inputs.len(),
- "argument"
- ),
- potentially_plural_count(provided_args.len(), "argument"),
- pluralize!("was", provided_args.len())
- ),
- DiagnosticId::Error(err_code.to_owned()),
- );
- err.multipart_suggestion_verbose(
- "wrap these arguments in parentheses to construct a tuple",
- vec![
- (lo.shrink_to_lo(), "(".to_string()),
- (hi.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- };
- self.label_fn_like(
- &mut err,
- fn_def_id,
- callee_ty,
- Some(mismatch_idx),
- is_method,
- );
- err.emit();
- return;
- }
- }
- }
-
- // Okay, so here's where it gets complicated in regards to what errors
- // we emit and how.
- // There are 3 different "types" of errors we might encounter.
- // 1) Missing/extra/swapped arguments
- // 2) Valid but incorrect arguments
- // 3) Invalid arguments
- // - Currently I think this only comes up with `CyclicTy`
- //
- // We first need to go through, remove those from (3) and emit those
- // as their own error, particularly since they're error code and
- // message is special. From what I can tell, we *must* emit these
- // here (vs somewhere prior to this function) since the arguments
- // become invalid *because* of how they get used in the function.
- // It is what it is.
-
- if errors.is_empty() {
- if cfg!(debug_assertions) {
- span_bug!(error_span, "expected errors from argument matrix");
- } else {
- tcx.sess
- .struct_span_err(
- error_span,
- "argument type mismatch was detected, \
- but rustc had trouble determining where",
- )
- .note(
- "we would appreciate a bug report: \
- https://github.com/rust-lang/rust/issues/new",
- )
- .emit();
- }
- return;
- }
-
- errors.drain_filter(|error| {
- let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false };
- let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
- let (expected_ty, _) = formal_and_expected_inputs[*expected_idx];
- let cause = &self.misc(provided_span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
- if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) {
- self.report_and_explain_type_error(trace, *e).emit();
- return true;
- }
- false
- });
-
- // We're done if we found errors, but we already emitted them.
- if errors.is_empty() {
- return;
- }
-
- // Okay, now that we've emitted the special errors separately, we
- // are only left missing/extra/swapped and mismatched arguments, both
- // can be collated pretty easily if needed.
-
- // Next special case: if there is only one "Incompatible" error, just emit that
- if let [
- Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))),
- ] = &errors[..]
- {
- let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx];
- let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx];
- let cause = &self.misc(provided_arg_span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
- let mut err = self.report_and_explain_type_error(trace, *err);
- self.emit_coerce_suggestions(
- &mut err,
- &provided_args[*provided_idx],
- provided_ty,
- Expectation::rvalue_hint(self, expected_ty)
- .only_has_type(self)
- .unwrap_or(formal_ty),
- None,
- None,
- );
- err.span_label(
- full_call_span,
- format!("arguments to this {} are incorrect", call_name),
- );
- // Call out where the function is defined
- self.label_fn_like(
- &mut err,
- fn_def_id,
- callee_ty,
- Some(expected_idx.as_usize()),
- is_method,
- );
- err.emit();
- return;
- }
-
- let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
- struct_span_err!(
- tcx.sess,
- full_call_span,
- E0308,
- "arguments to this {} are incorrect",
- call_name,
- )
- } else {
- tcx.sess.struct_span_err_with_code(
- full_call_span,
- &format!(
- "this {} takes {}{} but {} {} supplied",
- call_name,
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
- potentially_plural_count(provided_args.len(), "argument"),
- pluralize!("was", provided_args.len())
- ),
- DiagnosticId::Error(err_code.to_owned()),
- )
- };
-
- // As we encounter issues, keep track of what we want to provide for the suggestion
- let mut labels = vec![];
- // If there is a single error, we give a specific suggestion; otherwise, we change to
- // "did you mean" with the suggested function call
- enum SuggestionText {
- None,
- Provide(bool),
- Remove(bool),
- Swap,
- Reorder,
- DidYouMean,
- }
- let mut suggestion_text = SuggestionText::None;
-
- let mut errors = errors.into_iter().peekable();
- while let Some(error) = errors.next() {
- match error {
- Error::Invalid(provided_idx, expected_idx, compatibility) => {
- let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
- let (provided_ty, provided_span) = provided_arg_tys[provided_idx];
- if let Compatibility::Incompatible(error) = compatibility {
- let cause = &self.misc(provided_span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
- if let Some(e) = error {
- self.note_type_err(
- &mut err,
- &trace.cause,
- None,
- Some(trace.values),
- e,
- false,
- true,
- );
- }
- }
-
- self.emit_coerce_suggestions(
- &mut err,
- &provided_args[provided_idx],
- provided_ty,
- Expectation::rvalue_hint(self, expected_ty)
- .only_has_type(self)
- .unwrap_or(formal_ty),
- None,
- None,
- );
- }
- Error::Extra(arg_idx) => {
- let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
- let provided_ty_name = if !has_error_or_infer([provided_ty]) {
- // FIXME: not suggestable, use something else
- format!(" of type `{}`", provided_ty)
- } else {
- "".to_string()
- };
- labels
- .push((provided_span, format!("argument{} unexpected", provided_ty_name)));
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Remove(false),
- SuggestionText::Remove(_) => SuggestionText::Remove(true),
- _ => SuggestionText::DidYouMean,
- };
- }
- Error::Missing(expected_idx) => {
- // If there are multiple missing arguments adjacent to each other,
- // then we can provide a single error.
-
- let mut missing_idxs = vec![expected_idx];
- while let Some(e) = errors.next_if(|e| {
- matches!(e, Error::Missing(next_expected_idx)
- if *next_expected_idx == *missing_idxs.last().unwrap() + 1)
- }) {
- match e {
- Error::Missing(expected_idx) => missing_idxs.push(expected_idx),
- _ => unreachable!(),
- }
- }
-
- // NOTE: Because we might be re-arranging arguments, might have extra
- // arguments, etc. it's hard to *really* know where we should provide
- // this error label, so as a heuristic, we point to the provided arg, or
- // to the call if the missing inputs pass the provided args.
- match &missing_idxs[..] {
- &[expected_idx] => {
- let (_, input_ty) = formal_and_expected_inputs[expected_idx];
- let span = if let Some((_, arg_span)) =
- provided_arg_tys.get(expected_idx.to_provided_idx())
- {
- *arg_span
- } else {
- args_span
- };
- let rendered = if !has_error_or_infer([input_ty]) {
- format!(" of type `{}`", input_ty)
- } else {
- "".to_string()
- };
- labels.push((span, format!("an argument{} is missing", rendered)));
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Provide(false),
- SuggestionText::Provide(_) => SuggestionText::Provide(true),
- _ => SuggestionText::DidYouMean,
- };
- }
- &[first_idx, second_idx] => {
- let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
- let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
- let span = if let (Some((_, first_span)), Some((_, second_span))) = (
- provided_arg_tys.get(first_idx.to_provided_idx()),
- provided_arg_tys.get(second_idx.to_provided_idx()),
- ) {
- first_span.to(*second_span)
- } else {
- args_span
- };
- let rendered =
- if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
- format!(
- " of type `{}` and `{}`",
- first_expected_ty, second_expected_ty
- )
- } else {
- "".to_string()
- };
- labels.push((span, format!("two arguments{} are missing", rendered)));
- suggestion_text = match suggestion_text {
- SuggestionText::None | SuggestionText::Provide(_) => {
- SuggestionText::Provide(true)
- }
- _ => SuggestionText::DidYouMean,
- };
- }
- &[first_idx, second_idx, third_idx] => {
- let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
- let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
- let (_, third_expected_ty) = formal_and_expected_inputs[third_idx];
- let span = if let (Some((_, first_span)), Some((_, third_span))) = (
- provided_arg_tys.get(first_idx.to_provided_idx()),
- provided_arg_tys.get(third_idx.to_provided_idx()),
- ) {
- first_span.to(*third_span)
- } else {
- args_span
- };
- let rendered = if !has_error_or_infer([
- first_expected_ty,
- second_expected_ty,
- third_expected_ty,
- ]) {
- format!(
- " of type `{}`, `{}`, and `{}`",
- first_expected_ty, second_expected_ty, third_expected_ty
- )
- } else {
- "".to_string()
- };
- labels.push((span, format!("three arguments{} are missing", rendered)));
- suggestion_text = match suggestion_text {
- SuggestionText::None | SuggestionText::Provide(_) => {
- SuggestionText::Provide(true)
- }
- _ => SuggestionText::DidYouMean,
- };
- }
- missing_idxs => {
- let first_idx = *missing_idxs.first().unwrap();
- let last_idx = *missing_idxs.last().unwrap();
- // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
- // It's hard to *really* know where we should provide this error label, so this is a
- // decent heuristic
- let span = if let (Some((_, first_span)), Some((_, last_span))) = (
- provided_arg_tys.get(first_idx.to_provided_idx()),
- provided_arg_tys.get(last_idx.to_provided_idx()),
- ) {
- first_span.to(*last_span)
- } else {
- args_span
- };
- labels.push((span, format!("multiple arguments are missing")));
- suggestion_text = match suggestion_text {
- SuggestionText::None | SuggestionText::Provide(_) => {
- SuggestionText::Provide(true)
- }
- _ => SuggestionText::DidYouMean,
- };
- }
- }
- }
- Error::Swap(
- first_provided_idx,
- second_provided_idx,
- first_expected_idx,
- second_expected_idx,
- ) => {
- let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx];
- let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx];
- let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
- format!(", found `{}`", first_provided_ty)
- } else {
- String::new()
- };
- labels.push((
- first_span,
- format!("expected `{}`{}", first_expected_ty, first_provided_ty_name),
- ));
-
- let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx];
- let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx];
- let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
- format!(", found `{}`", second_provided_ty)
- } else {
- String::new()
- };
- labels.push((
- second_span,
- format!("expected `{}`{}", second_expected_ty, second_provided_ty_name),
- ));
-
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Swap,
- _ => SuggestionText::DidYouMean,
- };
- }
- Error::Permutation(args) => {
- for (dst_arg, dest_input) in args {
- let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
- let (provided_ty, provided_span) = provided_arg_tys[dest_input];
- let provided_ty_name = if !has_error_or_infer([provided_ty]) {
- format!(", found `{}`", provided_ty)
- } else {
- String::new()
- };
- labels.push((
- provided_span,
- format!("expected `{}`{}", expected_ty, provided_ty_name),
- ));
- }
-
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Reorder,
- _ => SuggestionText::DidYouMean,
- };
- }
- }
- }
-
- // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
- if labels.len() <= 5 {
- for (span, label) in labels {
- err.span_label(span, label);
- }
- }
-
- // Call out where the function is defined
- self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);
-
- // And add a suggestion block for all of the parameters
- let suggestion_text = match suggestion_text {
- SuggestionText::None => None,
- SuggestionText::Provide(plural) => {
- Some(format!("provide the argument{}", if plural { "s" } else { "" }))
- }
- SuggestionText::Remove(plural) => {
- Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
- }
- SuggestionText::Swap => Some("swap these arguments".to_string()),
- SuggestionText::Reorder => Some("reorder these arguments".to_string()),
- SuggestionText::DidYouMean => Some("did you mean".to_string()),
- };
- if let Some(suggestion_text) = suggestion_text {
- let source_map = self.sess().source_map();
- let (mut suggestion, suggestion_span) =
- if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) {
- ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
- } else {
- (
- format!(
- "{}(",
- source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
- fn_def_id.map_or("".to_string(), |fn_def_id| {
- tcx.item_name(fn_def_id).to_string()
- })
- })
- ),
- error_span,
- )
- };
- let mut needs_comma = false;
- for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
- if needs_comma {
- suggestion += ", ";
- } else {
- needs_comma = true;
- }
- let suggestion_text = if let Some(provided_idx) = provided_idx
- && let (_, provided_span) = provided_arg_tys[*provided_idx]
- && let Ok(arg_text) = source_map.span_to_snippet(provided_span)
- {
- arg_text
- } else {
- // Propose a placeholder of the correct type
- let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
- if expected_ty.is_unit() {
- "()".to_string()
- } else if expected_ty.is_suggestable(tcx, false) {
- format!("/* {} */", expected_ty)
- } else {
- "/* value */".to_string()
- }
- };
- suggestion += &suggestion_text;
- }
- suggestion += ")";
- err.span_suggestion_verbose(
- suggestion_span,
- &suggestion_text,
- suggestion,
- Applicability::HasPlaceholders,
- );
- }
-
- err.emit();
- }
-
- // AST fragment checking
- pub(in super::super) fn check_lit(
- &self,
- lit: &hir::Lit,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
-
- match lit.node {
- ast::LitKind::Str(..) => tcx.mk_static_str(),
- ast::LitKind::ByteStr(ref v) => {
- tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
- }
- ast::LitKind::Byte(_) => tcx.types.u8,
- ast::LitKind::Char(_) => tcx.types.char,
- ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)),
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)),
- ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
- let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
- ty::Int(_) | ty::Uint(_) => Some(ty),
- ty::Char => Some(tcx.types.u8),
- ty::RawPtr(..) => Some(tcx.types.usize),
- ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
- _ => None,
- });
- opt_ty.unwrap_or_else(|| self.next_int_var())
- }
- ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => {
- tcx.mk_mach_float(ty::float_ty(t))
- }
- ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
- let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
- ty::Float(_) => Some(ty),
- _ => None,
- });
- opt_ty.unwrap_or_else(|| self.next_float_var())
- }
- ast::LitKind::Bool(_) => tcx.types.bool,
- ast::LitKind::Err => tcx.ty_error(),
- }
- }
-
- pub fn check_struct_path(
- &self,
- qpath: &QPath<'_>,
- hir_id: hir::HirId,
- ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
- let path_span = qpath.span();
- let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
- let variant = match def {
- Res::Err => {
- self.set_tainted_by_errors();
- return None;
- }
- Res::Def(DefKind::Variant, _) => match ty.kind() {
- ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)),
- _ => bug!("unexpected type: {:?}", ty),
- },
- Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
- | Res::SelfTy { .. } => match ty.kind() {
- ty::Adt(adt, substs) if !adt.is_enum() => {
- Some((adt.non_enum_variant(), adt.did(), substs))
- }
- _ => None,
- },
- _ => bug!("unexpected definition: {:?}", def),
- };
-
- if let Some((variant, did, substs)) = variant {
- debug!("check_struct_path: did={:?} substs={:?}", did, substs);
- self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
-
- // Check bounds on type arguments used in the path.
- self.add_required_obligations_for_hir(path_span, did, substs, hir_id);
-
- Some((variant, ty))
- } else {
- match ty.kind() {
- ty::Error(_) => {
- // E0071 might be caused by a spelling error, which will have
- // already caused an error message and probably a suggestion
- // elsewhere. Refrain from emitting more unhelpful errors here
- // (issue #88844).
- }
- _ => {
- struct_span_err!(
- self.tcx.sess,
- path_span,
- E0071,
- "expected struct, variant or union type, found {}",
- ty.sort_string(self.tcx)
- )
- .span_label(path_span, "not a struct")
- .emit();
- }
- }
- None
- }
- }
-
- pub fn check_decl_initializer(
- &self,
- hir_id: hir::HirId,
- pat: &'tcx hir::Pat<'tcx>,
- init: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
- // for #42640 (default match binding modes).
- //
- // See #44848.
- let ref_bindings = pat.contains_explicit_ref_binding();
-
- let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
- if let Some(m) = ref_bindings {
- // Somewhat subtle: if we have a `ref` binding in the pattern,
- // we want to avoid introducing coercions for the RHS. This is
- // both because it helps preserve sanity and, in the case of
- // ref mut, for soundness (issue #23116). In particular, in
- // the latter case, we need to be clear that the type of the
- // referent for the reference that results is *equal to* the
- // type of the place it is referencing, and not some
- // supertype thereof.
- let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
- self.demand_eqtype(init.span, local_ty, init_ty);
- init_ty
- } else {
- self.check_expr_coercable_to_type(init, local_ty, None)
- }
- }
-
- pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
- // Determine and write the type which we'll check the pattern against.
- let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
- self.write_ty(decl.hir_id, decl_ty);
-
- // Type check the initializer.
- if let Some(ref init) = decl.init {
- let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init);
- self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty);
- }
-
- // Does the expected pattern type originate from an expression and what is the span?
- let (origin_expr, ty_span) = match (decl.ty, decl.init) {
- (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
- (_, Some(init)) => {
- (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
- } // No explicit type; so use the scrutinee.
- _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
- };
-
- // Type check the pattern. Override if necessary to avoid knock-on errors.
- self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
- let pat_ty = self.node_ty(decl.pat.hir_id);
- self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty);
-
- if let Some(blk) = decl.els {
- let previous_diverges = self.diverges.get();
- let else_ty = self.check_block_with_expected(blk, NoExpectation);
- let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
- if let Some(mut err) =
- self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
- {
- err.emit();
- }
- self.diverges.set(previous_diverges);
- }
- }
-
- /// Type check a `let` statement.
- pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
- self.check_decl(local.into());
- }
-
- pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) {
- // Don't do all the complex logic below for `DeclItem`.
- match stmt.kind {
- hir::StmtKind::Item(..) => return,
- hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
- }
-
- self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
-
- // Hide the outer diverging and `has_errors` flags.
- let old_diverges = self.diverges.replace(Diverges::Maybe);
- let old_has_errors = self.has_errors.replace(false);
-
- match stmt.kind {
- hir::StmtKind::Local(l) => {
- self.check_decl_local(l);
- }
- // Ignore for now.
- hir::StmtKind::Item(_) => {}
- hir::StmtKind::Expr(ref expr) => {
- // Check with expected type of `()`.
- self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
- if expr.can_have_side_effects() {
- self.suggest_semicolon_at_end(expr.span, err);
- }
- });
- }
- hir::StmtKind::Semi(ref expr) => {
- // All of this is equivalent to calling `check_expr`, but it is inlined out here
- // in order to capture the fact that this `match` is the last statement in its
- // function. This is done for better suggestions to remove the `;`.
- let expectation = match expr.kind {
- hir::ExprKind::Match(..) if is_last => IsLast(stmt.span),
- _ => NoExpectation,
- };
- self.check_expr_with_expectation(expr, expectation);
- }
- }
-
- // Combine the diverging and `has_error` flags.
- self.diverges.set(self.diverges.get() | old_diverges);
- self.has_errors.set(self.has_errors.get() | old_has_errors);
- }
-
- pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
- let unit = self.tcx.mk_unit();
- let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
-
- // if the block produces a `!` value, that can always be
- // (effectively) coerced to unit.
- if !ty.is_never() {
- self.demand_suptype(blk.span, unit, ty);
- }
- }
-
- pub(in super::super) fn check_block_with_expected(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let prev = self.ps.replace(self.ps.get().recurse(blk));
-
- // In some cases, blocks have just one exit, but other blocks
- // can be targeted by multiple breaks. This can happen both
- // with labeled blocks as well as when we desugar
- // a `try { ... }` expression.
- //
- // Example 1:
- //
- // 'a: { if true { break 'a Err(()); } Ok(()) }
- //
- // Here we would wind up with two coercions, one from
- // `Err(())` and the other from the tail expression
- // `Ok(())`. If the tail expression is omitted, that's a
- // "forced unit" -- unless the block diverges, in which
- // case we can ignore the tail expression (e.g., `'a: {
- // break 'a 22; }` would not force the type of the block
- // to be `()`).
- let tail_expr = blk.expr.as_ref();
- let coerce_to_ty = expected.coercion_target_type(self, blk.span);
- let coerce = if blk.targeted_by_break {
- CoerceMany::new(coerce_to_ty)
- } else {
- let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
- Some(e) => slice::from_ref(e),
- None => &[],
- };
- CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
- };
-
- let prev_diverges = self.diverges.get();
- let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
-
- let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
- for (pos, s) in blk.stmts.iter().enumerate() {
- self.check_stmt(s, blk.stmts.len() - 1 == pos);
- }
-
- // check the tail expression **without** holding the
- // `enclosing_breakables` lock below.
- let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
-
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
- let coerce = ctxt.coerce.as_mut().unwrap();
- if let Some(tail_expr_ty) = tail_expr_ty {
- let tail_expr = tail_expr.unwrap();
- let span = self.get_expr_coercion_span(tail_expr);
- let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
- let ty_for_diagnostic = coerce.merged_ty();
- // We use coerce_inner here because we want to augment the error
- // suggesting to wrap the block in square brackets if it might've
- // been mistaken array syntax
- coerce.coerce_inner(
- self,
- &cause,
- Some(tail_expr),
- tail_expr_ty,
- Some(&mut |diag: &mut Diagnostic| {
- self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic);
- }),
- false,
- );
- } else {
- // Subtle: if there is no explicit tail expression,
- // that is typically equivalent to a tail expression
- // of `()` -- except if the block diverges. In that
- // case, there is no value supplied from the tail
- // expression (assuming there are no other breaks,
- // this implies that the type of the block will be
- // `!`).
- //
- // #41425 -- label the implicit `()` as being the
- // "found type" here, rather than the "expected type".
- if !self.diverges.get().is_always() {
- // #50009 -- Do not point at the entire fn block span, point at the return type
- // span, as it is the cause of the requirement, and
- // `consider_hint_about_removing_semicolon` will point at the last expression
- // if it were a relevant part of the error. This improves usability in editors
- // that highlight errors inline.
- let mut sp = blk.span;
- let mut fn_span = None;
- if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
- let ret_sp = decl.output.span();
- if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
- // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
- // output would otherwise be incorrect and even misleading. Make sure
- // the span we're aiming at correspond to a `fn` body.
- if block_sp == blk.span {
- sp = ret_sp;
- fn_span = Some(ident.span);
- }
- }
- }
- coerce.coerce_forced_unit(
- self,
- &self.misc(sp),
- &mut |err| {
- if let Some(expected_ty) = expected.only_has_type(self) {
- if !self.consider_removing_semicolon(blk, expected_ty, err) {
- self.consider_returning_binding(blk, expected_ty, err);
- }
- if expected_ty == self.tcx.types.bool {
- // If this is caused by a missing `let` in a `while let`,
- // silence this redundant error, as we already emit E0070.
-
- // Our block must be a `assign desugar local; assignment`
- if let Some(hir::Node::Block(hir::Block {
- stmts:
- [
- hir::Stmt {
- kind:
- hir::StmtKind::Local(hir::Local {
- source:
- hir::LocalSource::AssignDesugar(_),
- ..
- }),
- ..
- },
- hir::Stmt {
- kind:
- hir::StmtKind::Expr(hir::Expr {
- kind: hir::ExprKind::Assign(..),
- ..
- }),
- ..
- },
- ],
- ..
- })) = self.tcx.hir().find(blk.hir_id)
- {
- self.comes_from_while_condition(blk.hir_id, |_| {
- err.downgrade_to_delayed_bug();
- })
- }
- }
- }
- if let Some(fn_span) = fn_span {
- err.span_label(
- fn_span,
- "implicitly returns `()` as its body has no tail or `return` \
- expression",
- );
- }
- },
- false,
- );
- }
- }
- });
-
- if ctxt.may_break {
- // If we can break from the block, then the block's exit is always reachable
- // (... as long as the entry is reachable) - regardless of the tail of the block.
- self.diverges.set(prev_diverges);
- }
-
- let mut ty = ctxt.coerce.unwrap().complete(self);
-
- if self.has_errors.get() || ty.references_error() {
- ty = self.tcx.ty_error()
- }
-
- self.write_ty(blk.hir_id, ty);
-
- self.ps.set(prev);
- ty
- }
-
- fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
- let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id));
- match node {
- Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
- | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
- let body = self.tcx.hir().body(body_id);
- if let ExprKind::Block(block, _) = &body.value.kind {
- return Some(block.span);
- }
- }
- _ => {}
- }
- None
- }
-
- /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
- fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
- let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id));
- self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
- }
-
- /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
- /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
- /// when given code like the following:
- /// ```text
- /// if false { return 0i32; } else { 1u32 }
- /// // ^^^^ point at this instead of the whole `if` expression
- /// ```
- fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
- let check_in_progress = |elem: &hir::Expr<'_>| {
- self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map(
- |_| match elem.kind {
- // Point at the tail expression when possible.
- hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span),
- _ => elem.span,
- },
- )
- };
-
- if let hir::ExprKind::If(_, _, Some(el)) = expr.kind {
- if let Some(rslt) = check_in_progress(el) {
- return rslt;
- }
- }
-
- if let hir::ExprKind::Match(_, arms, _) = expr.kind {
- let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body));
- if let Some(span) = iter.next() {
- if iter.next().is_none() {
- return span;
- }
- }
- }
-
- expr.span
- }
-
- fn overwrite_local_ty_if_err(
- &self,
- hir_id: hir::HirId,
- pat: &'tcx hir::Pat<'tcx>,
- decl_ty: Ty<'tcx>,
- ty: Ty<'tcx>,
- ) {
- if ty.references_error() {
- // Override the types everywhere with `err()` to avoid knock on errors.
- self.write_ty(hir_id, ty);
- self.write_ty(pat.hir_id, ty);
- let local_ty = LocalTy { decl_ty, revealed_ty: ty };
- self.locals.borrow_mut().insert(hir_id, local_ty);
- self.locals.borrow_mut().insert(pat.hir_id, local_ty);
- }
- }
-
- // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
- // The newly resolved definition is written into `type_dependent_defs`.
- fn finish_resolving_struct_path(
- &self,
- qpath: &QPath<'_>,
- path_span: Span,
- hir_id: hir::HirId,
- ) -> (Res, Ty<'tcx>) {
- match *qpath {
- QPath::Resolved(ref maybe_qself, ref path) => {
- let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
- let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true);
- (path.res, ty)
- }
- QPath::TypeRelative(ref qself, ref segment) => {
- let ty = self.to_ty(qself);
-
- let result = <dyn AstConv<'_>>::associated_path_to_ty(
- self, hir_id, path_span, ty, qself, segment, true,
- );
- let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
- let result = result.map(|(_, kind, def_id)| (kind, def_id));
-
- // Write back the new resolution.
- self.write_resolution(hir_id, result);
-
- (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty)
- }
- QPath::LangItem(lang_item, span, id) => {
- self.resolve_lang_item_path(lang_item, span, hir_id, id)
- }
- }
- }
-
- /// Given a vector of fulfillment errors, try to adjust the spans of the
- /// errors to more accurately point at the cause of the failure.
- ///
- /// This applies to calls, methods, and struct expressions. This will also
- /// try to deduplicate errors that are due to the same cause but might
- /// have been created with different [`ObligationCause`][traits::ObligationCause]s.
- pub(super) fn adjust_fulfillment_errors_for_expr_obligation(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- ) {
- // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that
- // other errors that have the same span and predicate can also get fixed,
- // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind.
- // This is important since if we adjust one span but not the other, then
- // we will have "duplicated" the error on the UI side.
- let mut remap_cause = FxHashSet::default();
- let mut not_adjusted = vec![];
-
- for error in errors {
- let before_span = error.obligation.cause.span;
- if self.adjust_fulfillment_error_for_expr_obligation(error)
- || before_span != error.obligation.cause.span
- {
- // Store both the predicate and the predicate *without constness*
- // since sometimes we instantiate and check both of these in a
- // method call, for example.
- remap_cause.insert((
- before_span,
- error.obligation.predicate,
- error.obligation.cause.clone(),
- ));
- remap_cause.insert((
- before_span,
- error.obligation.predicate.without_const(self.tcx),
- error.obligation.cause.clone(),
- ));
- } else {
- // If it failed to be adjusted once around, it may be adjusted
- // via the "remap cause" mapping the second time...
- not_adjusted.push(error);
- }
- }
-
- for error in not_adjusted {
- for (span, predicate, cause) in &remap_cause {
- if *predicate == error.obligation.predicate
- && span.contains(error.obligation.cause.span)
- {
- error.obligation.cause = cause.clone();
- continue;
- }
- }
- }
- }
-
- fn adjust_fulfillment_error_for_expr_obligation(
- &self,
- error: &mut traits::FulfillmentError<'tcx>,
- ) -> bool {
- let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx))
- = *error.obligation.cause.code().peel_derives() else { return false; };
- let hir = self.tcx.hir();
- let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; };
-
- // Skip over mentioning async lang item
- if Some(def_id) == self.tcx.lang_items().from_generator_fn()
- && error.obligation.cause.span.desugaring_kind()
- == Some(rustc_span::DesugaringKind::Async)
- {
- return false;
- }
-
- let Some(unsubstituted_pred) =
- self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx)
- else { return false; };
-
- let generics = self.tcx.generics_of(def_id);
- let predicate_substs = match unsubstituted_pred.kind().skip_binder() {
- ty::PredicateKind::Trait(pred) => pred.trait_ref.substs,
- ty::PredicateKind::Projection(pred) => pred.projection_ty.substs,
- _ => ty::List::empty(),
- };
-
- let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| {
- predicate_substs.types().find_map(|ty| {
- ty.walk().find_map(|arg| {
- if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Param(param_ty) = ty.kind()
- && matches(param_ty)
- {
- Some(arg)
- } else {
- None
- }
- })
- })
- };
-
- // Prefer generics that are local to the fn item, since these are likely
- // to be the cause of the unsatisfied predicate.
- let mut param_to_point_at = find_param_matching(&|param_ty| {
- self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id
- });
- // Fall back to generic that isn't local to the fn item. This will come
- // from a trait or impl, for example.
- let mut fallback_param_to_point_at = find_param_matching(&|param_ty| {
- self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id
- && param_ty.name != rustc_span::symbol::kw::SelfUpper
- });
- // Finally, the `Self` parameter is possibly the reason that the predicate
- // is unsatisfied. This is less likely to be true for methods, because
- // method probe means that we already kinda check that the predicates due
- // to the `Self` type are true.
- let mut self_param_to_point_at =
- find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper);
-
- // Finally, for ambiguity-related errors, we actually want to look
- // for a parameter that is the source of the inference type left
- // over in this predicate.
- if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code {
- fallback_param_to_point_at = None;
- self_param_to_point_at = None;
- param_to_point_at =
- self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate);
- }
-
- if self.closure_span_overlaps_error(error, expr.span) {
- return false;
- }
-
- match &expr.kind {
- hir::ExprKind::Path(qpath) => {
- if let hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Call(callee, args),
- hir_id: call_hir_id,
- span: call_span,
- ..
- }) = hir.get(hir.get_parent_node(expr.hir_id))
- && callee.hir_id == expr.hir_id
- {
- if self.closure_span_overlaps_error(error, *call_span) {
- return false;
- }
-
- for param in
- [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
- .into_iter()
- .flatten()
- {
- if self.point_at_arg_if_possible(
- error,
- def_id,
- param,
- *call_hir_id,
- callee.span,
- None,
- args,
- )
- {
- return true;
- }
- }
- }
- // Notably, we only point to params that are local to the
- // item we're checking, since those are the ones we are able
- // to look in the final `hir::PathSegment` for. Everything else
- // would require a deeper search into the `qpath` than I think
- // is worthwhile.
- if let Some(param_to_point_at) = param_to_point_at
- && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath)
- {
- return true;
- }
- }
- hir::ExprKind::MethodCall(segment, receiver, args, ..) => {
- for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
- .into_iter()
- .flatten()
- {
- if self.point_at_arg_if_possible(
- error,
- def_id,
- param,
- hir_id,
- segment.ident.span,
- Some(receiver),
- args,
- ) {
- return true;
- }
- }
- if let Some(param_to_point_at) = param_to_point_at
- && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment)
- {
- return true;
- }
- }
- hir::ExprKind::Struct(qpath, fields, ..) => {
- if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) =
- self.typeck_results.borrow().qpath_res(qpath, hir_id)
- {
- for param in
- [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
- {
- if let Some(param) = param
- && self.point_at_field_if_possible(
- error,
- def_id,
- param,
- variant_def_id,
- fields,
- )
- {
- return true;
- }
- }
- }
- if let Some(param_to_point_at) = param_to_point_at
- && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath)
- {
- return true;
- }
- }
- _ => {}
- }
-
- false
- }
-
- fn closure_span_overlaps_error(
- &self,
- error: &traits::FulfillmentError<'tcx>,
- span: Span,
- ) -> bool {
- if let traits::FulfillmentErrorCode::CodeSelectionError(
- traits::SelectionError::OutputTypeParameterMismatch(_, expected, _),
- ) = error.code
- && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind()
- && span.overlaps(self.tcx.def_span(*def_id))
- {
- true
- } else {
- false
- }
- }
-
- fn point_at_arg_if_possible(
- &self,
- error: &mut traits::FulfillmentError<'tcx>,
- def_id: DefId,
- param_to_point_at: ty::GenericArg<'tcx>,
- call_hir_id: hir::HirId,
- callee_span: Span,
- receiver: Option<&'tcx hir::Expr<'tcx>>,
- args: &'tcx [hir::Expr<'tcx>],
- ) -> bool {
- let sig = self.tcx.fn_sig(def_id).skip_binder();
- let args_referencing_param: Vec<_> = sig
- .inputs()
- .iter()
- .enumerate()
- .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
- .collect();
- // If there's one field that references the given generic, great!
- if let [(idx, _)] = args_referencing_param.as_slice()
- && let Some(arg) = receiver
- .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
- error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
- error.obligation.cause.map_code(|parent_code| {
- ObligationCauseCode::FunctionArgumentObligation {
- arg_hir_id: arg.hir_id,
- call_hir_id,
- parent_code,
- }
- });
- return true;
- } else if args_referencing_param.len() > 0 {
- // If more than one argument applies, then point to the callee span at least...
- // We have chance to fix this up further in `point_at_generics_if_possible`
- error.obligation.cause.span = callee_span;
- }
-
- false
- }
-
- fn point_at_field_if_possible(
- &self,
- error: &mut traits::FulfillmentError<'tcx>,
- def_id: DefId,
- param_to_point_at: ty::GenericArg<'tcx>,
- variant_def_id: DefId,
- expr_fields: &[hir::ExprField<'tcx>],
- ) -> bool {
- let def = self.tcx.adt_def(def_id);
-
- let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id);
- let fields_referencing_param: Vec<_> = def
- .variant_with_id(variant_def_id)
- .fields
- .iter()
- .filter(|field| {
- let field_ty = field.ty(self.tcx, identity_substs);
- find_param_in_ty(field_ty, param_to_point_at)
- })
- .collect();
-
- if let [field] = fields_referencing_param.as_slice() {
- for expr_field in expr_fields {
- // Look for the ExprField that matches the field, using the
- // same rules that check_expr_struct uses for macro hygiene.
- if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx)
- {
- error.obligation.cause.span = expr_field
- .expr
- .span
- .find_ancestor_in_same_ctxt(error.obligation.cause.span)
- .unwrap_or(expr_field.span);
- return true;
- }
- }
- }
-
- false
- }
-
- fn point_at_path_if_possible(
- &self,
- error: &mut traits::FulfillmentError<'tcx>,
- def_id: DefId,
- param: ty::GenericArg<'tcx>,
- qpath: &QPath<'tcx>,
- ) -> bool {
- match qpath {
- hir::QPath::Resolved(_, path) => {
- if let Some(segment) = path.segments.last()
- && self.point_at_generic_if_possible(error, def_id, param, segment)
- {
- return true;
- }
- }
- hir::QPath::TypeRelative(_, segment) => {
- if self.point_at_generic_if_possible(error, def_id, param, segment) {
- return true;
- }
- }
- _ => {}
- }
-
- false
- }
-
- fn point_at_generic_if_possible(
- &self,
- error: &mut traits::FulfillmentError<'tcx>,
- def_id: DefId,
- param_to_point_at: ty::GenericArg<'tcx>,
- segment: &hir::PathSegment<'tcx>,
- ) -> bool {
- let own_substs = self
- .tcx
- .generics_of(def_id)
- .own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id));
- let Some((index, _)) = own_substs
- .iter()
- .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_)))
- .enumerate()
- .find(|(_, arg)| **arg == param_to_point_at) else { return false };
- let Some(arg) = segment
- .args()
- .args
- .iter()
- .filter(|arg| matches!(arg, hir::GenericArg::Type(_)))
- .nth(index) else { return false; };
- error.obligation.cause.span = arg
- .span()
- .find_ancestor_in_same_ctxt(error.obligation.cause.span)
- .unwrap_or(arg.span());
- true
- }
-
- fn find_ambiguous_parameter_in<T: TypeVisitable<'tcx>>(
- &self,
- item_def_id: DefId,
- t: T,
- ) -> Option<ty::GenericArg<'tcx>> {
- struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId);
- impl<'tcx> TypeVisitor<'tcx> for FindAmbiguousParameter<'_, 'tcx> {
- type BreakTy = ty::GenericArg<'tcx>;
- fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
- if let Some(origin) = self.0.type_var_origin(ty)
- && let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) =
- origin.kind
- && let generics = self.0.tcx.generics_of(self.1)
- && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id)
- && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1)
- .get(index as usize)
- {
- ControlFlow::Break(*subst)
- } else {
- ty.super_visit_with(self)
- }
- }
- }
- t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value()
- }
-
- fn label_fn_like(
- &self,
- err: &mut Diagnostic,
- callable_def_id: Option<DefId>,
- callee_ty: Option<Ty<'tcx>>,
- // A specific argument should be labeled, instead of all of them
- expected_idx: Option<usize>,
- is_method: bool,
- ) {
- let Some(mut def_id) = callable_def_id else {
- return;
- };
-
- if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
- // Possibly points at either impl or trait item, so try to get it
- // to point to trait item, then get the parent.
- // This parent might be an impl in the case of an inherent function,
- // but the next check will fail.
- && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id)
- && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id)
- // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
- && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id)
- && let Some(callee_ty) = callee_ty
- {
- let callee_ty = callee_ty.peel_refs();
- match *callee_ty.kind() {
- ty::Param(param) => {
- let param =
- self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx);
- if param.kind.is_synthetic() {
- // if it's `impl Fn() -> ..` then just fall down to the def-id based logic
- def_id = param.def_id;
- } else {
- // Otherwise, find the predicate that makes this generic callable,
- // and point at that.
- let instantiated = self
- .tcx
- .explicit_predicates_of(self.body_id.owner)
- .instantiate_identity(self.tcx);
- // FIXME(compiler-errors): This could be problematic if something has two
- // fn-like predicates with different args, but callable types really never
- // do that, so it's OK.
- for (predicate, span) in
- std::iter::zip(instantiated.predicates, instantiated.spans)
- {
- if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
- && pred.self_ty().peel_refs() == callee_ty
- && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
- {
- err.span_note(span, "callable defined here");
- return;
- }
- }
- }
- }
- ty::Opaque(new_def_id, _)
- | ty::Closure(new_def_id, _)
- | ty::FnDef(new_def_id, _) => {
- def_id = new_def_id;
- }
- _ => {
- // Look for a user-provided impl of a `Fn` trait, and point to it.
- let new_def_id = self.probe(|_| {
- let trait_ref = ty::TraitRef::new(
- call_kind.to_def_id(self.tcx),
- self.tcx.mk_substs(
- [
- ty::GenericArg::from(callee_ty),
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: rustc_span::DUMMY_SP,
- })
- .into(),
- ]
- .into_iter(),
- ),
- );
- let obligation = traits::Obligation::new(
- traits::ObligationCause::dummy(),
- self.param_env,
- ty::Binder::dummy(ty::TraitPredicate {
- trait_ref,
- constness: ty::BoundConstness::NotConst,
- polarity: ty::ImplPolarity::Positive,
- }),
- );
- match SelectionContext::new(&self).select(&obligation) {
- Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
- Some(impl_source.impl_def_id)
- }
- _ => None,
- }
- });
- if let Some(new_def_id) = new_def_id {
- def_id = new_def_id;
- } else {
- return;
- }
- }
- }
- }
-
- if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
- let mut spans: MultiSpan = def_span.into();
-
- let params = self
- .tcx
- .hir()
- .get_if_local(def_id)
- .and_then(|node| node.body_id())
- .into_iter()
- .flat_map(|id| self.tcx.hir().body(id).params)
- .skip(if is_method { 1 } else { 0 });
-
- for (_, param) in params
- .into_iter()
- .enumerate()
- .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx))
- {
- spans.push_span_label(param.span, "");
- }
-
- let def_kind = self.tcx.def_kind(def_id);
- err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
- } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id)
- && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind
- {
- let param = expected_idx
- .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
- let (kind, span) = if let Some(param) = param {
- ("closure parameter", param.span)
- } else {
- ("closure", self.tcx.def_span(def_id))
- };
- err.span_note(span, &format!("{} defined here", kind));
- } else {
- let def_kind = self.tcx.def_kind(def_id);
- err.span_note(
- self.tcx.def_span(def_id),
- &format!("{} defined here", def_kind.descr(def_id)),
- );
- }
- }
-}
-
-fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool {
- let mut walk = ty.walk();
- while let Some(arg) = walk.next() {
- if arg == param_to_point_at {
- return true;
- } else if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Projection(..) = ty.kind()
- {
- // This logic may seem a bit strange, but typically when
- // we have a projection type in a function signature, the
- // argument that's being passed into that signature is
- // not actually constraining that projection's substs in
- // a meaningful way. So we skip it, and see improvements
- // in some UI tests.
- walk.skip_current_subtree();
- }
- }
- false
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs
deleted file mode 100644
index 0e22971d3..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs
+++ /dev/null
@@ -1,304 +0,0 @@
-mod _impl;
-mod arg_matrix;
-mod checks;
-mod suggestions;
-
-pub use _impl::*;
-pub use suggestions::*;
-
-use crate::astconv::AstConv;
-use crate::check::coercion::DynamicCoerceMany;
-use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};
-
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Const, Ty, TyCtxt};
-use rustc_session::Session;
-use rustc_span::symbol::Ident;
-use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
-
-use std::cell::{Cell, RefCell};
-use std::ops::Deref;
-
-/// The `FnCtxt` stores type-checking context needed to type-check bodies of
-/// functions, closures, and `const`s, including performing type inference
-/// with [`InferCtxt`].
-///
-/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures*
-/// and thus does not perform type inference.
-///
-/// See [`ItemCtxt`]'s docs for more.
-///
-/// [`ItemCtxt`]: crate::collect::ItemCtxt
-/// [`InferCtxt`]: infer::InferCtxt
-pub struct FnCtxt<'a, 'tcx> {
- pub(super) body_id: hir::HirId,
-
- /// The parameter environment used for proving trait obligations
- /// in this function. This can change when we descend into
- /// closures (as they bring new things into scope), hence it is
- /// not part of `Inherited` (as of the time of this writing,
- /// closures do not yet change the environment, but they will
- /// eventually).
- pub(super) param_env: ty::ParamEnv<'tcx>,
-
- /// Number of errors that had been reported when we started
- /// checking this function. On exit, if we find that *more* errors
- /// have been reported, we will skip regionck and other work that
- /// expects the types within the function to be consistent.
- // FIXME(matthewjasper) This should not exist, and it's not correct
- // if type checking is run in parallel.
- err_count_on_creation: usize,
-
- /// If `Some`, this stores coercion information for returned
- /// expressions. If `None`, this is in a context where return is
- /// inappropriate, such as a const expression.
- ///
- /// This is a `RefCell<DynamicCoerceMany>`, which means that we
- /// can track all the return expressions and then use them to
- /// compute a useful coercion from the set, similar to a match
- /// expression or other branching context. You can use methods
- /// like `expected_ty` to access the declared return type (if
- /// any).
- pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
-
- /// Used exclusively to reduce cost of advanced evaluation used for
- /// more helpful diagnostics.
- pub(super) in_tail_expr: bool,
-
- /// First span of a return site that we find. Used in error messages.
- pub(super) ret_coercion_span: Cell<Option<Span>>,
-
- pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
-
- pub(super) ps: Cell<UnsafetyState>,
-
- /// Whether the last checked node generates a divergence (e.g.,
- /// `return` will set this to `Always`). In general, when entering
- /// an expression or other node in the tree, the initial value
- /// indicates whether prior parts of the containing expression may
- /// have diverged. It is then typically set to `Maybe` (and the
- /// old value remembered) for processing the subparts of the
- /// current expression. As each subpart is processed, they may set
- /// the flag to `Always`, etc. Finally, at the end, we take the
- /// result and "union" it with the original value, so that when we
- /// return the flag indicates if any subpart of the parent
- /// expression (up to and including this part) has diverged. So,
- /// if you read it after evaluating a subexpression `X`, the value
- /// you get indicates whether any subexpression that was
- /// evaluating up to and including `X` diverged.
- ///
- /// We currently use this flag only for diagnostic purposes:
- ///
- /// - To warn about unreachable code: if, after processing a
- /// sub-expression but before we have applied the effects of the
- /// current node, we see that the flag is set to `Always`, we
- /// can issue a warning. This corresponds to something like
- /// `foo(return)`; we warn on the `foo()` expression. (We then
- /// update the flag to `WarnedAlways` to suppress duplicate
- /// reports.) Similarly, if we traverse to a fresh statement (or
- /// tail expression) from an `Always` setting, we will issue a
- /// warning. This corresponds to something like `{return;
- /// foo();}` or `{return; 22}`, where we would warn on the
- /// `foo()` or `22`.
- ///
- /// An expression represents dead code if, after checking it,
- /// the diverges flag is set to something other than `Maybe`.
- pub(super) diverges: Cell<Diverges>,
-
- /// Whether any child nodes have any type errors.
- pub(super) has_errors: Cell<bool>,
-
- pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
-
- pub(super) inh: &'a Inherited<'a, 'tcx>,
-
- /// True if the function or closure's return type is known before
- /// entering the function/closure, i.e. if the return type is
- /// either given explicitly or inferred from, say, an `Fn*` trait
- /// bound. Used for diagnostic purposes only.
- pub(super) return_type_pre_known: bool,
-
- /// True if the return type has an Opaque type
- pub(super) return_type_has_opaque: bool,
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn new(
- inh: &'a Inherited<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- ) -> FnCtxt<'a, 'tcx> {
- FnCtxt {
- body_id,
- param_env,
- err_count_on_creation: inh.tcx.sess.err_count(),
- ret_coercion: None,
- in_tail_expr: false,
- ret_coercion_span: Cell::new(None),
- resume_yield_tys: None,
- ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
- diverges: Cell::new(Diverges::Maybe),
- has_errors: Cell::new(false),
- enclosing_breakables: RefCell::new(EnclosingBreakables {
- stack: Vec::new(),
- by_id: Default::default(),
- }),
- inh,
- return_type_pre_known: true,
- return_type_has_opaque: false,
- }
- }
-
- pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
- ObligationCause::new(span, self.body_id, code)
- }
-
- pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
- self.cause(span, ObligationCauseCode::MiscObligation)
- }
-
- pub fn sess(&self) -> &Session {
- &self.tcx.sess
- }
-
- pub fn errors_reported_since_creation(&self) -> bool {
- self.tcx.sess.err_count() > self.err_count_on_creation
- }
-}
-
-impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
- type Target = Inherited<'a, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.inh
- }
-}
-
-impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
- fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn item_def_id(&self) -> Option<DefId> {
- None
- }
-
- fn get_type_parameter_bounds(
- &self,
- _: Span,
- def_id: DefId,
- _: Ident,
- ) -> ty::GenericPredicates<'tcx> {
- let tcx = self.tcx;
- let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
- let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id];
- ty::GenericPredicates {
- parent: None,
- predicates: tcx.arena.alloc_from_iter(
- self.param_env.caller_bounds().iter().filter_map(|predicate| {
- match predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => {
- // HACK(eddyb) should get the original `Span`.
- let span = tcx.def_span(def_id);
- Some((predicate, span))
- }
- _ => None,
- }
- }),
- ),
- }
- }
-
- fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
- let v = match def {
- Some(def) => infer::EarlyBoundRegion(span, def.name),
- None => infer::MiscVariable(span),
- };
- Some(self.next_region_var(v))
- }
-
- fn allow_ty_infer(&self) -> bool {
- true
- }
-
- fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
- if let Some(param) = param {
- if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
- return ty;
- }
- unreachable!()
- } else {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span,
- })
- }
- }
-
- fn ct_infer(
- &self,
- ty: Ty<'tcx>,
- param: Option<&ty::GenericParamDef>,
- span: Span,
- ) -> Const<'tcx> {
- if let Some(param) = param {
- if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
- return ct;
- }
- unreachable!()
- } else {
- self.next_const_var(
- ty,
- ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
- )
- }
- }
-
- fn projected_ty_from_poly_trait_ref(
- &self,
- span: Span,
- item_def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- poly_trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Ty<'tcx> {
- let trait_ref = self.replace_bound_vars_with_fresh_vars(
- span,
- infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
- poly_trait_ref,
- );
-
- let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
- self,
- self.tcx,
- span,
- item_def_id,
- item_segment,
- trait_ref.substs,
- );
-
- self.tcx().mk_projection(item_def_id, item_substs)
- }
-
- fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- if ty.has_escaping_bound_vars() {
- ty // FIXME: normalization and escaping regions
- } else {
- self.normalize_associated_types_in(span, ty)
- }
- }
-
- fn set_tainted_by_errors(&self) {
- self.infcx.set_tainted_by_errors()
- }
-
- fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
- self.write_ty(hir_id, ty)
- }
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
deleted file mode 100644
index ee0ad7b5d..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ /dev/null
@@ -1,1139 +0,0 @@
-use super::FnCtxt;
-use crate::astconv::AstConv;
-use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
-
-use hir::def_id::DefId;
-use rustc_ast::util::parser::ExprPrecedence;
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind};
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{
- Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
-};
-use rustc_infer::infer::{self, TyCtxtInferExt};
-use rustc_infer::traits::{self, StatementAsExpression};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
- err.span_suggestion_short(
- span.shrink_to_hi(),
- "consider using a semicolon here",
- ";",
- Applicability::MachineApplicable,
- );
- }
-
- /// On implicit return expressions with mismatched types, provides the following suggestions:
- ///
- /// - Points out the method's return type as the reason for the expected type.
- /// - Possible missing semicolon.
- /// - Possible missing return type if the return type is the default, and not `fn main()`.
- pub fn suggest_mismatched_types_on_tail(
- &self,
- err: &mut Diagnostic,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- blk_id: hir::HirId,
- ) -> bool {
- let expr = expr.peel_drop_temps();
- self.suggest_missing_semicolon(err, expr, expected, false);
- let mut pointing_at_return_type = false;
- if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
- let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
- pointing_at_return_type = self.suggest_missing_return_type(
- err,
- &fn_decl,
- expected,
- found,
- can_suggest,
- fn_id,
- );
- self.suggest_missing_break_or_return_expr(
- err, expr, &fn_decl, expected, found, blk_id, fn_id,
- );
- }
- pointing_at_return_type
- }
-
- /// When encountering an fn-like type, try accessing the output of the type
- /// // and suggesting calling it if it satisfies a predicate (i.e. if the
- /// output has a method or a field):
- /// ```compile_fail,E0308
- /// fn foo(x: usize) -> usize { x }
- /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
- /// ```
- pub(crate) fn suggest_fn_call(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- found: Ty<'tcx>,
- can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
- ) -> bool {
- let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
- else { return false; };
- if can_satisfy(output) {
- let (sugg_call, mut applicability) = match inputs.len() {
- 0 => ("".to_string(), Applicability::MachineApplicable),
- 1..=4 => (
- inputs
- .iter()
- .map(|ty| {
- if ty.is_suggestable(self.tcx, false) {
- format!("/* {ty} */")
- } else {
- "".to_string()
- }
- })
- .collect::<Vec<_>>()
- .join(", "),
- Applicability::HasPlaceholders,
- ),
- _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
- };
-
- let msg = match def_id_or_name {
- DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
- DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
- DefKind::Ctor(CtorOf::Variant, _) => {
- "instantiate this tuple variant".to_string()
- }
- kind => format!("call this {}", kind.descr(def_id)),
- },
- DefIdOrName::Name(name) => format!("call this {name}"),
- };
-
- let sugg = match expr.kind {
- hir::ExprKind::Call(..)
- | hir::ExprKind::Path(..)
- | hir::ExprKind::Index(..)
- | hir::ExprKind::Lit(..) => {
- vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
- }
- hir::ExprKind::Closure { .. } => {
- // Might be `{ expr } || { bool }`
- applicability = Applicability::MaybeIncorrect;
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
- ]
- }
- _ => {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
- ]
- }
- };
-
- err.multipart_suggestion_verbose(
- format!("use parentheses to {msg}"),
- sugg,
- applicability,
- );
-
- return true;
- }
- false
- }
-
- /// Extracts information about a callable type for diagnostics. This is a
- /// heuristic -- it doesn't necessarily mean that a type is always callable,
- /// because the callable type must also be well-formed to be called.
- pub(in super::super) fn extract_callable_info(
- &self,
- expr: &Expr<'_>,
- found: Ty<'tcx>,
- ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
- // Autoderef is useful here because sometimes we box callables, etc.
- let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
- match *found.kind() {
- ty::FnPtr(fn_sig) =>
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
- }
- ty::Opaque(def_id, substs) => {
- self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
- && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Param(param) => {
- let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
- self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
- if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- _ => None,
- }
- }) else { return None; };
-
- let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
- let inputs = inputs
- .skip_binder()
- .iter()
- .map(|ty| {
- self.replace_bound_vars_with_fresh_vars(
- expr.span,
- infer::FnCall,
- inputs.rebind(*ty),
- )
- })
- .collect();
-
- // We don't want to register any extra obligations, which should be
- // implied by wf, but also because that would possibly result in
- // erroneous errors later on.
- let infer::InferOk { value: output, obligations: _ } =
- self.normalize_associated_types_in_as_infer_ok(expr.span, output);
-
- if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
- }
-
- pub fn suggest_two_fn_call(
- &self,
- err: &mut Diagnostic,
- lhs_expr: &'tcx hir::Expr<'tcx>,
- lhs_ty: Ty<'tcx>,
- rhs_expr: &'tcx hir::Expr<'tcx>,
- rhs_ty: Ty<'tcx>,
- can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
- ) -> bool {
- let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
- else { return false; };
- let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
- else { return false; };
-
- if can_satisfy(lhs_output_ty, rhs_output_ty) {
- let mut sugg = vec![];
- let mut applicability = Applicability::MachineApplicable;
-
- for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
- let (sugg_call, this_applicability) = match inputs.len() {
- 0 => ("".to_string(), Applicability::MachineApplicable),
- 1..=4 => (
- inputs
- .iter()
- .map(|ty| {
- if ty.is_suggestable(self.tcx, false) {
- format!("/* {ty} */")
- } else {
- "/* value */".to_string()
- }
- })
- .collect::<Vec<_>>()
- .join(", "),
- Applicability::HasPlaceholders,
- ),
- _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
- };
-
- applicability = applicability.max(this_applicability);
-
- match expr.kind {
- hir::ExprKind::Call(..)
- | hir::ExprKind::Path(..)
- | hir::ExprKind::Index(..)
- | hir::ExprKind::Lit(..) => {
- sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
- }
- hir::ExprKind::Closure { .. } => {
- // Might be `{ expr } || { bool }`
- applicability = Applicability::MaybeIncorrect;
- sugg.extend([
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
- ]);
- }
- _ => {
- sugg.extend([
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
- ]);
- }
- }
- }
-
- err.multipart_suggestion_verbose(
- format!("use parentheses to call these"),
- sugg,
- applicability,
- );
-
- true
- } else {
- false
- }
- }
-
- pub fn suggest_deref_ref_or_into(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- ) {
- let expr = expr.peel_blocks();
- if let Some((sp, msg, suggestion, applicability, verbose)) =
- self.check_ref(expr, found, expected)
- {
- if verbose {
- err.span_suggestion_verbose(sp, &msg, suggestion, applicability);
- } else {
- err.span_suggestion(sp, &msg, suggestion, applicability);
- }
- } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
- && let ty::FnDef(def_id, ..) = &found.kind()
- && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
- {
- err.span_label(sp, format!("{found} defined here"));
- } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
- let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if !methods.is_empty() {
- let mut suggestions = methods.iter()
- .filter_map(|conversion_method| {
- let receiver_method_ident = expr.method_ident();
- if let Some(method_ident) = receiver_method_ident
- && method_ident.name == conversion_method.name
- {
- return None // do not suggest code that is already there (#53348)
- }
-
- let method_call_list = [sym::to_vec, sym::to_string];
- let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
- && receiver_method.ident.name == sym::clone
- && method_call_list.contains(&conversion_method.name)
- // If receiver is `.clone()` and found type has one of those methods,
- // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
- // to an owned type (`Vec` or `String`). These conversions clone internally,
- // so we remove the user's `clone` call.
- {
- vec![(
- receiver_method.ident.span,
- conversion_method.name.to_string()
- )]
- } else if expr.precedence().order()
- < ExprPrecedence::MethodCall.order()
- {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
- ]
- } else {
- vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
- };
- let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
- if let Some(name) = struct_pat_shorthand_field {
- sugg.insert(
- 0,
- (expr.span.shrink_to_lo(), format!("{}: ", name)),
- );
- }
- Some(sugg)
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.multipart_suggestions(
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- }
- } else if let ty::Adt(found_adt, found_substs) = found.kind()
- && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
- && let ty::Adt(expected_adt, expected_substs) = expected.kind()
- && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
- && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
- && inner_ty.is_str()
- {
- let ty = found_substs.type_at(0);
- let mut peeled = ty;
- let mut ref_cnt = 0;
- while let ty::Ref(_, inner, _) = peeled.kind() {
- peeled = *inner;
- ref_cnt += 1;
- }
- if let ty::Adt(adt, _) = peeled.kind()
- && self.tcx.is_diagnostic_item(sym::String, adt.did())
- {
- err.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- "try converting the passed type into a `&str`",
- format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
-
- /// When encountering the expected boxed value allocated in the stack, suggest allocating it
- /// in the heap by calling `Box::new()`.
- pub(in super::super) fn suggest_boxing_when_appropriate(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return;
- }
- if !expected.is_box() || found.is_box() {
- return;
- }
- let boxed_found = self.tcx.mk_box(found);
- if self.can_coerce(boxed_found, expected) {
- err.multipart_suggestion(
- "store this in the heap by calling `Box::new`",
- vec![
- (expr.span.shrink_to_lo(), "Box::new(".to_string()),
- (expr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- err.note(
- "for more on the distinction between the stack and the heap, read \
- https://doc.rust-lang.org/book/ch15-01-box.html, \
- https://doc.rust-lang.org/rust-by-example/std/box.html, and \
- https://doc.rust-lang.org/std/boxed/index.html",
- );
- }
- }
-
- /// When encountering a closure that captures variables, where a FnPtr is expected,
- /// suggest a non-capturing closure
- pub(in super::super) fn suggest_no_capture_closure(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
- if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
- // Report upto four upvars being captured to reduce the amount error messages
- // reported back to the user.
- let spans_and_labels = upvars
- .iter()
- .take(4)
- .map(|(var_hir_id, upvar)| {
- let var_name = self.tcx.hir().name(*var_hir_id).to_string();
- let msg = format!("`{}` captured here", var_name);
- (upvar.span, msg)
- })
- .collect::<Vec<_>>();
-
- let mut multi_span: MultiSpan =
- spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
- for (sp, label) in spans_and_labels {
- multi_span.push_span_label(sp, label);
- }
- err.span_note(
- multi_span,
- "closures can only be coerced to `fn` types if they do not capture any variables"
- );
- }
- }
- }
-
- /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
- #[instrument(skip(self, err))]
- pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- // Handle #68197.
-
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return false;
- }
- let pin_did = self.tcx.lang_items().pin_type();
- // This guards the `unwrap` and `mk_box` below.
- if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
- return false;
- }
- let box_found = self.tcx.mk_box(found);
- let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
- let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
- match expected.kind() {
- ty::Adt(def, _) if Some(def.did()) == pin_did => {
- if self.can_coerce(pin_box_found, expected) {
- debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
- match found.kind() {
- ty::Adt(def, _) if def.is_box() => {
- err.help("use `Box::pin`");
- }
- _ => {
- err.multipart_suggestion(
- "you need to pin and box this expression",
- vec![
- (expr.span.shrink_to_lo(), "Box::pin(".to_string()),
- (expr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
- true
- } else if self.can_coerce(pin_found, expected) {
- match found.kind() {
- ty::Adt(def, _) if def.is_box() => {
- err.help("use `Box::pin`");
- true
- }
- _ => false,
- }
- } else {
- false
- }
- }
- ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
- // Check if the parent expression is a call to Pin::new. If it
- // is and we were expecting a Box, ergo Pin<Box<expected>>, we
- // can suggest Box::pin.
- let parent = self.tcx.hir().get_parent_node(expr.hir_id);
- let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else {
- return false;
- };
- match fn_name.kind {
- ExprKind::Path(QPath::TypeRelative(
- hir::Ty {
- kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
- ..
- },
- method,
- )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
- err.span_suggestion(
- fn_name.span,
- "use `Box::pin` to pin and box this expression",
- "Box::pin",
- Applicability::MachineApplicable,
- );
- true
- }
- _ => false,
- }
- }
- _ => false,
- }
- }
-
- /// A common error is to forget to add a semicolon at the end of a block, e.g.,
- ///
- /// ```compile_fail,E0308
- /// # fn bar_that_returns_u32() -> u32 { 4 }
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return expression in a block would make sense on its own as a
- /// statement and the return type has been left as default or has been specified as `()`. If so,
- /// it suggests adding a semicolon.
- ///
- /// If the expression is the expression of a closure without block (`|| expr`), a
- /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
- pub fn suggest_missing_semicolon(
- &self,
- err: &mut Diagnostic,
- expression: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- needs_block: bool,
- ) {
- if expected.is_unit() {
- // `BlockTailExpression` only relevant if the tail expr would be
- // useful on its own.
- match expression.kind {
- ExprKind::Call(..)
- | ExprKind::MethodCall(..)
- | ExprKind::Loop(..)
- | ExprKind::If(..)
- | ExprKind::Match(..)
- | ExprKind::Block(..)
- if expression.can_have_side_effects()
- // If the expression is from an external macro, then do not suggest
- // adding a semicolon, because there's nowhere to put it.
- // See issue #81943.
- && !in_external_macro(self.tcx.sess, expression.span) =>
- {
- if needs_block {
- err.multipart_suggestion(
- "consider using a semicolon here",
- vec![
- (expression.span.shrink_to_lo(), "{ ".to_owned()),
- (expression.span.shrink_to_hi(), "; }".to_owned()),
- ],
- Applicability::MachineApplicable,
- );
- } else {
- err.span_suggestion(
- expression.span.shrink_to_hi(),
- "consider using a semicolon here",
- ";",
- Applicability::MachineApplicable,
- );
- }
- }
- _ => (),
- }
- }
- }
-
- /// A possible error is to forget to add a return type that is needed:
- ///
- /// ```compile_fail,E0308
- /// # fn bar_that_returns_u32() -> u32 { 4 }
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return type is left as default, the method is not part of an
- /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
- /// type.
- pub(in super::super) fn suggest_missing_return_type(
- &self,
- err: &mut Diagnostic,
- fn_decl: &hir::FnDecl<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- can_suggest: bool,
- fn_id: hir::HirId,
- ) -> bool {
- let found =
- self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
- // Only suggest changing the return type for methods that
- // haven't set a return type at all (and aren't `fn main()` or an impl).
- match &fn_decl.output {
- &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
- // `fn main()` must return `()`, do not suggest changing return type
- err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
- return true;
- }
- &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
- if found.is_suggestable(self.tcx, false) {
- err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
- return true;
- } else if let ty::Closure(_, substs) = found.kind()
- // FIXME(compiler-errors): Get better at printing binders...
- && let closure = substs.as_closure()
- && closure.sig().is_suggestable(self.tcx, false)
- {
- err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
- return true;
- } else {
- // FIXME: if `found` could be `impl Iterator` we should suggest that.
- err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
- return true
- }
- }
- &hir::FnRetTy::Return(ref ty) => {
- // Only point to return type if the expected type is the return type, as if they
- // are not, the expectation must have been caused by something else.
- debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
- let span = ty.span;
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
- debug!("suggest_missing_return_type: return type {:?}", ty);
- debug!("suggest_missing_return_type: expected type {:?}", ty);
- let bound_vars = self.tcx.late_bound_vars(fn_id);
- let ty = Binder::bind_with_vars(ty, bound_vars);
- let ty = self.normalize_associated_types_in(span, ty);
- let ty = self.tcx.erase_late_bound_regions(ty);
- if self.can_coerce(expected, ty) {
- err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
- self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
- return true;
- }
- }
- _ => {}
- }
- false
- }
-
- /// check whether the return type is a generic type with a trait bound
- /// only suggest this if the generic param is not present in the arguments
- /// if this is true, hint them towards changing the return type to `impl Trait`
- /// ```compile_fail,E0308
- /// fn cant_name_it<T: Fn() -> u32>() -> T {
- /// || 3
- /// }
- /// ```
- fn try_suggest_return_impl_trait(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- fn_id: hir::HirId,
- ) {
- // Only apply the suggestion if:
- // - the return type is a generic parameter
- // - the generic param is not used as a fn param
- // - the generic param has at least one bound
- // - the generic param doesn't appear in any other bounds where it's not the Self type
- // Suggest:
- // - Changing the return type to be `impl <all bounds>`
-
- debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
-
- let ty::Param(expected_ty_as_param) = expected.kind() else { return };
-
- let fn_node = self.tcx.hir().find(fn_id);
-
- let Some(hir::Node::Item(hir::Item {
- kind:
- hir::ItemKind::Fn(
- hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
- hir::Generics { params, predicates, .. },
- _body_id,
- ),
- ..
- })) = fn_node else { return };
-
- if params.get(expected_ty_as_param.index as usize).is_none() {
- return;
- };
-
- // get all where BoundPredicates here, because they are used in to cases below
- let where_predicates = predicates
- .iter()
- .filter_map(|p| match p {
- WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
- bounds,
- bounded_ty,
- ..
- }) => {
- // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below)
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty);
- Some((ty, bounds))
- }
- _ => None,
- })
- .map(|(ty, bounds)| match ty.kind() {
- ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
- // check whether there is any predicate that contains our `T`, like `Option<T>: Send`
- _ => match ty.contains(expected) {
- true => Err(()),
- false => Ok(None),
- },
- })
- .collect::<Result<Vec<_>, _>>();
-
- let Ok(where_predicates) = where_predicates else { return };
-
- // now get all predicates in the same types as the where bounds, so we can chain them
- let predicates_from_where =
- where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
-
- // extract all bounds from the source code using their spans
- let all_matching_bounds_strs = predicates_from_where
- .filter_map(|bound| match bound {
- GenericBound::Trait(_, _) => {
- self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
- }
- _ => None,
- })
- .collect::<Vec<String>>();
-
- if all_matching_bounds_strs.len() == 0 {
- return;
- }
-
- let all_bounds_str = all_matching_bounds_strs.join(" + ");
-
- let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param);
- matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
- });
-
- if ty_param_used_in_fn_params {
- return;
- }
-
- err.span_suggestion(
- fn_return.span(),
- "consider using an impl return type",
- format!("impl {}", all_bounds_str),
- Applicability::MaybeIncorrect,
- );
- }
-
- pub(in super::super) fn suggest_missing_break_or_return_expr(
- &self,
- err: &mut Diagnostic,
- expr: &'tcx hir::Expr<'tcx>,
- fn_decl: &hir::FnDecl<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- id: hir::HirId,
- fn_id: hir::HirId,
- ) {
- if !expected.is_unit() {
- return;
- }
- let found = self.resolve_vars_with_obligations(found);
-
- let in_loop = self.is_loop(id)
- || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
-
- let in_local_statement = self.is_local_statement(id)
- || self
- .tcx
- .hir()
- .parent_iter(id)
- .any(|(parent_id, _)| self.is_local_statement(parent_id));
-
- if in_loop && in_local_statement {
- err.multipart_suggestion(
- "you might have meant to break the loop with this value",
- vec![
- (expr.span.shrink_to_lo(), "break ".to_string()),
- (expr.span.shrink_to_hi(), ";".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- return;
- }
-
- if let hir::FnRetTy::Return(ty) = fn_decl.output {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
- let bound_vars = self.tcx.late_bound_vars(fn_id);
- let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
- let ty = self.normalize_associated_types_in(expr.span, ty);
- let ty = match self.tcx.asyncness(fn_id.owner) {
- hir::IsAsync::Async => self
- .tcx
- .infer_ctxt()
- .enter(|infcx| {
- infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
- span_bug!(
- fn_decl.output.span(),
- "failed to get output type of async function"
- )
- })
- })
- .skip_binder(),
- hir::IsAsync::NotAsync => ty,
- };
- if self.can_coerce(found, ty) {
- err.multipart_suggestion(
- "you might have meant to return this value",
- vec![
- (expr.span.shrink_to_lo(), "return ".to_string()),
- (expr.span.shrink_to_hi(), ";".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
-
- pub(in super::super) fn suggest_missing_parentheses(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- ) {
- let sp = self.tcx.sess.source_map().start_point(expr.span);
- if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
- // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
- self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp);
- }
- }
-
- /// Given an expression type mismatch, peel any `&` expressions until we get to
- /// a block expression, and then suggest replacing the braces with square braces
- /// if it was possibly mistaken array syntax.
- pub(crate) fn suggest_block_to_brackets_peeling_refs(
- &self,
- diag: &mut Diagnostic,
- mut expr: &hir::Expr<'_>,
- mut expr_ty: Ty<'tcx>,
- mut expected_ty: Ty<'tcx>,
- ) {
- loop {
- match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
- (
- hir::ExprKind::AddrOf(_, _, inner_expr),
- ty::Ref(_, inner_expr_ty, _),
- ty::Ref(_, inner_expected_ty, _),
- ) => {
- expr = *inner_expr;
- expr_ty = *inner_expr_ty;
- expected_ty = *inner_expected_ty;
- }
- (hir::ExprKind::Block(blk, _), _, _) => {
- self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
- break;
- }
- _ => break,
- }
- }
- }
-
- pub(crate) fn suggest_copied_or_cloned(
- &self,
- diag: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expr_ty: Ty<'tcx>,
- expected_ty: Ty<'tcx>,
- ) {
- let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
- let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
- if adt_def != expected_adt_def {
- return;
- }
-
- let mut suggest_copied_or_cloned = || {
- let expr_inner_ty = substs.type_at(0);
- let expected_inner_ty = expected_substs.type_at(0);
- if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
- && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok()
- {
- let def_path = self.tcx.def_path_str(adt_def.did());
- if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) {
- diag.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- format!(
- "use `{def_path}::copied` to copy the value inside the `{def_path}`"
- ),
- ".copied()",
- Applicability::MachineApplicable,
- );
- } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
- && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
- self,
- self.param_env,
- *ty,
- clone_did,
- expr.span
- )
- {
- diag.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- format!(
- "use `{def_path}::cloned` to clone the value inside the `{def_path}`"
- ),
- ".cloned()",
- Applicability::MachineApplicable,
- );
- }
- }
- };
-
- if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
- && adt_def.did() == result_did
- // Check that the error types are equal
- && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
- {
- suggest_copied_or_cloned();
- } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
- && adt_def.did() == option_did
- {
- suggest_copied_or_cloned();
- }
- }
-
- /// Suggest wrapping the block in square brackets instead of curly braces
- /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
- pub(crate) fn suggest_block_to_brackets(
- &self,
- diag: &mut Diagnostic,
- blk: &hir::Block<'_>,
- blk_ty: Ty<'tcx>,
- expected_ty: Ty<'tcx>,
- ) {
- if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
- if self.can_coerce(blk_ty, *elem_ty)
- && blk.stmts.is_empty()
- && blk.rules == hir::BlockCheckMode::DefaultBlock
- {
- let source_map = self.tcx.sess.source_map();
- if let Ok(snippet) = source_map.span_to_snippet(blk.span) {
- if snippet.starts_with('{') && snippet.ends_with('}') {
- diag.multipart_suggestion_verbose(
- "to create an array, use square brackets instead of curly braces",
- vec![
- (
- blk.span
- .shrink_to_lo()
- .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
- "[".to_string(),
- ),
- (
- blk.span
- .shrink_to_hi()
- .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
- "]".to_string(),
- ),
- ],
- Applicability::MachineApplicable,
- );
- }
- }
- }
- }
- }
-
- fn is_loop(&self, id: hir::HirId) -> bool {
- let node = self.tcx.hir().get(id);
- matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
- }
-
- fn is_local_statement(&self, id: hir::HirId) -> bool {
- let node = self.tcx.hir().get(id);
- matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
- }
-
- /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
- /// which is a side-effect of autoref.
- pub(crate) fn note_type_is_not_clone(
- &self,
- diag: &mut Diagnostic,
- expected_ty: Ty<'tcx>,
- found_ty: Ty<'tcx>,
- expr: &hir::Expr<'_>,
- ) {
- let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; };
- let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
- let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
- let results = self.typeck_results.borrow();
- // First, look for a `Clone::clone` call
- if segment.ident.name == sym::clone
- && results.type_dependent_def_id(expr.hir_id).map_or(
- false,
- |did| {
- let assoc_item = self.tcx.associated_item(did);
- assoc_item.container == ty::AssocItemContainer::TraitContainer
- && assoc_item.container_id(self.tcx) == clone_trait_did
- },
- )
- // If that clone call hasn't already dereferenced the self type (i.e. don't give this
- // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
- && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
- // Check that we're in fact trying to clone into the expected type
- && self.can_coerce(*pointee_ty, expected_ty)
- // And the expected type doesn't implement `Clone`
- && !self.predicate_must_hold_considering_regions(&traits::Obligation {
- cause: traits::ObligationCause::dummy(),
- param_env: self.param_env,
- recursion_depth: 0,
- predicate: ty::Binder::dummy(ty::TraitRef {
- def_id: clone_trait_did,
- substs: self.tcx.mk_substs([expected_ty.into()].iter()),
- })
- .without_const()
- .to_predicate(self.tcx),
- })
- {
- diag.span_note(
- callee_expr.span,
- &format!(
- "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
- ),
- );
- }
- }
-
- /// A common error is to add an extra semicolon:
- ///
- /// ```compile_fail,E0308
- /// fn foo() -> usize {
- /// 22;
- /// }
- /// ```
- ///
- /// This routine checks if the final statement in a block is an
- /// expression with an explicit semicolon whose type is compatible
- /// with `expected_ty`. If so, it suggests removing the semicolon.
- pub(crate) fn consider_removing_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- err: &mut Diagnostic,
- ) -> bool {
- if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
- if let StatementAsExpression::NeedsBoxing = boxed {
- err.span_suggestion_verbose(
- span_semi,
- "consider removing this semicolon and boxing the expression",
- "",
- Applicability::HasPlaceholders,
- );
- } else {
- err.span_suggestion_short(
- span_semi,
- "remove this semicolon",
- "",
- Applicability::MachineApplicable,
- );
- }
- true
- } else {
- false
- }
- }
-}
-
-pub enum DefIdOrName {
- DefId(DefId),
- Name(&'static str),
-}