summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_hir_typeck
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck')
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs38
-rw-r--r--compiler/rustc_hir_typeck/src/autoderef.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs63
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs106
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs133
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs65
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs38
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs425
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs33
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs61
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs203
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs136
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs45
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs354
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs22
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs27
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs21
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs74
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs248
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude2021.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs279
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs1972
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs69
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs55
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs34
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs96
36 files changed, 2831 insertions, 1851 deletions
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index e25a9e903..b6f19d3cc 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -212,7 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.can_coerce(arm_ty, ret_ty)
&& prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty))
// The match arms need to unify for the case of `impl Trait`.
- && !matches!(ret_ty.kind(), ty::Opaque(..))
+ && !matches!(ret_ty.kind(), ty::Alias(ty::Opaque, ..))
}
_ => false,
};
@@ -224,14 +224,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut ret_span: MultiSpan = semi_span.into();
ret_span.push_span_label(
expr.span,
- "this could be implicitly returned but it is a statement, not a \
- tail expression",
+ "this could be implicitly returned but it is a statement, not a tail expression",
);
ret_span.push_span_label(ret, "the `match` arms can conform to this return type");
ret_span.push_span_label(
semi_span,
- "the `match` is a statement because of this semicolon, consider \
- removing it",
+ "the `match` is a statement because of this semicolon, consider removing it",
);
diag.span_note(ret_span, "you might have meant to return the `match` expression");
diag.tool_only_span_suggestion(
@@ -289,15 +287,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
let node = {
- let rslt = self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(hir_id));
+ let rslt = self.tcx.hir().parent_id(self.tcx.hir().parent_id(hir_id));
self.tcx.hir().get(rslt)
};
if let hir::Node::Block(block) = node {
// check that the body's parent is an fn
- let parent = self
- .tcx
- .hir()
- .get(self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id)));
+ let parent = self.tcx.hir().get_parent(self.tcx.hir().parent_id(block.hir_id));
if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) =
(&block.expr, parent)
{
@@ -518,7 +513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let substs = sig.output().walk().find_map(|arg| {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Opaque(def_id, substs) = *ty.kind()
+ && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *ty.kind()
&& def_id == rpit_def_id
{
Some(substs)
@@ -526,7 +521,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None
}
})?;
- let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs);
if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) {
return None;
@@ -540,17 +534,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
let pred = pred.kind().rebind(match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => {
- assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty);
+ // FIXME(rpitit): This will need to be fixed when we move to associated types
+ assert!(matches!(
+ *trait_pred.trait_ref.self_ty().kind(),
+ ty::Alias(_, ty::AliasTy { def_id, substs, .. })
+ if def_id == rpit_def_id && substs == substs
+ ));
ty::PredicateKind::Clause(ty::Clause::Trait(
- trait_pred.with_self_type(self.tcx, ty),
+ trait_pred.with_self_ty(self.tcx, ty),
))
}
ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => {
- assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty);
- proj_pred.projection_ty.substs = self.tcx.mk_substs_trait(
- ty,
- proj_pred.projection_ty.substs.iter().skip(1),
- );
+ assert!(matches!(
+ *proj_pred.projection_ty.self_ty().kind(),
+ ty::Alias(_, ty::AliasTy { def_id, substs, .. })
+ if def_id == rpit_def_id && substs == substs
+ ));
+ proj_pred = proj_pred.with_self_ty(self.tcx, ty);
ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred))
}
_ => continue,
diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs
index 41b52a4c4..7873257c4 100644
--- a/compiler/rustc_hir_typeck/src/autoderef.rs
+++ b/compiler/rustc_hir_typeck/src/autoderef.rs
@@ -2,11 +2,11 @@
use super::method::MethodCallee;
use super::{FnCtxt, PlaceOp};
+use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind};
use rustc_infer::infer::InferOk;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
-use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind};
use std::iter;
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index b09ddf80e..b617821fb 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -1,13 +1,14 @@
-use super::method::probe::{IsSuggestion, Mode, ProbeScope};
+use super::method::probe::ProbeScope;
use super::method::MethodCallee;
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
use crate::type_error_struct;
use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{self, CtorKind, Namespace, Res};
use rustc_hir::def_id::DefId;
+use rustc_hir_analysis::autoderef::Autoderef;
use rustc_infer::{
infer,
traits::{self, Obligation},
@@ -25,7 +26,6 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_target::spec::abi;
-use rustc_trait_selection::autoderef::Autoderef;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -241,7 +241,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
if let Some(ok) = self.lookup_method_in_trait(
- call_expr.span,
+ self.misc(call_expr.span),
method_name,
trait_def_id,
adjusted_ty,
@@ -288,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_span: Span,
) {
let hir = self.tcx.hir();
- let parent_hir_id = hir.get_parent_node(hir_id);
+ let parent_hir_id = hir.parent_id(hir_id);
let parent_node = hir.get(parent_hir_id);
if let (
hir::Node::Expr(hir::Expr {
@@ -303,7 +303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
// Actually need to unwrap a few more layers of HIR to get to
// the _real_ closure...
- let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id));
+ let async_closure = hir.parent_id(hir.parent_id(parent_hir_id));
if let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
..
@@ -336,7 +336,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
) -> bool {
- let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id);
+ let hir_id = self.tcx.hir().parent_id(call_expr.hir_id);
let parent_node = self.tcx.hir().get(hir_id);
if let (
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }),
@@ -375,14 +375,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
let predicates = self.tcx.predicates_of(def_id);
let predicates = predicates.instantiate(self.tcx, subst);
- for (predicate, predicate_span) in
- predicates.predicates.iter().zip(&predicates.spans)
- {
+ for (predicate, predicate_span) in predicates {
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy_with_span(callee_expr.span),
self.param_env,
- *predicate,
+ predicate,
);
let result = self.evaluate_obligation(&obligation);
self.tcx
@@ -391,7 +389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_expr.span,
&format!("evaluate({:?}) = {:?}", predicate, result),
)
- .span_label(*predicate_span, "predicate")
+ .span_label(predicate_span, "predicate")
.emit();
}
}
@@ -399,6 +397,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
ty::FnPtr(sig) => (sig, None),
_ => {
+ for arg in arg_exprs {
+ self.check_expr(arg);
+ }
+
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
&& let [segment] = path.segments
&& let Some(mut diag) = self
@@ -424,21 +426,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
-
- // This is the "default" function signature, used in case of error.
- // In that case, we check each argument against "error" in order to
- // set up all the node type bindings.
- (
- ty::Binder::dummy(self.tcx.mk_fn_sig(
- self.err_args(arg_exprs.len()).into_iter(),
- self.tcx.ty_error(),
- false,
- hir::Unsafety::Normal,
- abi::Abi::Rust,
- )),
- None,
- )
+ let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
+
+ return self.tcx.ty_error_with_guaranteed(err);
}
};
@@ -498,20 +488,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Expectation<'tcx>,
) -> Option<Ty<'tcx>> {
if let [callee_expr, rest @ ..] = arg_exprs {
- let callee_ty = self.check_expr(callee_expr);
+ let callee_ty = self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)?;
+
// First, do a probe with `IsSuggestion(true)` to avoid emitting
// any strange errors. If it's successful, then we'll do a true
// method lookup.
let Ok(pick) = self
- .probe_for_name(
- Mode::MethodCall,
+ .lookup_probe_for_diagnostic(
segment.ident,
- IsSuggestion(true),
callee_ty,
- call_expr.hir_id,
+ call_expr,
// We didn't record the in scope traits during late resolution
// so we need to probe AllTraits unfortunately
ProbeScope::AllTraits,
+ expected.only_has_type(self),
) else {
return None;
};
@@ -521,7 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_expr,
call_expr,
callee_ty,
- pick,
+ &pick,
segment,
);
if pick.illegal_sized_bound.is_some() {
@@ -591,7 +581,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
callee_expr: &'tcx hir::Expr<'tcx>,
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
- ) {
+ ) -> ErrorGuaranteed {
let mut unit_variant = None;
if let hir::ExprKind::Path(qpath) = &callee_expr.kind
&& let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _)
@@ -667,8 +657,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
- if let Some((maybe_def, output_ty, _)) =
- self.extract_callable_info(callee_expr, callee_ty)
+ if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty)
&& !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span)
{
let descr = match maybe_def {
@@ -720,7 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(span, label);
}
}
- err.emit();
+ err.emit()
}
fn confirm_deferred_closure_call(
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 890a068a7..712f9b87a 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -31,14 +31,16 @@
use super::FnCtxt;
use crate::type_error_struct;
-use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
+use hir::ExprKind;
+use rustc_errors::{
+ struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
use rustc_hir as hir;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::cast::{CastKind, CastTy};
use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef};
use rustc_session::lint;
use rustc_session::Session;
@@ -75,10 +77,8 @@ enum PointerKind<'tcx> {
VTable(Option<DefId>),
/// Slice
Length,
- /// The unsize info of this projection
- OfProjection(ty::ProjectionTy<'tcx>),
- /// The unsize info of this opaque ty
- OfOpaque(DefId, SubstsRef<'tcx>),
+ /// The unsize info of this projection or opaque type
+ OfAlias(ty::AliasTy<'tcx>),
/// The unsize info of this parameter
OfParam(ty::ParamTy),
}
@@ -118,8 +118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Pointers to foreign types are thin, despite being unsized
ty::Foreign(..) => Some(PointerKind::Thin),
// We should really try to normalize here.
- ty::Projection(pi) => Some(PointerKind::OfProjection(pi)),
- ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)),
+ ty::Alias(_, pi) => Some(PointerKind::OfAlias(pi)),
ty::Param(p) => Some(PointerKind::OfParam(p)),
// Insufficient type information.
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None,
@@ -153,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[derive(Copy, Clone)]
pub enum CastError {
- ErrorGuaranteed,
+ ErrorGuaranteed(ErrorGuaranteed),
CastToBool,
CastToChar,
@@ -178,8 +177,8 @@ pub enum CastError {
}
impl From<ErrorGuaranteed> for CastError {
- fn from(_: ErrorGuaranteed) -> Self {
- CastError::ErrorGuaranteed
+ fn from(err: ErrorGuaranteed) -> Self {
+ CastError::ErrorGuaranteed(err)
}
}
@@ -227,11 +226,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
match e {
- CastError::ErrorGuaranteed => {
+ CastError::ErrorGuaranteed(_) => {
// an error has already been reported
}
CastError::NeedDeref => {
- let error_span = self.span;
let mut err = make_invalid_casting_error(
fcx.tcx.sess,
self.span,
@@ -239,21 +237,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
self.cast_ty,
fcx,
);
- let cast_ty = fcx.ty_to_string(self.cast_ty);
- err.span_label(
- error_span,
- format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty),
- );
- if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr_span) {
- err.span_suggestion(
- self.expr_span,
- "dereference the expression",
- format!("*{}", snippet),
- Applicability::MaybeIncorrect,
+
+ if matches!(self.expr.kind, ExprKind::AddrOf(..)) {
+ // get just the borrow part of the expression
+ let span = self.expr_span.with_hi(self.expr.peel_borrows().span.lo());
+ err.span_suggestion_verbose(
+ span,
+ "remove the unneeded borrow",
+ "",
+ Applicability::MachineApplicable,
);
} else {
- err.span_help(self.expr_span, "dereference the expression with `*`");
+ err.span_suggestion_verbose(
+ self.expr_span.shrink_to_lo(),
+ "dereference the expression",
+ "*",
+ Applicability::MachineApplicable,
+ );
}
+
err.emit();
}
CastError::NeedViaThinPtr | CastError::NeedViaPtr => {
@@ -274,6 +276,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
}
));
}
+
+ self.try_suggest_collection_to_bool(fcx, &mut err);
+
err.emit();
}
CastError::NeedViaInt => {
@@ -521,6 +526,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} else {
err.span_label(self.span, "invalid cast");
}
+
+ self.try_suggest_collection_to_bool(fcx, &mut err);
+
err.emit();
}
CastError::SizedUnsizedCast => {
@@ -851,13 +859,15 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
- (_, DynStar) | (DynStar, _) => {
+ (_, DynStar) => {
if fcx.tcx.features().dyn_star {
bug!("should be handled by `try_coerce`")
} else {
Err(CastError::IllegalCast)
}
}
+
+ (DynStar, _) => Err(CastError::IllegalCast),
}
}
@@ -976,11 +986,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast),
Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))),
- Some(
- PointerKind::OfProjection(_)
- | PointerKind::OfOpaque(_, _)
- | PointerKind::OfParam(_),
- ) => Err(CastError::IntToFatCast(None)),
+ Some(PointerKind::OfAlias(_) | PointerKind::OfParam(_)) => {
+ Err(CastError::IntToFatCast(None))
+ }
}
}
@@ -1084,4 +1092,40 @@ impl<'a, 'tcx> CastCheck<'tcx> {
},
);
}
+
+ /// Attempt to suggest using `.is_empty` when trying to cast from a
+ /// collection type to a boolean.
+ fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
+ if self.cast_ty.is_bool() {
+ let derefed = fcx
+ .autoderef(self.expr_span, self.expr_ty)
+ .silence_errors()
+ .find(|t| matches!(t.0.kind(), ty::Str | ty::Slice(..)));
+
+ if let Some((deref_ty, _)) = derefed {
+ // Give a note about what the expr derefs to.
+ if deref_ty != self.expr_ty.peel_refs() {
+ err.span_note(
+ self.expr_span,
+ format!(
+ "this expression `Deref`s to `{}` which implements `is_empty`",
+ fcx.ty_to_string(deref_ty)
+ ),
+ );
+ }
+
+ // Create a multipart suggestion: add `!` and `.is_empty()` in
+ // place of the cast.
+ let suggestion = vec![
+ (self.expr_span.shrink_to_lo(), "!".to_string()),
+ (self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
+ ];
+
+ err.multipart_suggestion_verbose(format!(
+ "consider using the `is_empty` method on `{}` to determine if it contains anything",
+ fcx.ty_to_string(self.expr_ty),
+ ), suggestion, Applicability::MaybeIncorrect);
+ }
+ }
+ }
}
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 32f86b804..57feefbca 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -1,4 +1,7 @@
use crate::coercion::CoerceMany;
+use crate::errors::{
+ LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy,
+};
use crate::gather_locals::GatherLocalsVisitor;
use crate::FnCtxt;
use crate::GeneratorTypes;
@@ -9,8 +12,9 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir_analysis::check::fn_maybe_err;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::RegionVariableOrigin;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
use rustc_span::def_id::LocalDefId;
+use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use std::cell::RefCell;
@@ -168,6 +172,10 @@ pub(super) fn check_fn<'a, 'tcx>(
check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
}
+ if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() {
+ check_lang_start_fn(tcx, fn_sig, decl, fn_def_id);
+ }
+
gen_ty
}
@@ -223,3 +231,126 @@ fn check_panic_info_fn(
tcx.sess.span_err(span, "should have no const parameters");
}
}
+
+fn check_lang_start_fn<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ fn_sig: ty::FnSig<'tcx>,
+ decl: &'tcx hir::FnDecl<'tcx>,
+ def_id: LocalDefId,
+) {
+ let inputs = fn_sig.inputs();
+
+ let arg_count = inputs.len();
+ if arg_count != 4 {
+ tcx.sess.emit_err(LangStartIncorrectNumberArgs {
+ params_span: tcx.def_span(def_id),
+ found_param_count: arg_count,
+ });
+ }
+
+ // only check args if they should exist by checking the count
+ // note: this does not handle args being shifted or their order swapped very nicely
+ // but it's a lang item, users shouldn't frequently encounter this
+
+ // first arg is `main: fn() -> T`
+ if let Some(&main_arg) = inputs.get(0) {
+ // make a Ty for the generic on the fn for diagnostics
+ // FIXME: make the lang item generic checks check for the right generic *kind*
+ // for example `start`'s generic should be a type parameter
+ let generics = tcx.generics_of(def_id);
+ let fn_generic = generics.param_at(0, tcx);
+ let generic_tykind =
+ ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name });
+ let generic_ty = tcx.mk_ty(generic_tykind);
+ let expected_fn_sig =
+ tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
+ let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
+
+ // we emit the same error to suggest changing the arg no matter what's wrong with the arg
+ let emit_main_fn_arg_err = || {
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[0].span,
+ param_num: 1,
+ expected_ty: expected_ty,
+ found_ty: main_arg,
+ });
+ };
+
+ if let ty::FnPtr(main_fn_sig) = main_arg.kind() {
+ let main_fn_inputs = main_fn_sig.inputs();
+ if main_fn_inputs.iter().count() != 0 {
+ emit_main_fn_arg_err();
+ }
+
+ let output = main_fn_sig.output();
+ output.map_bound(|ret_ty| {
+ // if the output ty is a generic, it's probably the right one
+ if !matches!(ret_ty.kind(), ty::Param(_)) {
+ emit_main_fn_arg_err();
+ }
+ });
+ } else {
+ emit_main_fn_arg_err();
+ }
+ }
+
+ // second arg is isize
+ if let Some(&argc_arg) = inputs.get(1) {
+ if argc_arg != tcx.types.isize {
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[1].span,
+ param_num: 2,
+ expected_ty: tcx.types.isize,
+ found_ty: argc_arg,
+ });
+ }
+ }
+
+ // third arg is `*const *const u8`
+ if let Some(&argv_arg) = inputs.get(2) {
+ let mut argv_is_okay = false;
+ if let ty::RawPtr(outer_ptr) = argv_arg.kind() {
+ if outer_ptr.mutbl.is_not() {
+ if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() {
+ if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 {
+ argv_is_okay = true;
+ }
+ }
+ }
+ }
+
+ if !argv_is_okay {
+ let inner_ptr_ty =
+ tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
+ let expected_ty =
+ tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[2].span,
+ param_num: 3,
+ expected_ty,
+ found_ty: argv_arg,
+ });
+ }
+ }
+
+ // fourth arg is `sigpipe: u8`
+ if let Some(&sigpipe_arg) = inputs.get(3) {
+ if sigpipe_arg != tcx.types.u8 {
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[3].span,
+ param_num: 4,
+ expected_ty: tcx.types.u8,
+ found_ty: sigpipe_arg,
+ });
+ }
+ }
+
+ // output type is isize
+ if fn_sig.output() != tcx.types.isize {
+ tcx.sess.emit_err(LangStartIncorrectRetTy {
+ ret_span: decl.output.span(),
+ expected_ty: tcx.types.isize,
+ found_ty: fn_sig.output(),
+ });
+ }
+}
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 429cb60ba..12a2abfa7 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -13,7 +13,7 @@ use rustc_infer::infer::{InferOk, InferResult};
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor};
use rustc_span::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
@@ -21,6 +21,7 @@ use rustc_trait_selection::traits::error_reporting::ArgKind;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use std::cmp;
use std::iter;
+use std::ops::ControlFlow;
/// What signature do we *expect* the closure to have from context?
#[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
@@ -54,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match expected.to_option(self) {
- Some(ty) => self.deduce_expectations_from_expected_type(ty),
+ Some(ty) => self.deduce_closure_signature(ty),
None => (None, None),
};
let body = self.tcx.hir().body(closure.body);
@@ -162,14 +163,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Given the expected type, figures out what it can about this closure we
/// are about to type check:
#[instrument(skip(self), level = "debug")]
- fn deduce_expectations_from_expected_type(
+ fn deduce_closure_signature(
&self,
expected_ty: Ty<'tcx>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
match *expected_ty.kind() {
- ty::Opaque(def_id, substs) => self.deduce_signature_from_predicates(
- self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
- ),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
+ .deduce_closure_signature_from_predicates(
+ expected_ty,
+ self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
+ ),
ty::Dynamic(ref object_type, ..) => {
let sig = object_type.projection_bounds().find_map(|pb| {
let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self);
@@ -180,7 +183,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.and_then(|did| self.tcx.fn_trait_kind_from_def_id(did));
(sig, kind)
}
- ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates(
+ ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
+ self.tcx.mk_ty_var(self.root_var(vid)),
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
),
ty::FnPtr(sig) => {
@@ -191,8 +195,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- fn deduce_signature_from_predicates(
+ fn deduce_closure_signature_from_predicates(
&self,
+ expected_ty: Ty<'tcx>,
predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let mut expected_sig = None;
@@ -213,13 +218,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if expected_sig.is_none()
&& let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder()
{
- expected_sig = self.normalize(
+ let inferred_sig = self.normalize(
obligation.cause.span,
self.deduce_sig_from_projection(
Some(obligation.cause.span),
bound_predicate.rebind(proj_predicate),
),
);
+ // Make sure that we didn't infer a signature that mentions itself.
+ // This can happen when we elaborate certain supertrait bounds that
+ // mention projections containing the `Self` type. See #105401.
+ struct MentionsTy<'tcx> {
+ expected_ty: Ty<'tcx>,
+ }
+ impl<'tcx> TypeVisitor<'tcx> for MentionsTy<'tcx> {
+ type BreakTy = ();
+
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if t == self.expected_ty {
+ ControlFlow::Break(())
+ } else {
+ t.super_visit_with(self)
+ }
+ }
+ }
+ if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
+ expected_sig = inferred_sig;
+ }
}
// Even if we can't infer the full signature, we may be able to
@@ -425,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// `deduce_expectations_from_expected_type` introduces
// late-bound lifetimes defined elsewhere, which we now
// anonymize away, so as not to confuse the user.
- let bound_sig = self.tcx.anonymize_late_bound_regions(bound_sig);
+ let bound_sig = self.tcx.anonymize_bound_vars(bound_sig);
let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig);
@@ -499,7 +524,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// FIXME(#45727): As discussed in [this comment][c1], naively
// forcing equality here actually results in suboptimal error
- // messages in some cases. For now, if there would have been
+ // messages in some cases. For now, if there would have been
// an obvious error, we fallback to declaring the type of the
// closure to be the one the user gave, which allows other
// error message code to trigger.
@@ -622,14 +647,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
),
bound_vars,
);
- // Astconv can't normalize inputs or outputs with escaping bound vars,
- // so normalize them here, after we've wrapped them in a binder.
- let result = self.normalize(self.tcx.hir().span(hir_id), result);
let c_result = self.inh.infcx.canonicalize_response(result);
self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
- result
+ // Normalize only after registering in `user_provided_sigs`.
+ self.normalize(self.tcx.hir().span(hir_id), result)
}
/// Invoked when we are translating the generator that results
@@ -677,17 +700,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
get_future_output(obligation.predicate, obligation.cause.span)
})?
}
- ty::Opaque(def_id, substs) => self
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
.tcx
.bound_explicit_item_bounds(def_id)
.subst_iter_copied(self.tcx, substs)
.find_map(|(p, s)| get_future_output(p, s))?,
ty::Error(_) => return None,
- ty::Projection(proj)
- if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder =>
+ ty::Alias(ty::Projection, proj)
+ if self.tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder =>
{
self.tcx
- .bound_explicit_item_bounds(proj.item_def_id)
+ .bound_explicit_item_bounds(proj.def_id)
.subst_iter_copied(self.tcx, proj.substs)
.find_map(|(p, s)| get_future_output(p, s))?
}
@@ -743,11 +766,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// The `Future` trait has only one associated item, `Output`,
// so check that this is what we see.
let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0];
- if output_assoc_item != predicate.projection_ty.item_def_id {
+ if output_assoc_item != predicate.projection_ty.def_id {
span_bug!(
cause_span,
"projecting associated item `{:?}` from future, which is not Output `{:?}`",
- predicate.projection_ty.item_def_id,
+ predicate.projection_ty.def_id,
output_assoc_item,
);
}
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index f0b349f0c..bbf7b81a2 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -13,7 +13,7 @@
//! useful for freezing mut things (that is, when the expected type is &T
//! but you have &mut T) and also for avoiding the linearity
//! of mut things (when the expected is &mut T and you have &mut T). See
-//! the various `src/test/ui/coerce/*.rs` tests for
+//! the various `tests/ui/coerce/*.rs` tests for
//! examples of where this is useful.
//!
//! ## Subtle note
@@ -118,7 +118,7 @@ fn identity(_: Ty<'_>) -> Vec<Adjustment<'_>> {
vec![]
}
-fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>> {
+fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'_>> {
move |target| vec![Adjustment { kind, target }]
}
@@ -171,6 +171,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// Just ignore error types.
if a.references_error() || b.references_error() {
+ // Best-effort try to unify these types -- we're already on the error path,
+ // so this will have the side-effect of making sure we have no ambiguities
+ // due to `[type error]` and `_` not coercing together.
+ let _ = self.commit_if_ok(|_| self.at(&self.cause, self.param_env).eq(a, b));
return success(vec![], self.fcx.tcx.ty_error(), vec![]);
}
@@ -309,7 +313,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// If we have a parameter of type `&M T_a` and the value
// provided is `expr`, we will be adding an implicit borrow,
- // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
+ // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
// to type check, we will construct the type that `&M*expr` would
// yield.
@@ -336,7 +340,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
continue;
}
- // At this point, we have deref'd `a` to `referent_ty`. So
+ // At this point, we have deref'd `a` to `referent_ty`. So
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
// In the autoderef loop for `&'a mut Vec<T>`, we would get
// three callbacks:
@@ -367,7 +371,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// - if in sub mode, that means we want to use `'b` (the
// region from the target reference) for both
// pointers [2]. This is because sub mode (somewhat
- // arbitrarily) returns the subtype region. In the case
+ // arbitrarily) returns the subtype region. In the case
// where we are coercing to a target type, we know we
// want to use that target type region (`'b`) because --
// for the program to type-check -- it must be the
@@ -379,7 +383,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// annotate the region of a borrow), and regionck has
// code that adds edges from the region of a borrow
// (`'b`, here) into the regions in the borrowed
- // expression (`*x`, here). (Search for "link".)
+ // expression (`*x`, here). (Search for "link".)
// - if in lub mode, things can get fairly complicated. The
// easiest thing is just to make a fresh
// region variable [4], which effectively means we defer
@@ -453,7 +457,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
// As a special case, if we would produce `&'a *x`, that's
// a total no-op. We end up with the type `&'a T` just as
- // we started with. In that case, just skip it
+ // we started with. In that case, just skip it
// altogether. This is just an optimization.
//
// Note that for `&mut`, we DO want to reborrow --
@@ -1472,7 +1476,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// if let Some(x) = ... { }
//
// we wind up with a second match arm that is like `_ =>
- // ()`. That is the case we are considering here. We take
+ // ()`. That is the case we are considering here. We take
// a different path to get the right "expected, found"
// message and so forth (and because we know that
// `expression_ty` will be unit).
@@ -1543,12 +1547,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
err.span_label(cause.span, "return type is not `()`");
}
ObligationCauseCode::BlockTailExpression(blk_id) => {
- let parent_id = fcx.tcx.hir().get_parent_node(blk_id);
+ let parent_id = fcx.tcx.hir().parent_id(blk_id);
err = self.report_return_mismatched_types(
cause,
expected,
found,
- coercion_error.clone(),
+ coercion_error,
fcx,
parent_id,
expression,
@@ -1567,14 +1571,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
cause,
expected,
found,
- coercion_error.clone(),
+ coercion_error,
fcx,
id,
expression,
None,
);
if !fcx.tcx.features().unsized_locals {
- let id = fcx.tcx.hir().get_parent_node(id);
+ let id = fcx.tcx.hir().parent_id(id);
unsized_return = self.is_return_ty_unsized(fcx, id);
}
}
@@ -1583,7 +1587,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
cause,
expected,
found,
- coercion_error.clone(),
+ coercion_error,
);
}
}
@@ -1664,7 +1668,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
let mut pointing_at_return_type = false;
let mut fn_output = None;
- let parent_id = fcx.tcx.hir().get_parent_node(id);
+ let parent_id = fcx.tcx.hir().parent_id(id);
let parent = fcx.tcx.hir().get(parent_id);
if let Some(expr) = expression
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent
@@ -1803,9 +1807,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// Get the return type.
&& let hir::TyKind::OpaqueDef(..) = ty.kind
{
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty);
+ let ty = fcx.astconv().ast_ty_to_ty( ty);
// Get the `impl Trait`'s `DefId`.
- if let ty::Opaque(def_id, _) = ty.kind()
+ if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind()
// Get the `impl Trait`'s `Item` so that we can get its trait bounds and
// get the `Trait`'s `DefId`.
&& let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) =
@@ -1862,7 +1866,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool {
if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id)
&& let hir::FnRetTy::Return(ty) = fn_decl.output
- && let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty)
+ && let ty = fcx.astconv().ast_ty_to_ty( ty)
&& let ty::Dynamic(..) = ty.kind()
{
return true;
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 24184bdbf..f4c4d4310 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,5 +1,6 @@
use crate::FnCtxt;
use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_errors::MultiSpan;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
@@ -23,7 +24,7 @@ use std::cmp::min;
use std::iter;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn emit_coerce_suggestions(
+ pub fn emit_type_mismatch_suggestions(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'tcx>,
@@ -36,11 +37,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
- self.annotate_expected_due_to_let_ty(err, expr, error);
+ self.annotate_alternative_method_deref(err, expr, error);
// Use `||` to give these suggestions a precedence
let _ = self.suggest_missing_parentheses(err, expr)
+ || self.suggest_remove_last_method_call(err, expr, expected)
+ || self.suggest_associated_const(err, expr, expected)
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
+ || self.suggest_option_to_bool(err, expr, expr_ty, expected)
|| self.suggest_compatible_variants(err, expr, expected, expr_ty)
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
@@ -48,13 +52,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
+ || self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
- || self.suggest_option_to_bool(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected);
+ }
+ pub fn emit_coerce_suggestions(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ expr_ty: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+ error: Option<TypeError<'tcx>>,
+ ) {
+ if expr_ty == expected {
+ return;
+ }
+
+ self.annotate_expected_due_to_let_ty(err, expr, error);
+ self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
+ self.check_for_range_as_method_call(err, expr, expr_ty, expected);
+ self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
+ self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
}
/// Requires that the two types unify, and prints an error message if
@@ -163,7 +186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expr = expr.peel_drop_temps();
let cause = self.misc(expr.span);
let expr_ty = self.resolve_vars_with_obligations(checked_ty);
- let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone());
+ let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e);
let is_insufficiently_polymorphic =
matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..));
@@ -189,9 +212,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
- error: Option<TypeError<'_>>,
+ error: Option<TypeError<'tcx>>,
) {
- let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+ let parent = self.tcx.hir().parent_id(expr.hir_id);
match (self.tcx.hir().find(parent), error) {
(Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _)
if init.hir_id == expr.hir_id =>
@@ -238,10 +261,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::Path { res: hir::def::Res::Local(hir_id), .. },
)) => {
if let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(*hir_id) {
- let parent = self.tcx.hir().get_parent_node(pat.hir_id);
primary_span = pat.span;
secondary_span = pat.span;
- match self.tcx.hir().find(parent) {
+ match self.tcx.hir().find_parent(pat.hir_id) {
Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => {
primary_span = ty.span;
post_message = " type";
@@ -286,10 +308,177 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.downgrade_to_delayed_bug();
}
}
+ (
+ Some(hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Binary(_, lhs, rhs), ..
+ })),
+ Some(TypeError::Sorts(ExpectedFound { expected, .. })),
+ ) if rhs.hir_id == expr.hir_id
+ && self.typeck_results.borrow().expr_ty_adjusted_opt(lhs) == Some(expected) =>
+ {
+ err.span_label(lhs.span, &format!("expected because this is `{expected}`"));
+ }
_ => {}
}
}
+ fn annotate_alternative_method_deref(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ error: Option<TypeError<'tcx>>,
+ ) {
+ let parent = self.tcx.hir().parent_id(expr.hir_id);
+ let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else {return;};
+ let Some(hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Assign(lhs, rhs, _), ..
+ })) = self.tcx.hir().find(parent) else {return; };
+ if rhs.hir_id != expr.hir_id || expected.is_closure() {
+ return;
+ }
+ let hir::ExprKind::Unary(hir::UnOp::Deref, deref) = lhs.kind else { return; };
+ let hir::ExprKind::MethodCall(path, base, args, _) = deref.kind else { return; };
+ let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; };
+
+ let Ok(pick) = self
+ .lookup_probe_for_diagnostic(
+ path.ident,
+ self_ty,
+ deref,
+ probe::ProbeScope::TraitsInScope,
+ None,
+ ) else {
+ return;
+ };
+ let in_scope_methods = self.probe_for_name_many(
+ probe::Mode::MethodCall,
+ path.ident,
+ Some(expected),
+ probe::IsSuggestion(true),
+ self_ty,
+ deref.hir_id,
+ probe::ProbeScope::TraitsInScope,
+ );
+ let other_methods_in_scope: Vec<_> =
+ in_scope_methods.iter().filter(|c| c.item.def_id != pick.item.def_id).collect();
+
+ let all_methods = self.probe_for_name_many(
+ probe::Mode::MethodCall,
+ path.ident,
+ Some(expected),
+ probe::IsSuggestion(true),
+ self_ty,
+ deref.hir_id,
+ probe::ProbeScope::AllTraits,
+ );
+ let suggestions: Vec<_> = all_methods
+ .into_iter()
+ .filter(|c| c.item.def_id != pick.item.def_id)
+ .map(|c| {
+ let m = c.item;
+ let substs = ty::InternalSubsts::for_item(self.tcx, m.def_id, |param, _| {
+ self.var_for_def(deref.span, param)
+ });
+ vec![
+ (
+ deref.span.until(base.span),
+ format!(
+ "{}({}",
+ with_no_trimmed_paths!(
+ self.tcx.def_path_str_with_substs(m.def_id, substs,)
+ ),
+ match self.tcx.fn_sig(m.def_id).input(0).skip_binder().kind() {
+ ty::Ref(_, _, hir::Mutability::Mut) => "&mut ",
+ ty::Ref(_, _, _) => "&",
+ _ => "",
+ },
+ ),
+ ),
+ match &args[..] {
+ [] => (base.span.shrink_to_hi().with_hi(deref.span.hi()), ")".to_string()),
+ [first, ..] => (base.span.between(first.span), ", ".to_string()),
+ },
+ ]
+ })
+ .collect();
+ if suggestions.is_empty() {
+ return;
+ }
+ let mut path_span: MultiSpan = path.ident.span.into();
+ path_span.push_span_label(
+ path.ident.span,
+ with_no_trimmed_paths!(format!(
+ "refers to `{}`",
+ self.tcx.def_path_str(pick.item.def_id),
+ )),
+ );
+ let container_id = pick.item.container_id(self.tcx);
+ let container = with_no_trimmed_paths!(self.tcx.def_path_str(container_id));
+ for def_id in pick.import_ids {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+ path_span.push_span_label(
+ self.tcx.hir().span(hir_id),
+ format!("`{container}` imported here"),
+ );
+ }
+ let tail = with_no_trimmed_paths!(match &other_methods_in_scope[..] {
+ [] => return,
+ [candidate] => format!(
+ "the method of the same name on {} `{}`",
+ match candidate.kind {
+ probe::CandidateKind::InherentImplCandidate(..) => "the inherent impl for",
+ _ => "trait",
+ },
+ self.tcx.def_path_str(candidate.item.container_id(self.tcx))
+ ),
+ [.., last] if other_methods_in_scope.len() < 5 => {
+ format!(
+ "the methods of the same name on {} and `{}`",
+ other_methods_in_scope[..other_methods_in_scope.len() - 1]
+ .iter()
+ .map(|c| format!(
+ "`{}`",
+ self.tcx.def_path_str(c.item.container_id(self.tcx))
+ ))
+ .collect::<Vec<String>>()
+ .join(", "),
+ self.tcx.def_path_str(last.item.container_id(self.tcx))
+ )
+ }
+ _ => format!(
+ "the methods of the same name on {} other traits",
+ other_methods_in_scope.len()
+ ),
+ });
+ err.span_note(
+ path_span,
+ &format!(
+ "the `{}` call is resolved to the method in `{container}`, shadowing {tail}",
+ path.ident,
+ ),
+ );
+ if suggestions.len() > other_methods_in_scope.len() {
+ err.note(&format!(
+ "additionally, there are {} other available methods that aren't in scope",
+ suggestions.len() - other_methods_in_scope.len()
+ ));
+ }
+ err.multipart_suggestions(
+ &format!(
+ "you might have meant to call {}; you can use the fully-qualified path to call {} \
+ explicitly",
+ if suggestions.len() == 1 {
+ "the other method"
+ } else {
+ "one of the other methods"
+ },
+ if suggestions.len() == 1 { "it" } else { "one of them" },
+ ),
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+
/// If the expected type is an enum (Issue #55250) with any variants whose
/// sole field is of the found type, suggest such variants. (Issue #42764)
fn suggest_compatible_variants(
@@ -324,7 +513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Unroll desugaring, to make sure this works for `for` loops etc.
loop {
- parent = self.tcx.hir().get_parent_node(id);
+ parent = self.tcx.hir().parent_id(id);
if let Some(parent_span) = self.tcx.hir().opt_span(parent) {
if parent_span.find_ancestor_inside(expr.span).is_some() {
// The parent node is part of the same span, so is the result of the
@@ -396,7 +585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
- .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)"));
+ .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
let sole_field_ty = sole_field.ty(self.tcx, substs);
if self.can_coerce(expr_ty, sole_field_ty) {
@@ -604,12 +793,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
};
- let local_parent = self.tcx.hir().get_parent_node(local_id);
+ let local_parent = self.tcx.hir().parent_id(local_id);
let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) = self.tcx.hir().find(local_parent) else {
return None;
};
- let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
+ let param_parent = self.tcx.hir().parent_id(*param_hir_id);
let Some(Node::Expr(hir::Expr {
hir_id: expr_hir_id,
kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
@@ -618,7 +807,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
};
- let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
+ let expr_parent = self.tcx.hir().parent_id(*expr_hir_id);
let hir = self.tcx.hir().find(expr_parent);
let closure_params_len = closure_fn_decl.inputs.len();
let (
@@ -671,7 +860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => None,
}?;
- match hir.find(hir.get_parent_node(expr.hir_id))? {
+ match hir.find_parent(expr.hir_id)? {
Node::ExprField(field) => {
if field.ident.name == local.name && field.is_shorthand {
return Some(local.name);
@@ -697,7 +886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Returns whether the given expression is an `else if`.
pub(crate) fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
if let hir::ExprKind::If(..) = expr.kind {
- let parent_id = self.tcx.hir().get_parent_node(expr.hir_id);
+ let parent_id = self.tcx.hir().parent_id(expr.hir_id);
if let Some(Node::Expr(hir::Expr {
kind: hir::ExprKind::If(_, _, Some(else_expr)),
..
@@ -854,7 +1043,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Assign(..),
..
- })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
+ })) = self.tcx.hir().find_parent(expr.hir_id)
{
if mutability.is_mut() {
// Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
@@ -976,7 +1165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
// If we've reached our target type with just removing `&`, then just print now.
- if steps == 0 {
+ if steps == 0 && !remove.trim().is_empty() {
return Some((
prefix_span,
format!("consider removing the `{}`", remove.trim()),
@@ -1035,6 +1224,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
(prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
};
+ if suggestion.trim().is_empty() {
+ return None;
+ }
return Some((
span,
@@ -1081,9 +1273,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut sugg = vec![];
- if let Some(hir::Node::ExprField(field)) =
- self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
- {
+ if let Some(hir::Node::ExprField(field)) = self.tcx.hir().find_parent(expr.hir_id) {
// `expr` is a literal field for a struct, only suggest if appropriate
if field.is_shorthand {
// This is a field literal
@@ -1265,7 +1455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
match (&expected_ty.kind(), &checked_ty.kind()) {
- (&ty::Int(ref exp), &ty::Int(ref found)) => {
+ (ty::Int(exp), ty::Int(found)) => {
let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
{
(Some(exp), Some(found)) if exp < found => (true, false),
@@ -1278,7 +1468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
true
}
- (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
+ (ty::Uint(exp), ty::Uint(found)) => {
let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
{
(Some(exp), Some(found)) if exp < found => (true, false),
@@ -1311,7 +1501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
true
}
- (&ty::Float(ref exp), &ty::Float(ref found)) => {
+ (ty::Float(exp), ty::Float(found)) => {
if found.bit_width() < exp.bit_width() {
suggest_to_change_suffix_or_into(err, false, true);
} else if literal_is_ty_suffixed(expr) {
@@ -1347,7 +1537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
true
}
- (&ty::Float(ref exp), &ty::Uint(ref found)) => {
+ (ty::Float(exp), ty::Uint(found)) => {
// if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
if exp.bit_width() > found.bit_width().unwrap_or(256) {
err.multipart_suggestion_verbose(
@@ -1376,7 +1566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
true
}
- (&ty::Float(ref exp), &ty::Int(ref found)) => {
+ (ty::Float(exp), ty::Int(found)) => {
// if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
if exp.bit_width() > found.bit_width().unwrap_or(256) {
err.multipart_suggestion_verbose(
@@ -1422,4 +1612,189 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => false,
}
}
+
+ /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
+ pub fn check_for_range_as_method_call(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ checked_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) {
+ if !hir::is_range_literal(expr) {
+ return;
+ }
+ let hir::ExprKind::Struct(
+ hir::QPath::LangItem(LangItem::Range, ..),
+ [start, end],
+ _,
+ ) = expr.kind else { return; };
+ let parent = self.tcx.hir().parent_id(expr.hir_id);
+ if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) {
+ // Ignore `Foo { field: a..Default::default() }`
+ return;
+ }
+ let mut expr = end.expr;
+ let mut expectation = Some(expected_ty);
+ while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
+ // Getting to the root receiver and asserting it is a fn call let's us ignore cases in
+ // `tests/ui/methods/issues/issue-90315.stderr`.
+ expr = rcvr;
+ // If we have more than one layer of calls, then the expected ty
+ // cannot guide the method probe.
+ expectation = None;
+ }
+ let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
+ let ty::Adt(adt, _) = checked_ty.kind() else { return; };
+ if self.tcx.lang_items().range_struct() != Some(adt.did()) {
+ return;
+ }
+ if let ty::Adt(adt, _) = expected_ty.kind()
+ && self.tcx.lang_items().range_struct() == Some(adt.did())
+ {
+ return;
+ }
+ // Check if start has method named end.
+ let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
+ let [hir::PathSegment { ident, .. }] = p.segments else { return; };
+ let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
+ let Ok(_pick) = self.lookup_probe_for_diagnostic(
+ *ident,
+ self_ty,
+ expr,
+ probe::ProbeScope::AllTraits,
+ expectation,
+ ) else { return; };
+ let mut sugg = ".";
+ let mut span = start.expr.span.between(end.expr.span);
+ if span.lo() + BytePos(2) == span.hi() {
+ // There's no space between the start, the range op and the end, suggest removal which
+ // will be more noticeable than the replacement of `..` with `.`.
+ span = span.with_lo(span.lo() + BytePos(1));
+ sugg = "";
+ }
+ err.span_suggestion_verbose(
+ span,
+ "you likely meant to write a method call instead of a range",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+
+ /// Identify when the type error is because `()` is found in a binding that was assigned a
+ /// block without a tail expression.
+ fn check_for_binding_assigned_block_without_tail_expression(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ checked_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) {
+ if !checked_ty.is_unit() {
+ return;
+ }
+ let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; };
+ let hir::def::Res::Local(hir_id) = path.res else { return; };
+ let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
+ return;
+ };
+ let Some(hir::Node::Local(hir::Local {
+ ty: None,
+ init: Some(init),
+ ..
+ })) = self.tcx.hir().find_parent(pat.hir_id) else { return; };
+ let hir::ExprKind::Block(block, None) = init.kind else { return; };
+ if block.expr.is_some() {
+ return;
+ }
+ let [.., stmt] = block.stmts else {
+ err.span_label(block.span, "this empty block is missing a tail expression");
+ return;
+ };
+ let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; };
+ let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else { return; };
+ if self.can_eq(self.param_env, expected_ty, ty).is_ok() {
+ err.span_suggestion_short(
+ stmt.span.with_lo(tail_expr.span.hi()),
+ "remove this semicolon",
+ "",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_label(block.span, "this block is missing a tail expression");
+ }
+ }
+
+ fn check_wrong_return_type_due_to_generic_arg(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ checked_ty: Ty<'tcx>,
+ ) {
+ let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { return; };
+ enum CallableKind {
+ Function,
+ Method,
+ Constructor,
+ }
+ let mut maybe_emit_help = |def_id: hir::def_id::DefId,
+ callable: rustc_span::symbol::Ident,
+ args: &[hir::Expr<'_>],
+ kind: CallableKind| {
+ let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
+ let fn_ty = self.tcx.bound_type_of(def_id).0;
+ if !fn_ty.is_fn() {
+ return;
+ }
+ let fn_sig = fn_ty.fn_sig(self.tcx).skip_binder();
+ let Some(&arg) = fn_sig.inputs().get(arg_idx + if matches!(kind, CallableKind::Method) { 1 } else { 0 }) else { return; };
+ if matches!(arg.kind(), ty::Param(_))
+ && fn_sig.output().contains(arg)
+ && self.node_ty(args[arg_idx].hir_id) == checked_ty
+ {
+ let mut multi_span: MultiSpan = parent_expr.span.into();
+ multi_span.push_span_label(
+ args[arg_idx].span,
+ format!(
+ "this argument influences the {} of `{}`",
+ if matches!(kind, CallableKind::Constructor) {
+ "type"
+ } else {
+ "return type"
+ },
+ callable
+ ),
+ );
+ err.span_help(
+ multi_span,
+ format!(
+ "the {} `{}` due to the type of the argument passed",
+ match kind {
+ CallableKind::Function => "return type of this call is",
+ CallableKind::Method => "return type of this call is",
+ CallableKind::Constructor => "type constructed contains",
+ },
+ checked_ty
+ ),
+ );
+ }
+ };
+ match parent_expr.kind {
+ hir::ExprKind::Call(fun, args) => {
+ let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = fun.kind else { return; };
+ let hir::def::Res::Def(kind, def_id) = path.res else { return; };
+ let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
+ CallableKind::Constructor
+ } else {
+ CallableKind::Function
+ };
+ maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind);
+ }
+ hir::ExprKind::MethodCall(method, _receiver, args, _span) => {
+ let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) else { return; };
+ maybe_emit_help(def_id, method.ident, args, CallableKind::Method)
+ }
+ _ => return,
+ }
+ }
}
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 507272fde..5b4fd5e4a 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -172,3 +172,36 @@ impl AddToDiagnostic for TypeMismatchFruTypo {
);
}
}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_lang_start_incorrect_number_params)]
+#[note(hir_typeck_lang_start_incorrect_number_params_note_expected_count)]
+#[note(hir_typeck_lang_start_expected_sig_note)]
+pub struct LangStartIncorrectNumberArgs {
+ #[primary_span]
+ pub params_span: Span,
+ pub found_param_count: usize,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_lang_start_incorrect_param)]
+pub struct LangStartIncorrectParam<'tcx> {
+ #[primary_span]
+ #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
+ pub param_span: Span,
+
+ pub param_num: usize,
+ pub expected_ty: Ty<'tcx>,
+ pub found_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_lang_start_incorrect_ret_ty)]
+pub struct LangStartIncorrectRetTy<'tcx> {
+ #[primary_span]
+ #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
+ pub ret_span: Span,
+
+ pub expected_ty: Ty<'tcx>,
+ pub found_ty: Ty<'tcx>,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index ed87b94a0..bc7474cdf 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -104,16 +104,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
- // FIXME(compiler-errors): We probably should fold some of the
- // `suggest_` functions from `emit_coerce_suggestions` into here,
- // since some of those aren't necessarily just coerce suggestions.
- let _ = self.suggest_deref_ref_or_into(
+ let _ = self.emit_type_mismatch_suggestions(
&mut err,
expr.peel_drop_temps(),
- expected_ty,
ty,
+ expected_ty,
None,
- ) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty);
+ None,
+ );
extend_err(&mut err);
err.emit();
}
@@ -236,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) => self.check_expr_path(qpath, expr, args),
_ => self.check_expr_kind(expr, expected),
});
+ let ty = self.resolve_vars_if_possible(ty);
// Warn for non-block expressions with diverging children.
match expr.kind {
@@ -352,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Struct(qpath, fields, ref base_expr) => {
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
}
- ExprKind::Field(base, field) => self.check_field(expr, &base, field),
+ ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
hir::ExprKind::Err => tcx.ty_error(),
@@ -397,7 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
E0614,
"type `{oprnd_t}` cannot be dereferenced",
);
- let sp = tcx.sess.source_map().start_point(expr.span);
+ let sp = tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) =
tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
{
@@ -460,9 +459,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
hir::BorrowKind::Ref => {
// Note: at this point, we cannot say what the best lifetime
- // is to use for resulting pointer. We want to use the
+ // is to use for resulting pointer. We want to use the
// shortest lifetime possible so as to avoid spurious borrowck
- // errors. Moreover, the longest lifetime will depend on the
+ // errors. Moreover, the longest lifetime will depend on the
// precise details of the value whose address is being taken
// (and how long it is valid), which we don't know yet until
// type inference is complete.
@@ -688,7 +687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
} else {
// If `ctxt.coerce` is `None`, we can just ignore
- // the type of the expression. This is because
+ // the type of the expression. This is because
// either this was a break *without* a value, in
// which case it is always a legal type (`()`), or
// else an error would have been flagged by the
@@ -922,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
original_expr_id: HirId,
then: impl FnOnce(&hir::Expr<'_>),
) {
- let mut parent = self.tcx.hir().get_parent_node(original_expr_id);
+ let mut parent = self.tcx.hir().parent_id(original_expr_id);
while let Some(node) = self.tcx.hir().find(parent) {
match node {
hir::Node::Expr(hir::Expr {
@@ -945,7 +944,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}) => {
// Check if our original expression is a child of the condition of a while loop
let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| {
- self.tcx.hir().find_parent_node(*id)
+ self.tcx.hir().opt_parent_id(*id)
})
.take_while(|id| *id != parent)
.any(|id| id == expr.hir_id);
@@ -961,7 +960,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| hir::Node::TraitItem(_)
| hir::Node::Crate(_) => break,
_ => {
- parent = self.tcx.hir().get_parent_node(parent);
+ parent = self.tcx.hir().parent_id(parent);
}
}
}
@@ -1085,7 +1084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Do not suggest `if let x = y` as `==` is way more likely to be the intention.
let hir = self.tcx.hir();
if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
- hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id)))
+ hir.get_parent(hir.parent_id(expr.hir_id))
{
err.span_suggestion_verbose(
expr.span.shrink_to_lo(),
@@ -1245,6 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
SelfSource::MethodCall(rcvr),
error,
Some((rcvr, args)),
+ expected,
) {
err.emit();
}
@@ -1874,7 +1874,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
if let ExprKind::Struct(
QPath::LangItem(LangItem::Range, ..),
- &[ref range_start, ref range_end],
+ [range_start, range_end],
_,
) = last_expr_field.expr.kind
&& let variant_field =
@@ -2187,6 +2187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
base: &'tcx hir::Expr<'tcx>,
field: Ident,
+ expected: Expectation<'tcx>,
) -> Ty<'tcx> {
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
let base_ty = self.check_expr(base);
@@ -2218,7 +2219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
return field_ty;
}
- private_candidate = Some((adjustments, base_def.did(), field_ty));
+ private_candidate = Some((adjustments, base_def.did()));
}
}
ty::Tuple(tys) => {
@@ -2241,16 +2242,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
- if let Some((adjustments, did, field_ty)) = private_candidate {
+ if let Some((adjustments, did)) = private_candidate {
// (#90483) apply adjustments to avoid ExprUseVisitor from
// creating erroneous projection.
self.apply_adjustments(base, adjustments);
- self.ban_private_field_access(expr, base_ty, field, did);
- return field_ty;
+ self.ban_private_field_access(expr, base_ty, field, did, expected.only_has_type(self));
+ return self.tcx().ty_error();
}
if field.name == kw::Empty {
- } else if self.method_exists(field, base_ty, expr.hir_id, true) {
+ } else if self.method_exists(
+ field,
+ base_ty,
+ expr.hir_id,
+ true,
+ expected.only_has_type(self),
+ ) {
self.ban_take_value_of_method(expr, base_ty, field);
} else if !base_ty.is_primitive_ty() {
self.ban_nonexisting_field(field, base, expr, base_ty);
@@ -2391,7 +2398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Param(param_ty) => {
self.point_at_param_definition(&mut err, param_ty);
}
- ty::Opaque(_, _) => {
+ ty::Alias(ty::Opaque, _) => {
self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
}
_ => {}
@@ -2424,10 +2431,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn ban_private_field_access(
&self,
- expr: &hir::Expr<'_>,
+ expr: &hir::Expr<'tcx>,
expr_t: Ty<'tcx>,
field: Ident,
base_did: DefId,
+ return_ty: Option<Ty<'tcx>>,
) {
let struct_path = self.tcx().def_path_str(base_did);
let kind_name = self.tcx().def_kind(base_did).descr(base_did);
@@ -2439,7 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
err.span_label(field.span, "private field");
// Also check if an accessible method exists, which is often what is meant.
- if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id)
+ if self.method_exists(field, expr_t, expr.hir_id, false, return_ty)
+ && !self.expr_in_place(expr.hir_id)
{
self.suggest_method_call(
&mut err,
@@ -2453,7 +2462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}
- fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) {
+ fn ban_take_value_of_method(&self, expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident) {
let mut err = type_error_struct!(
self.tcx().sess,
field.span,
@@ -2464,7 +2473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(field.span, "method, not a field");
let expr_is_call =
if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id))
+ self.tcx.hir().get_parent(expr.hir_id)
{
expr.hir_id == callee.hir_id
} else {
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 03b174c77..c8cda0dc9 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -417,7 +417,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
// Named constants have to be equated with the value
// being matched, so that's a read of the value being matched.
//
- // FIXME: We don't actually reads for ZSTs.
+ // FIXME: We don't actually reads for ZSTs.
needs_to_be_read = true;
}
_ => {
@@ -756,8 +756,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
/// closure as the DefId.
fn walk_captures(&mut self, closure_expr: &hir::Closure<'_>) {
- fn upvar_is_local_variable<'tcx>(
- upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
+ fn upvar_is_local_variable(
+ upvars: Option<&FxIndexMap<hir::HirId, hir::Upvar>>,
upvar_id: hir::HirId,
body_owner_is_closure: bool,
) -> bool {
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index ac6b0924a..dde879780 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -42,7 +42,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
// We now see if we can make progress. This might cause us to
// unify inference variables for opaque types, since we may
// have unified some other type variables during the first
- // phase of fallback. This means that we only replace
+ // phase of fallback. This means that we only replace
// inference variables with their underlying opaque types as a
// last resort.
//
@@ -76,7 +76,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
// (and the setting of `#![feature(never_type_fallback)]`).
//
// Fallback becomes very dubious if we have encountered
- // type-checking errors. In that case, fallback to Error.
+ // type-checking errors. In that case, fallback to Error.
//
// Sets `FnCtxt::fallback_has_occurred` if fallback is performed
// during this call.
@@ -136,7 +136,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
/// constrained to have some other type).
///
/// However, the fallback used to be `()` (before the `!` type was
- /// added). Moreover, there are cases where the `!` type 'leaks
+ /// added). Moreover, there are cases where the `!` type 'leaks
/// out' from dead code into type variables that affect live
/// code. The most common case is something like this:
///
@@ -149,7 +149,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
/// ```
///
/// Here, coercing the type `!` into `?M` will create a diverging
- /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
+ /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
/// ?M`. If `?M` winds up unconstrained, then `?X` will
/// fallback. If it falls back to `!`, then all the type variables
/// will wind up equal to `!` -- this includes the type `?D`
@@ -185,7 +185,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
///
/// The algorithm we use:
/// * Identify all variables that are coerced *into* by a
- /// diverging variable. Do this by iterating over each
+ /// diverging variable. Do this by iterating over each
/// diverging, unsolved variable and finding all variables
/// reachable from there. Call that set `D`.
/// * Walk over all unsolved, non-diverging variables, and find
@@ -308,7 +308,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
if relationship.self_in_trait && relationship.output {
// This case falls back to () to ensure that the code pattern in
- // src/test/ui/never_type/fallback-closure-ret.rs continues to
+ // tests/ui/never_type/fallback-closure-ret.rs continues to
// compile when never_type_fallback is enabled.
//
// This rule is not readily explainable from first principles,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 952d27262..6ed8adb47 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1,7 +1,7 @@
use crate::callee::{self, DeferredCallResolution};
use crate::method::{self, MethodCallee, SelfSource};
use crate::rvalue_scopes;
-use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -10,6 +10,9 @@ 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_hir_analysis::astconv::generics::{
+ check_generic_arg_count_for_call, create_substs_for_generic_args,
+};
use rustc_hir_analysis::astconv::{
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
GenericArgCountResult, IsMethodCall, PathSeg,
@@ -22,9 +25,9 @@ use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, Ty, UserType,
+ self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType,
};
-use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts};
+use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
@@ -161,47 +164,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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>) {
@@ -333,22 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- /// 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(span, value);
- debug!("instantiate_type_scheme = {:?}", result);
- result
- }
-
- /// As `instantiate_type_scheme`, but for the bounds found in a
- /// generic type scheme.
+ /// Instantiates and normalizes the bounds for a given item
pub(in super::super) fn instantiate_bounds(
&self,
span: Span,
@@ -425,23 +372,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
- let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t);
+ pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> {
+ RawTy { raw: ty, normalized: self.normalize(span, ty) }
+ }
+
+ pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> {
+ let t = self.astconv().ast_ty_to_ty(ast_t);
self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None));
- t
+ self.handle_raw_ty(ast_t.span, 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));
+ if Self::can_contain_user_lifetime_bounds(ty.raw) {
+ let c_ty = self.canonicalize_response(UserType::Ty(ty.raw));
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
+ ty.normalized
+ }
+
+ pub(super) fn user_substs_for_adt(ty: RawTy<'tcx>) -> UserSubsts<'tcx> {
+ match (ty.raw.kind(), ty.normalized.kind()) {
+ (ty::Adt(_, substs), _) => UserSubsts { substs, user_self_ty: None },
+ (_, ty::Adt(adt, substs)) => UserSubsts {
+ substs,
+ user_self_ty: Some(UserSelfTy { impl_def_id: adt.did(), self_ty: ty.raw }),
+ },
+ _ => bug!("non-adt type {:?}", ty),
+ }
}
pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
@@ -711,12 +673,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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.
+ // See tests/ui/impl-trait/hidden-type-is-opaque.rs and
+ // tests/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 ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind()
&& let Some(def_id) = def_id.as_local()
&& self.opaque_type_origin(def_id, DUMMY_SP).is_some() {
return None;
@@ -795,7 +757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &'tcx QPath<'tcx>,
hir_id: hir::HirId,
span: Span,
- ) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
+ ) -> (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
debug!(
"resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
qpath, hir_id, span
@@ -818,7 +780,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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)
+ let ty = self.astconv().ast_ty_to_ty_in_path(qself);
+ (self.handle_raw_ty(span, ty), qself, segment)
}
QPath::LangItem(..) => {
bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`")
@@ -826,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
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));
+ self.register_wf_obligation(ty.raw.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));
@@ -834,7 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let item_name = item_segment.ident;
let result = self
- .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id)
+ .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
.or_else(|error| {
let result = match error {
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
@@ -845,17 +808,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 !(matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait()) {
+ self.register_wf_obligation(
+ ty.raw.into(),
+ qself.span,
+ traits::WellFormed(None),
+ );
}
if item_name.name != kw::Empty {
if let Some(mut e) = self.report_method_error(
span,
- ty,
+ ty.normalized,
item_name,
SelfSource::QPath(qself),
error,
None,
+ Expectation::NoExpectation,
) {
e.emit();
}
@@ -864,7 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
if result.is_ok() {
- self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None));
+ self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None));
}
// Write back the new resolution.
@@ -1001,7 +969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn instantiate_value_path(
&self,
segments: &[hir::PathSegment<'_>],
- self_ty: Option<Ty<'tcx>>,
+ self_ty: Option<RawTy<'tcx>>,
res: Res,
span: Span,
hir_id: hir::HirId,
@@ -1010,8 +978,12 @@ impl<'a, 'tcx> FnCtxt<'a, '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,
+ Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments(
+ segments,
+ self_ty.map(|ty| ty.raw),
+ kind,
+ def_id,
+ span,
),
_ => bug!("instantiate_value_path on {:?}", res),
};
@@ -1022,8 +994,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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 });
+ let adt_def = self_ty.normalized.ty_adt_def().unwrap();
+ user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty: self_ty.raw });
is_alias_variant_ctor = true;
}
Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
@@ -1042,7 +1014,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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");
+ let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw;
user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty });
}
}
@@ -1057,8 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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,
+ let generics_has_err = self.astconv().prohibit_generics(
segments.iter().enumerate().filter_map(|(index, seg)| {
if !generic_segs.contains(&index) || is_alias_variant_ctor {
Some(seg)
@@ -1099,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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(
+ let arg_count = check_generic_arg_count_for_call(
tcx,
span,
def_id,
@@ -1124,19 +1095,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.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_kind, ctor_def_id) = variant.ctor.unwrap();
- (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs))
+ let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id));
+ match ty.normalized.ty_adt_def() {
+ Some(adt_def) if adt_def.has_ctor() => {
+ let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap();
+ let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
+ let user_substs = Self::user_substs_for_adt(ty);
+ user_self_ty = user_substs.user_self_ty;
+ (new_res, Some(user_substs.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() {
+ if let Some(adt_def) = ty.normalized.ty_adt_def() {
match adt_def.adt_kind() {
AdtKind::Enum => {
err.help("did you mean to use one of the enum's variants?");
@@ -1160,10 +1133,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
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() {
@@ -1209,10 +1178,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> ty::GenericArg<'tcx> {
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
- <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into()
+ self.fcx.astconv().ast_region_to_region(lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
- self.fcx.to_ty(ty).into()
+ self.fcx.to_ty(ty).raw.into()
}
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
@@ -1244,10 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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()
+ tcx.bound_type_of(param.def_id).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.
@@ -1258,9 +1224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
GenericParamDefKind::Const { has_default } => {
if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
+ tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
self.fcx.var_for_def(self.span, param)
}
@@ -1269,13 +1233,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- let substs = self_ctor_substs.unwrap_or_else(|| {
- <dyn AstConv<'_>>::create_substs_for_generic_args(
+ let substs_raw = self_ctor_substs.unwrap_or_else(|| {
+ create_substs_for_generic_args(
tcx,
def_id,
&[],
has_self,
- self_ty,
+ self_ty.map(|s| s.raw),
&arg_count,
&mut CreateCtorSubstsContext {
fcx: self,
@@ -1286,17 +1250,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
)
});
- 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.write_user_type_annotation_from_substs(hir_id, def_id, substs_raw, user_self_ty);
+
+ // Normalize only after registering type annotations.
+ let substs = self.normalize(span, substs_raw);
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);
+ let ty = tcx.bound_type_of(def_id);
+ assert!(!substs.has_escaping_bound_vars());
+ assert!(!ty.0.has_escaping_bound_vars());
+ let ty_substituted = self.normalize(span, ty.subst(tcx, substs));
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`
@@ -1304,9 +1272,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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);
+ let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs));
+ let self_ty = self.normalize(span, self_ty);
match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) {
Ok(ok) => self.register_infer_ok_obligations(ok),
Err(_) => {
@@ -1455,9 +1422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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))
- {
+ while let hir::Node::Expr(parent_expr) = self.tcx.hir().get_parent(expr_id) {
match &parent_expr.kind {
hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
if lhs.hir_id == expr_id {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
index fc83994ca..6f26afcaf 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
@@ -4,15 +4,13 @@ use rustc_index::vec::IndexVec;
use rustc_middle::ty::error::TypeError;
rustc_index::newtype_index! {
- pub(crate) struct ExpectedIdx {
- DEBUG_FORMAT = "ExpectedIdx({})",
- }
+ #[debug_format = "ExpectedIdx({})"]
+ pub(crate) struct ExpectedIdx {}
}
rustc_index::newtype_index! {
- pub(crate) struct ProvidedIdx {
- DEBUG_FORMAT = "ProvidedIdx({})",
- }
+ #[debug_format = "ProvidedIdx({})"]
+ pub(crate) struct ProvidedIdx {}
}
impl ExpectedIdx {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 60fec05d3..2d841d53f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -5,11 +5,11 @@ use crate::method::MethodCallee;
use crate::Expectation::*;
use crate::TupleArgumentsFlag::*;
use crate::{
- struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs,
+ struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
TupleArgumentsFlag,
};
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -28,7 +28,7 @@ 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::symbol::{kw, Ident};
use rustc_span::{self, sym, Span};
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
@@ -214,7 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"cannot use call notation; the first type parameter \
for the function trait is neither a tuple nor unit"
)
- .delay_as_bug();
+ .emit();
(self.err_args(provided_args.len()), None)
}
}
@@ -473,7 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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 {
+ let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
hir::ExprKind::Call(
hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
_,
@@ -481,12 +481,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Res::Def(DefKind::Ctor(of, _), _) =
self.typeck_results.borrow().qpath_res(qpath, *hir_id)
{
- (call_span, *span, Some(of), false)
+ let name = match of {
+ CtorOf::Struct => "struct",
+ CtorOf::Variant => "enum variant",
+ };
+ (call_span, *span, name, false)
} else {
- (call_span, *span, None, false)
+ (call_span, *span, "function", false)
}
}
- hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false),
+ hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, "function", false),
hir::ExprKind::MethodCall(path_segment, _, _, span) => {
let ident_span = path_segment.ident.span;
let ident_span = if let Some(args) = path_segment.args {
@@ -494,17 +498,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
ident_span
};
- // methods are never ctors
- (*span, ident_span, None, true)
+ (*span, ident_span, "method", 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 {
@@ -690,8 +688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err = tcx.sess.struct_span_err_with_code(
full_call_span,
&format!(
- "this {} takes {}{} but {} {} supplied",
- call_name,
+ "{call_name} takes {}{} but {} {} supplied",
if c_variadic { "at least " } else { "" },
potentially_plural_count(
formal_and_expected_inputs.len(),
@@ -1013,7 +1010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
args_span
};
- labels.push((span, format!("multiple arguments are missing")));
+ labels.push((span, "multiple arguments are missing".to_string()));
suggestion_text = match suggestion_text {
SuggestionText::None | SuggestionText::Provide(_) => {
SuggestionText::Provide(true)
@@ -1141,6 +1138,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"()".to_string()
} else if expected_ty.is_suggestable(tcx, false) {
format!("/* {} */", expected_ty)
+ } else if let Some(fn_def_id) = fn_def_id
+ && self.tcx.def_kind(fn_def_id).is_fn_like()
+ && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
+ && let Some(arg) = self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit)
+ && arg.name != kw::SelfLower
+ {
+ format!("/* {} */", arg.name)
} else {
"/* value */".to_string()
}
@@ -1169,7 +1173,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match lit.node {
ast::LitKind::Str(..) => tcx.mk_static_str(),
- ast::LitKind::ByteStr(ref v) => {
+ 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,
@@ -1215,31 +1219,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
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::Variant, _) => match ty.normalized.ty_adt_def() {
+ Some(adt) => {
+ Some((adt.variant_of_res(def), adt.did(), Self::user_substs_for_adt(ty)))
+ }
+ _ => bug!("unexpected type: {:?}", ty.normalized),
},
Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
| Res::SelfTyParam { .. }
- | Res::SelfTyAlias { .. } => match ty.kind() {
- ty::Adt(adt, substs) if !adt.is_enum() => {
- Some((adt.non_enum_variant(), adt.did(), substs))
+ | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() {
+ Some(adt) if !adt.is_enum() => {
+ Some((adt.non_enum_variant(), adt.did(), Self::user_substs_for_adt(ty)))
}
_ => None,
},
_ => bug!("unexpected definition: {:?}", def),
};
- if let Some((variant, did, substs)) = variant {
+ if let Some((variant, did, ty::UserSubsts { substs, user_self_ty })) = variant {
debug!("check_struct_path: did={:?} substs={:?}", did, substs);
- self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
+
+ // Register type annotation.
+ self.write_user_type_annotation_from_substs(hir_id, did, substs, user_self_ty);
// Check bounds on type arguments used in the path.
self.add_required_obligations_for_hir(path_span, did, substs, hir_id);
- Some((variant, ty))
+ Some((variant, ty.normalized))
} else {
- match ty.kind() {
+ match ty.normalized.kind() {
ty::Error(_) => {
// E0071 might be caused by a spelling error, which will have
// already caused an error message and probably a suggestion
@@ -1252,7 +1260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
path_span,
E0071,
"expected struct, variant or union type, found {}",
- ty.sort_string(self.tcx)
+ ty.normalized.sort_string(self.tcx)
)
.span_label(path_span, "not a struct")
.emit();
@@ -1300,7 +1308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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);
+ self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, init_ty);
}
// Does the expected pattern type originate from an expression and what is the span?
@@ -1315,7 +1323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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);
+ self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty);
if let Some(blk) = decl.els {
let previous_diverges = self.diverges.get();
@@ -1620,14 +1628,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&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 };
+ let err = self.tcx.ty_error();
+ self.write_ty(hir_id, err);
+ self.write_ty(pat.hir_id, err);
+ let local_ty = LocalTy { decl_ty: err, revealed_ty: err };
self.locals.borrow_mut().insert(hir_id, local_ty);
self.locals.borrow_mut().insert(pat.hir_id, local_ty);
}
@@ -1640,20 +1648,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &QPath<'_>,
path_span: Span,
hir_id: hir::HirId,
- ) -> (Res, Ty<'tcx>) {
+ ) -> (Res, RawTy<'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)
+ let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw);
+ let ty = self.astconv().res_to_ty(self_ty, path, true);
+ (path.res, self.handle_raw_ty(path_span, 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 result = self
+ .astconv()
+ .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true);
let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
+ let ty = self.handle_raw_ty(path_span, ty);
let result = result.map(|(_, kind, def_id)| (kind, def_id));
// Write back the new resolution.
@@ -1662,7 +1671,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(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)
+ let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id, id);
+ (res, self.handle_raw_ty(path_span, ty))
}
}
}
@@ -1682,7 +1692,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 remap_cause = FxIndexSet::default();
let mut not_adjusted = vec![];
for error in errors {
@@ -1710,6 +1720,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ // Adjust any other errors that come from other cause codes, when these
+ // errors are of the same predicate as one we successfully adjusted, and
+ // when their spans overlap (suggesting they're due to the same root cause).
+ //
+ // This is because due to normalization, we often register duplicate
+ // obligations with misc obligations that are basically impossible to
+ // line back up with a useful ExprBindingObligation.
for error in not_adjusted {
for (span, predicate, cause) in &remap_cause {
if *predicate == error.obligation.predicate
@@ -1796,7 +1813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir_id: call_hir_id,
span: call_span,
..
- }) = hir.get(hir.get_parent_node(expr.hir_id))
+ }) = hir.get_parent(expr.hir_id)
&& callee.hir_id == expr.hir_id
{
if self.closure_span_overlaps_error(error, *call_span) {
@@ -2111,8 +2128,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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)
+ for (predicate, span) in instantiated
{
if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
&& pred.self_ty().peel_refs() == callee_ty
@@ -2124,7 +2140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
- ty::Opaque(new_def_id, _)
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id: new_def_id, .. })
| ty::Closure(new_def_id, _)
| ty::FnDef(new_def_id, _) => {
def_id = new_def_id;
@@ -2132,19 +2148,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => {
// 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(
+ let trait_ref = self.tcx.mk_trait_ref(
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(),
- ),
+ [
+ callee_ty,
+ self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: rustc_span::DUMMY_SP,
+ }),
+ ],
);
let obligation = traits::Obligation::new(
self.tcx,
@@ -2217,7 +2229,7 @@ fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>)
if arg == param_to_point_at {
return true;
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Projection(..) = ty.kind()
+ && let ty::Alias(ty::Projection, ..) = ty.kind()
{
// This logic may seem a bit strange, but typically when
// we have a projection type in a function signature, the
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 30b59da78..428fde642 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -17,11 +17,10 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
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_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable};
use rustc_session::Session;
use rustc_span::symbol::Ident;
-use rustc_span::{self, Span};
+use rustc_span::{self, Span, DUMMY_SP};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
use std::cell::{Cell, RefCell};
@@ -176,6 +175,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn_sig
})
}),
+ autoderef_steps: Box::new(|ty| {
+ let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors();
+ let mut steps = vec![];
+ while let Some((ty, _)) = autoderef.next() {
+ steps.push((ty, autoderef.current_obligations()));
+ }
+ steps
+ }),
}
}
@@ -287,8 +294,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
poly_trait_ref,
);
- let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
- self,
+ let item_substs = self.astconv().create_substs_for_associated_item(
span,
item_def_id,
item_segment,
@@ -298,11 +304,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
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(span, ty)
+ fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
+ match ty.kind() {
+ ty::Adt(adt_def, _) => Some(*adt_def),
+ // FIXME(#104767): Should we handle bound regions here?
+ ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => {
+ self.normalize(span, ty).ty_adt_def()
+ }
+ _ => None,
}
}
@@ -310,7 +319,21 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
self.infcx.set_tainted_by_errors(e)
}
- fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
+ fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
+ // FIXME: normalization and escaping regions
+ let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
self.write_ty(hir_id, ty)
}
}
+
+/// Represents a user-provided type in the raw form (never normalized).
+///
+/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`,
+/// and the API in this module, which expect `Ty` to be fully normalized.
+#[derive(Clone, Copy, Debug)]
+pub struct RawTy<'tcx> {
+ pub raw: Ty<'tcx>,
+
+ /// The normalized form of `raw`, stored here for efficiency.
+ pub normalized: Ty<'tcx>,
+}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 4f92477b5..4d673ac91 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,33 +1,37 @@
use super::FnCtxt;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
-use rustc_infer::infer;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty};
+use rustc_middle::ty::{
+ self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
+ TypeVisitable,
+};
use rustc_session::errors::ExprParenthesesNeeded;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::source_map::Spanned;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::NormalizeExt;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
self.typeck_results
.borrow()
.liberated_fn_sigs()
- .get(self.tcx.hir().get_parent_node(self.body_id))
+ .get(self.tcx.hir().parent_id(self.body_id))
.copied()
}
@@ -89,7 +93,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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)
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found)
else { return false; };
if can_satisfy(output) {
let (sugg_call, mut applicability) = match inputs.len() {
@@ -158,99 +162,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 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>,
+ ty: 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::Clause(ty::Clause::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::Clause(ty::Clause::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.at(&self.misc(expr.span), self.param_env).normalize(output);
-
- if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
}
pub fn suggest_two_fn_call(
@@ -262,9 +176,9 @@ impl<'a, 'tcx> FnCtxt<'a, '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)
+ let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty)
else { return false; };
- let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+ let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty)
else { return false; };
if can_satisfy(lhs_output_ty, rhs_output_ty) {
@@ -317,11 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- err.multipart_suggestion_verbose(
- format!("use parentheses to call these"),
- sugg,
- applicability,
- );
+ err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability);
true
} else {
@@ -329,6 +239,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ pub fn suggest_remove_last_method_call(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ ) -> bool {
+ if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) = expr.kind &&
+ let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr) &&
+ self.can_coerce(recv_ty, expected) {
+ let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) {
+ expr.span.with_lo(recv_span.hi())
+ } else {
+ expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1))
+ };
+ err.span_suggestion_verbose(
+ span,
+ "try removing the method call",
+ "",
+ Applicability::MachineApplicable,
+ );
+ return true;
+ }
+ false
+ }
+
pub fn suggest_deref_ref_or_into(
&self,
err: &mut Diagnostic,
@@ -391,10 +326,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
return true;
} 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)
+ && 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"));
+ let name = self.tcx.item_name(def_id);
+ let kind = self.tcx.def_kind(def_id);
+ if let DefKind::Ctor(of, CtorKind::Fn) = kind {
+ err.span_label(sp, format!("`{name}` defines {} constructor here, which should be called", match of {
+ CtorOf::Struct => "a struct",
+ CtorOf::Variant => "an enum variant",
+ }));
+ } else {
+ let descr = kind.descr(def_id);
+ err.span_label(sp, format!("{descr} `{name}` defined here"));
+ }
return true;
} else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
return true;
@@ -416,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& 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,
+ // to an owned type (`Vec` or `String`). These conversions clone internally,
// so we remove the user's `clone` call.
{
vec![(
@@ -613,10 +558,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
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
+ // 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 parent = self.tcx.hir().parent_id(expr.hir_id);
let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else {
return false;
};
@@ -752,12 +697,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return true
}
}
- &hir::FnRetTy::Return(ref ty) => {
+ hir::FnRetTy::Return(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);
+ let ty = self.astconv().ast_ty_to_ty(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);
@@ -828,7 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
..
}) => {
// 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);
+ let ty = self.astconv().ast_ty_to_ty(bounded_ty);
Some((ty, bounds))
}
_ => None,
@@ -866,7 +811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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);
+ let ty = self.astconv().ast_ty_to_ty( param);
matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
});
@@ -920,7 +865,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if let hir::FnRetTy::Return(ty) = fn_decl.output {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
+ let ty = self.astconv().ast_ty_to_ty(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 = match self.tcx.asyncness(fn_id.owner) {
@@ -948,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
) -> bool {
- let sp = self.tcx.sess.source_map().start_point(expr.span);
+ let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
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 }`
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
@@ -988,6 +933,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ pub(crate) fn suggest_clone_for_ref(
+ &self,
+ diag: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expr_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
+ && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
+ && expected_ty == *inner_ty
+ && self
+ .infcx
+ .type_implements_trait(
+ clone_trait_def,
+ [self.tcx.erase_regions(expected_ty)],
+ self.param_env
+ )
+ .must_apply_modulo_regions()
+ {
+ diag.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ "consider using clone here",
+ ".clone()",
+ Applicability::MachineApplicable,
+ );
+ return true;
+ }
+ false
+ }
+
pub(crate) fn suggest_copied_or_cloned(
&self,
diag: &mut Diagnostic,
@@ -1126,9 +1101,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let hir = self.tcx.hir();
- let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| {
- matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
- }).next();
+ let cond_parent = hir.parent_iter(expr.hir_id).find(|(_, node)| {
+ !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
+ });
// Don't suggest:
// `let Some(_) = a.is_some() && b`
// ++++++++++
@@ -1234,10 +1209,114 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
true
}
+ ExprKind::Lit(Spanned {
+ node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
+ span,
+ }) => {
+ let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; };
+ if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
+ return false;
+ }
+ if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
+ return false;
+ }
+ let (_, suffix) = snippet.split_at(snippet.len() - 3);
+ let value = match suffix {
+ "f32" => (lit - 0xf32) / (16 * 16 * 16),
+ "f64" => (lit - 0xf64) / (16 * 16 * 16),
+ _ => return false,
+ };
+ err.span_suggestions(
+ expr.span,
+ "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
+ [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
+ Applicability::MaybeIncorrect,
+ );
+ true
+ }
_ => false,
}
}
+ pub(crate) fn suggest_associated_const(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ let Some((DefKind::AssocFn, old_def_id)) = self.typeck_results.borrow().type_dependent_def(expr.hir_id) else {
+ return false;
+ };
+ let old_item_name = self.tcx.item_name(old_def_id);
+ let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
+ if old_item_name == capitalized_name {
+ return false;
+ }
+ let (item, segment) = match expr.kind {
+ hir::ExprKind::Path(QPath::Resolved(
+ Some(ty),
+ hir::Path { segments: [segment], .. },
+ ))
+ | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
+ let self_ty = self.astconv().ast_ty_to_ty(ty);
+ if let Ok(pick) = self.probe_for_name(
+ Mode::Path,
+ Ident::new(capitalized_name, segment.ident.span),
+ Some(expected_ty),
+ IsSuggestion(true),
+ self_ty,
+ expr.hir_id,
+ ProbeScope::TraitsInScope,
+ ) {
+ (pick.item, segment)
+ } else {
+ return false;
+ }
+ }
+ hir::ExprKind::Path(QPath::Resolved(
+ None,
+ hir::Path { segments: [.., segment], .. },
+ )) => {
+ // we resolved through some path that doesn't end in the item name,
+ // better not do a bad suggestion by accident.
+ if old_item_name != segment.ident.name {
+ return false;
+ }
+ if let Some(item) = self
+ .tcx
+ .associated_items(self.tcx.parent(old_def_id))
+ .filter_by_name_unhygienic(capitalized_name)
+ .next()
+ {
+ (*item, segment)
+ } else {
+ return false;
+ }
+ }
+ _ => return false,
+ };
+ if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
+ // Same item
+ return false;
+ }
+ let item_ty = self.tcx.type_of(item.def_id);
+ // FIXME(compiler-errors): This check is *so* rudimentary
+ if item_ty.needs_subst() {
+ return false;
+ }
+ if self.can_coerce(item_ty, expected_ty) {
+ err.span_suggestion_verbose(
+ segment.ident.span,
+ format!("try referring to the associated const `{capitalized_name}` instead",),
+ capitalized_name,
+ Applicability::MachineApplicable,
+ );
+ true
+ } else {
+ false
+ }
+ }
+
fn is_loop(&self, id: hir::HirId) -> bool {
let node = self.tcx.hir().get(id);
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
@@ -1276,18 +1355,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& !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)
+ && let trait_ref = ty::Binder::dummy(self.tcx.mk_trait_ref(clone_trait_did, [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),
- })
+ && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
+ self.tcx,
+ traits::ObligationCause::dummy(),
+ self.param_env,
+ trait_ref,
+ ))
{
diag.span_note(
callee_expr.span,
@@ -1295,6 +1370,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
),
);
+ let owner = self.tcx.hir().enclosing_body_owner(expr.hir_id);
+ if let ty::Param(param) = expected_ty.kind()
+ && let Some(generics) = self.tcx.hir().get_generics(owner)
+ {
+ suggest_constraining_type_params(
+ self.tcx,
+ generics,
+ diag,
+ vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
+ );
+ } else {
+ self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
+ }
}
}
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 9a096f24f..15dd3412c 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -77,7 +77,8 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
Some(ref ty) => {
let o_ty = self.fcx.to_ty(&ty);
- let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty));
+ let c_ty =
+ self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw));
debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
self.fcx
.typeck_results
@@ -85,7 +86,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
.user_provided_types_mut()
.insert(ty.hir_id, c_ty);
- Some(LocalTy { decl_ty: o_ty, revealed_ty: o_ty })
+ Some(LocalTy { decl_ty: o_ty.normalized, revealed_ty: o_ty.normalized })
}
None => None,
};
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
index fd8ea1ad7..b3dd3031d 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
@@ -233,6 +233,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
self.tcx()
.sess
.delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`"));
+ return;
}
let ty = self.tcx().erase_regions(ty);
let m = self.tcx().parent_module(expr.hir_id).to_def_id();
@@ -303,8 +304,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
let mut reinit = None;
match expr.kind {
ExprKind::Assign(lhs, rhs, _) => {
- self.visit_expr(lhs);
self.visit_expr(rhs);
+ self.visit_expr(lhs);
reinit = Some(lhs);
}
@@ -432,7 +433,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
self.drop_ranges.add_control_edge(self.expr_index, *target)
}),
- ExprKind::Break(destination, ..) => {
+ ExprKind::Break(destination, value) => {
// destination either points to an expression or to a block. We use
// find_target_expression_from_destination to use the last expression of the block
// if destination points to a block.
@@ -442,7 +443,11 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
// will refer to the end of the block due to the post order traversal.
self.find_target_expression_from_destination(destination).map_or((), |target| {
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
- })
+ });
+
+ if let Some(value) = value {
+ self.visit_expr(value);
+ }
}
ExprKind::Call(f, args) => {
@@ -464,6 +469,12 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
ExprKind::AddrOf(..)
| ExprKind::Array(..)
+ // FIXME(eholk): We probably need special handling for AssignOps. The ScopeTree builder
+ // in region.rs runs both lhs then rhs and rhs then lhs and then sets all yields to be
+ // the latest they show up in either traversal. With the older scope-based
+ // approximation, this was fine, but it's probably not right now. What we probably want
+ // to do instead is still run both orders, but consider anything that showed up as a
+ // yield in either order.
| ExprKind::AssignOp(..)
| ExprKind::Binary(..)
| ExprKind::Block(..)
@@ -501,6 +512,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
// Increment expr_count here to match what InteriorVisitor expects.
self.expr_index = self.expr_index + 1;
+
+ // Save a node mapping to get better CFG visualization
+ self.drop_ranges.add_node_mapping(pat.hir_id, self.expr_index);
}
}
@@ -520,7 +534,7 @@ impl DropRangesBuilder {
}
});
}
- debug!("hir_id_map: {:?}", tracked_value_map);
+ debug!("hir_id_map: {:#?}", tracked_value_map);
let num_values = tracked_value_map.len();
Self {
tracked_value_map,
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs
index c0a0bfe8e..e8d31be79 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs
@@ -2,6 +2,7 @@
//! flow graph when needed for debugging.
use rustc_graphviz as dot;
+use rustc_hir::{Expr, ExprKind, Node};
use rustc_middle::ty::TyCtxt;
use super::{DropRangesBuilder, PostOrderId};
@@ -80,10 +81,14 @@ impl<'a> dot::Labeller<'a> for DropRangesGraph<'_, '_> {
.post_order_map
.iter()
.find(|(_hir_id, &post_order_id)| post_order_id == *n)
- .map_or("<unknown>".into(), |(hir_id, _)| self
- .tcx
- .hir()
- .node_to_string(*hir_id))
+ .map_or("<unknown>".into(), |(hir_id, _)| format!(
+ "{}{}",
+ self.tcx.hir().node_to_string(*hir_id),
+ match self.tcx.hir().find(*hir_id) {
+ Some(Node::Expr(Expr { kind: ExprKind::Yield(..), .. })) => " (yield)",
+ _ => "",
+ }
+ ))
)
.into(),
)
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs
index 2abcadcc9..f7b493bc2 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs
@@ -79,7 +79,7 @@ pub fn compute_drop_ranges<'a, 'tcx>(
/// result of `foo`. On the other hand, if `place` points to `x` then `f` will
/// be called both on the `ExprKind::Path` node that represents the expression
/// as well as the HirId of the local `x` itself.
-fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
+fn for_each_consumable(hir: Map<'_>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
f(place);
let node = hir.find(place.hir_id());
if let Some(Node::Expr(expr)) = node {
@@ -96,15 +96,13 @@ fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl Fn
}
rustc_index::newtype_index! {
- pub struct PostOrderId {
- DEBUG_FORMAT = "id({})",
- }
+ #[debug_format = "id({})"]
+ pub struct PostOrderId {}
}
rustc_index::newtype_index! {
- pub struct TrackedValueIndex {
- DEBUG_FORMAT = "hidx({})",
- }
+ #[debug_format = "hidx({})"]
+ pub struct TrackedValueIndex {}
}
/// Identifies a value whose drop state we need to track.
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
index bfe95852a..ed3d89031 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
@@ -116,7 +116,7 @@ impl<'tcx> ExprUseDelegate<'tcx> {
// where the `identity(...)` (the rvalue) produces a return type
// of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
// `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
- // `a` will be considered borrowed). Other parts of the code will ensure
+ // `a` will be considered borrowed). Other parts of the code will ensure
// that if `y` is live over a yield, `&'y mut A` appears in the generator
// state. If `'y` is live, then any sound region analysis must conclude
// that `'a` is also live. So if this causes a bug, blame some other
@@ -140,7 +140,7 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
diag_expr_id: HirId,
) {
let hir = self.tcx.hir();
- let parent = match hir.find_parent_node(place_with_id.hir_id) {
+ let parent = match hir.opt_parent_id(place_with_id.hir_id) {
Some(parent) => parent,
None => place_with_id.hir_id,
};
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index 3b1518ff7..7af526053 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -71,10 +71,8 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
yield_data.expr_and_pat_count, self.expr_count, source_span
);
- if self.fcx.sess().opts.unstable_opts.drop_tracking
- && self
- .drop_ranges
- .is_dropped_at(hir_id, yield_data.expr_and_pat_count)
+ if self
+ .is_dropped_at_yield_location(hir_id, yield_data.expr_and_pat_count)
{
debug!("value is dropped at yield point; not recording");
return false;
@@ -173,6 +171,18 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
}
}
}
+
+ /// If drop tracking is enabled, consult drop_ranges to see if a value is
+ /// known to be dropped at a yield point and therefore can be omitted from
+ /// the generator witness.
+ fn is_dropped_at_yield_location(&self, value_hir_id: HirId, yield_location: usize) -> bool {
+ // short-circuit if drop tracking is not enabled.
+ if !self.fcx.sess().opts.unstable_opts.drop_tracking {
+ return false;
+ }
+
+ self.drop_ranges.is_dropped_at(value_hir_id, yield_location)
+ }
}
pub fn resolve_interior<'a, 'tcx>(
@@ -448,7 +458,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
// the yield, even if it's not borrowed or referenced after the yield. Ideally this would
// *only* happen for types with observable drop, not all types which wrap them, but that
// doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in
- // src/test/ui/generator/drop-tracking-parent-expression.rs.
+ // tests/ui/generator/drop-tracking-parent-expression.rs.
let scope = if self.drop_ranges.is_borrowed_temporary(expr)
|| ty.map_or(true, |ty| {
// Avoid ICEs in needs_drop.
@@ -563,7 +573,7 @@ fn check_must_not_suspend_ty<'tcx>(
}
ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data),
// FIXME: support adding the attribute to TAITs
- ty::Opaque(def, _) => {
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
let mut has_emitted = false;
for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) {
// We only look at the `DefId`, so it is safe to skip the binder here.
@@ -607,10 +617,7 @@ fn check_must_not_suspend_ty<'tcx>(
ty::Tuple(fields) => {
let mut has_emitted = false;
let comps = match data.expr.map(|e| &e.kind) {
- Some(hir::ExprKind::Tup(comps)) => {
- debug_assert_eq!(comps.len(), fields.len());
- Some(comps)
- }
+ Some(hir::ExprKind::Tup(comps)) if comps.len() == fields.len() => Some(comps),
_ => None,
};
for (i, ty) in fields.iter().enumerate() {
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index c2dc14024..3c873024c 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -105,6 +105,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
.note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
+ let mut should_delay_as_bug = false;
+ if let Err(LayoutError::Unknown(bad_from)) = sk_from && bad_from.references_error() {
+ should_delay_as_bug = true;
+ }
+ if let Err(LayoutError::Unknown(bad_to)) = sk_to && bad_to.references_error() {
+ should_delay_as_bug = true;
+ }
+ if should_delay_as_bug {
+ err.delay_as_bug();
+ }
}
err.emit();
}
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 5b2352cda..7ddf9eaa4 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -205,7 +205,7 @@ fn typeck_with_fallback<'tcx>(
if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
- <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
+ fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
} else {
tcx.fn_sig(def_id)
};
@@ -220,11 +220,11 @@ fn typeck_with_fallback<'tcx>(
} else {
let expected_type = body_ty
.and_then(|ty| match ty.kind {
- hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)),
+ hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)),
_ => None,
})
.unwrap_or_else(|| match tcx.hir().get(id) {
- Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) {
+ Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) {
Node::Expr(&hir::Expr {
kind: hir::ExprKind::ConstBlock(ref anon_const),
..
@@ -240,10 +240,8 @@ fn typeck_with_fallback<'tcx>(
}),
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
| Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
- let operand_ty = asm
- .operands
- .iter()
- .filter_map(|(op, _op_sp)| match op {
+ let operand_ty =
+ asm.operands.iter().find_map(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const }
if anon_const.hir_id == id =>
{
@@ -259,8 +257,7 @@ fn typeck_with_fallback<'tcx>(
}))
}
_ => None,
- })
- .next();
+ });
operand_ty.unwrap_or_else(fallback)
}
_ => fallback(),
@@ -300,7 +297,7 @@ fn typeck_with_fallback<'tcx>(
fcx.resolve_generator_interiors(def_id.to_def_id());
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
- let ty = fcx.normalize_ty(span, ty);
+ let ty = fcx.normalize(span, ty);
fcx.require_type_is_sized(ty, span, code);
}
@@ -462,8 +459,8 @@ fn fatally_break_rust(sess: &Session) {
));
}
-fn has_expected_num_generic_args<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn has_expected_num_generic_args(
+ tcx: TyCtxt<'_>,
trait_did: Option<DefId>,
expected: usize,
) -> bool {
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index 0b5dc946c..48c75cde9 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -736,7 +736,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => {
- // box p1, &p1, &mut p1. we can ignore the mutability of
+ // box p1, &p1, &mut p1. we can ignore the mutability of
// PatKind::Ref since that information is already contained
// in the type.
let subplace = self.cat_deref(pat, place_with_id)?;
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 03d0e7926..372ea30eb 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -4,6 +4,9 @@ use crate::{callee, FnCtxt};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
+use rustc_hir_analysis::astconv::generics::{
+ check_generic_arg_count_for_call, create_substs_for_generic_args,
+};
use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
use rustc_infer::infer::{self, InferOk};
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
@@ -12,10 +15,10 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{self, SubstsRef};
use rustc_middle::ty::{self, GenericParamDefKind, Ty};
-use rustc_span::Span;
+use rustc_middle::ty::{InternalSubsts, UserSubsts, UserType};
+use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits;
-use std::iter;
use std::ops::Deref;
struct ConfirmContext<'a, 'tcx> {
@@ -45,7 +48,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
unadjusted_self_ty: Ty<'tcx>,
- pick: probe::Pick<'tcx>,
+ pick: &probe::Pick<'tcx>,
segment: &hir::PathSegment<'_>,
) -> ConfirmResult<'tcx> {
debug!(
@@ -71,7 +74,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
fn confirm(
&mut self,
unadjusted_self_ty: Ty<'tcx>,
- pick: probe::Pick<'tcx>,
+ pick: &probe::Pick<'tcx>,
segment: &hir::PathSegment<'_>,
) -> ConfirmResult<'tcx> {
// Adjust the self expression the user provided and obtain the adjusted type.
@@ -89,7 +92,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
// something which derefs to `Self` actually implements the trait and the caller
// wanted to make a static dispatch on it but forgot to import the trait.
- // See test `src/test/ui/issue-35976.rs`.
+ // See test `tests/ui/issue-35976.rs`.
//
// In that case, we'll error anyway, but we'll also re-run the search with all traits
// in scope, and if we find another method which can be used, we'll output an
@@ -97,7 +100,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
let filler_substs = rcvr_substs
.extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def));
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
- &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
+ self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
);
// Unify the (adjusted) self type with what the method expects.
@@ -330,7 +333,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// variables.
let generics = self.tcx.generics_of(pick.item.def_id);
- let arg_count_correct = <dyn AstConv<'_>>::check_generic_arg_count_for_call(
+ let arg_count_correct = check_generic_arg_count_for_call(
self.tcx,
self.span,
pick.item.def_id,
@@ -368,11 +371,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
) -> subst::GenericArg<'tcx> {
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
- <dyn AstConv<'_>>::ast_region_to_region(self.cfcx.fcx, lt, Some(param))
- .into()
+ self.cfcx.fcx.astconv().ast_region_to_region(lt, Some(param)).into()
}
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
- self.cfcx.to_ty(ty).into()
+ self.cfcx.to_ty(ty).raw.into()
}
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
@@ -397,7 +399,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
self.cfcx.var_for_def(self.cfcx.span, param)
}
}
- <dyn AstConv<'_>>::create_substs_for_generic_args(
+
+ let substs = create_substs_for_generic_args(
self.tcx,
pick.item.def_id,
parent_substs,
@@ -405,7 +408,47 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
None,
&arg_count_correct,
&mut MethodSubstsCtxt { cfcx: self, pick, seg },
- )
+ );
+
+ // When the method is confirmed, the `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 !substs.is_empty() && !generics.params.is_empty() {
+ let user_type_annotation = self.probe(|_| {
+ let user_substs = UserSubsts {
+ substs: InternalSubsts::for_item(self.tcx, pick.item.def_id, |param, _| {
+ let i = param.index as usize;
+ if i < generics.parent_count {
+ self.fcx.var_for_def(DUMMY_SP, param)
+ } else {
+ substs[i]
+ }
+ }),
+ user_self_ty: None, // not relevant here
+ };
+
+ self.fcx.canonicalize_user_type_annotation(UserType::TypeOf(
+ pick.item.def_id,
+ user_substs,
+ ))
+ });
+
+ debug!("instantiate_method_substs: user_type_annotation={:?}", user_type_annotation);
+ self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
+ }
+
+ self.normalize(self.span, substs)
}
fn unify_receivers(
@@ -521,7 +564,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
fn predicates_require_illegal_sized_bound(
&self,
- predicates: &ty::InstantiatedPredicates<'tcx>,
+ predicates: ty::InstantiatedPredicates<'tcx>,
) -> Option<Span> {
let sized_def_id = self.tcx.lang_items().sized_trait()?;
@@ -531,10 +574,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
if trait_pred.def_id() == sized_def_id =>
{
- let span = iter::zip(&predicates.predicates, &predicates.spans)
+ let span = predicates
+ .iter()
.find_map(
|(p, span)| {
- if *p == obligation.predicate { Some(*span) } else { None }
+ if p == obligation.predicate { Some(span) } else { None }
},
)
.unwrap_or(rustc_span::DUMMY_SP);
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index a2ca5c3b7..b810a967a 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -11,7 +11,7 @@ pub use self::suggest::SelfSource;
pub use self::MethodError::*;
use crate::errors::OpMethodGenericParams;
-use crate::{Expectation, FnCtxt};
+use crate::FnCtxt;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
@@ -57,7 +57,12 @@ pub enum MethodError<'tcx> {
PrivateMatch(DefKind, DefId, Vec<DefId>),
// Found a `Self: Sized` bound where `Self` is a trait object.
- IllegalSizedBound(Vec<DefId>, bool, Span),
+ IllegalSizedBound {
+ candidates: Vec<DefId>,
+ needs_mut: bool,
+ bound_span: Span,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ },
// Found a match, but the return type is wrong
BadReturnType,
@@ -92,10 +97,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_ty: Ty<'tcx>,
call_expr_id: hir::HirId,
allow_private: bool,
+ return_type: Option<Ty<'tcx>>,
) -> bool {
match self.probe_for_name(
probe::Mode::MethodCall,
method_name,
+ return_type,
IsSuggestion(false),
self_ty,
call_expr_id,
@@ -112,8 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(NoMatch(..)) => false,
Err(Ambiguity(..)) => true,
Err(PrivateMatch(..)) => allow_private,
- Err(IllegalSizedBound(..)) => true,
- Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
+ Err(IllegalSizedBound { .. }) => true,
+ Err(BadReturnType) => false,
}
}
@@ -125,17 +132,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
msg: &str,
method_name: Ident,
self_ty: Ty<'tcx>,
- call_expr: &hir::Expr<'_>,
+ call_expr: &hir::Expr<'tcx>,
span: Option<Span>,
) {
let params = self
- .probe_for_name(
- probe::Mode::MethodCall,
+ .lookup_probe_for_diagnostic(
method_name,
- IsSuggestion(true),
self_ty,
- call_expr.hir_id,
+ call_expr,
ProbeScope::TraitsInScope,
+ None,
)
.map(|pick| {
let sig = self.tcx.fn_sig(pick.item.def_id);
@@ -192,8 +198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
- let result =
- self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment);
+ let result = self.confirm_method(span, self_expr, call_expr, self_ty, &pick, segment);
debug!("result = {:?}", result);
if let Some(span) = result.illegal_sized_bound {
@@ -210,34 +215,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ProbeScope::TraitsInScope,
) {
Ok(ref new_pick) if pick.differs_from(new_pick) => {
- needs_mut = true;
+ needs_mut = new_pick.self_ty.ref_mutability() != self_ty.ref_mutability();
}
_ => {}
}
}
// We probe again, taking all traits into account (not only those in scope).
- let candidates =
- match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) {
- // If we find a different result the caller probably forgot to import a trait.
- Ok(ref new_pick) if pick.differs_from(new_pick) => {
- vec![new_pick.item.container_id(self.tcx)]
- }
- Err(Ambiguity(ref sources)) => sources
- .iter()
- .filter_map(|source| {
- match *source {
- // Note: this cannot come from an inherent impl,
- // because the first probing succeeded.
- CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
- CandidateSource::Trait(_) => None,
- }
- })
- .collect(),
- _ => Vec::new(),
- };
-
- return Err(IllegalSizedBound(candidates, needs_mut, span));
+ let candidates = match self.lookup_probe_for_diagnostic(
+ segment.ident,
+ self_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ None,
+ ) {
+ // If we find a different result the caller probably forgot to import a trait.
+ Ok(ref new_pick) if pick.differs_from(new_pick) => {
+ vec![new_pick.item.container_id(self.tcx)]
+ }
+ Err(Ambiguity(ref sources)) => sources
+ .iter()
+ .filter_map(|source| {
+ match *source {
+ // Note: this cannot come from an inherent impl,
+ // because the first probing succeeded.
+ CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
+ CandidateSource::Trait(_) => None,
+ }
+ })
+ .collect(),
+ _ => Vec::new(),
+ };
+
+ return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
}
Ok(result.callee)
@@ -248,12 +258,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
method_name: Ident,
self_ty: Ty<'tcx>,
- call_expr: &'tcx hir::Expr<'tcx>,
+ call_expr: &hir::Expr<'_>,
scope: ProbeScope,
) -> probe::PickResult<'tcx> {
let pick = self.probe_for_name(
probe::Mode::MethodCall,
method_name,
+ None,
IsSuggestion(false),
self_ty,
call_expr.hir_id,
@@ -263,53 +274,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(pick)
}
- pub(super) fn obligation_for_method(
+ pub fn lookup_probe_for_diagnostic(
&self,
- span: Span,
- trait_def_id: DefId,
+ method_name: Ident,
self_ty: Ty<'tcx>,
- opt_input_types: Option<&[Ty<'tcx>]>,
- ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
- {
- // Construct a trait-reference `self_ty : Trait<input_tys>`
- let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
- match param.kind {
- GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
- GenericParamDefKind::Type { .. } => {
- if param.index == 0 {
- return self_ty.into();
- } else if let Some(input_types) = opt_input_types {
- return input_types[param.index as usize - 1].into();
- }
- }
- }
- self.var_for_def(span, param)
- });
-
- let trait_ref = ty::TraitRef::new(trait_def_id, substs);
-
- // Construct an obligation
- let poly_trait_ref = ty::Binder::dummy(trait_ref);
- (
- traits::Obligation::misc(
- self.tcx,
- span,
- self.body_id,
- self.param_env,
- poly_trait_ref.without_const(),
- ),
- substs,
- )
+ call_expr: &hir::Expr<'_>,
+ scope: ProbeScope,
+ return_type: Option<Ty<'tcx>>,
+ ) -> probe::PickResult<'tcx> {
+ let pick = self.probe_for_name(
+ probe::Mode::MethodCall,
+ method_name,
+ return_type,
+ IsSuggestion(true),
+ self_ty,
+ call_expr.hir_id,
+ scope,
+ )?;
+ Ok(pick)
}
- pub(super) fn obligation_for_op_method(
+ pub(super) fn obligation_for_method(
&self,
- span: Span,
+ cause: ObligationCause<'tcx>,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
- opt_input_type: Option<Ty<'tcx>>,
- opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
- expected: Expectation<'tcx>,
+ opt_input_types: Option<&[Ty<'tcx>]>,
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
{
// Construct a trait-reference `self_ty : Trait<input_tys>`
@@ -319,35 +309,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
GenericParamDefKind::Type { .. } => {
if param.index == 0 {
return self_ty.into();
- } else if let Some(input_type) = opt_input_type {
- return input_type.into();
+ } else if let Some(input_types) = opt_input_types {
+ return input_types[param.index as usize - 1].into();
}
}
}
- self.var_for_def(span, param)
+ self.var_for_def(cause.span, param)
});
- let trait_ref = ty::TraitRef::new(trait_def_id, substs);
+ let trait_ref = self.tcx.mk_trait_ref(trait_def_id, substs);
// Construct an obligation
let poly_trait_ref = ty::Binder::dummy(trait_ref);
- let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
-
(
traits::Obligation::new(
self.tcx,
- traits::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, hir::ExprKind::Lit(_))),
- output_ty,
- },
- ),
+ cause,
self.param_env,
- poly_trait_ref,
+ poly_trait_ref.without_const(),
),
substs,
)
@@ -358,55 +337,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// In particular, it doesn't really do any probing: it simply constructs
/// an obligation for a particular trait with the given self type and checks
/// whether that trait is implemented.
- #[instrument(level = "debug", skip(self, span))]
+ #[instrument(level = "debug", skip(self))]
pub(super) fn lookup_method_in_trait(
&self,
- span: Span,
+ cause: ObligationCause<'tcx>,
m_name: Ident,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
let (obligation, substs) =
- self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
- self.construct_obligation_for_trait(
- span,
- m_name,
- trait_def_id,
- obligation,
- substs,
- None,
- false,
- )
- }
-
- pub(super) fn lookup_op_method_in_trait(
- &self,
- span: Span,
- m_name: Ident,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- opt_input_type: Option<Ty<'tcx>>,
- opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
- expected: Expectation<'tcx>,
- ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- let (obligation, substs) = self.obligation_for_op_method(
- span,
- trait_def_id,
- self_ty,
- opt_input_type,
- opt_input_expr,
- expected,
- );
- self.construct_obligation_for_trait(
- span,
- m_name,
- trait_def_id,
- obligation,
- substs,
- opt_input_expr,
- true,
- )
+ self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types);
+ self.construct_obligation_for_trait(m_name, trait_def_id, obligation, substs)
}
// FIXME(#18741): it seems likely that we can consolidate some of this
@@ -414,13 +356,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// of this method is basically the same as confirmation.
fn construct_obligation_for_trait(
&self,
- span: Span,
m_name: Ident,
trait_def_id: DefId,
obligation: traits::PredicateObligation<'tcx>,
substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
- opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
- is_op: bool,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!(?obligation);
@@ -436,7 +375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
tcx.sess.delay_span_bug(
- span,
+ obligation.cause.span,
"operator trait does not have corresponding operator method",
);
return None;
@@ -457,29 +396,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Instantiate late-bound regions and substitute the trait
// parameters into the method type to get the actual method type.
//
- // N.B., instantiate late-bound regions first so that
- // `instantiate_type_scheme` can normalize associated types that
- // may reference those regions.
+ // N.B., instantiate late-bound regions before normalizing the
+ // function signature so that normalization does not need to deal
+ // with bound regions.
let fn_sig = tcx.bound_fn_sig(def_id);
let fn_sig = fn_sig.subst(self.tcx, substs);
- let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);
-
- let cause = if is_op {
- 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, hir::ExprKind::Lit(_))),
- output_ty: None,
- },
- )
- } else {
- traits::ObligationCause::misc(span, self.body_id)
- };
+ let fn_sig =
+ self.replace_bound_vars_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig);
- let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig);
+ let InferOk { value, obligations: o } =
+ self.at(&obligation.cause, self.param_env).normalize(fn_sig);
let fn_sig = {
obligations.extend(o);
value
@@ -487,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
- // so this also effectively registers `obligation` as well. (We
+ // so this also effectively registers `obligation` as well. (We
// used to register `obligation` explicitly, but that resulted in
// double error messages being reported.)
//
@@ -495,7 +421,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// any late-bound regions appearing in its bounds.
let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
- let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(bounds);
+ let InferOk { value, obligations: o } =
+ self.at(&obligation.cause, self.param_env).normalize(bounds);
let bounds = {
obligations.extend(o);
value
@@ -503,7 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
assert!(!bounds.has_escaping_bound_vars());
- let predicates_cause = cause.clone();
+ let predicates_cause = obligation.cause.clone();
obligations.extend(traits::predicates_for_generics(
move |_, _| predicates_cause.clone(),
self.param_env,
@@ -518,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
obligations.push(traits::Obligation::new(
tcx,
- cause,
+ obligation.cause,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())),
));
@@ -584,6 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let pick = self.probe_for_name(
probe::Mode::Path,
method_name,
+ None,
IsSuggestion(false),
self_ty,
expr_id,
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
index dea14dd93..3d6c2119b 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -341,8 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Find an identifier with which this trait was imported (note that `_` doesn't count).
let any_id = import_items
.iter()
- .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
- .next();
+ .find_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None });
if let Some(any_id) = any_id {
if any_id.name == Empty {
// Glob import, so just use its name.
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index ae299cc9d..a24814313 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -9,6 +9,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
+use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -29,7 +30,6 @@ use rustc_span::lev_distance::{
};
use rustc_span::symbol::sym;
use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::autoderef::{self, Autoderef};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy;
use rustc_trait_selection::traits::query::method_autoderef::{
@@ -38,9 +38,9 @@ use rustc_trait_selection::traits::query::method_autoderef::{
use rustc_trait_selection::traits::query::CanonicalTyGoal;
use rustc_trait_selection::traits::NormalizeExt;
use rustc_trait_selection::traits::{self, ObligationCause};
+use std::cell::RefCell;
use std::cmp::max;
use std::iter;
-use std::mem;
use std::ops::Deref;
use smallvec::{smallvec, SmallVec};
@@ -62,28 +62,29 @@ struct ProbeContext<'a, 'tcx> {
/// This is the OriginalQueryValues for the steps queries
/// that are answered in steps.
- orig_steps_var_values: OriginalQueryValues<'tcx>,
+ orig_steps_var_values: &'a OriginalQueryValues<'tcx>,
steps: &'tcx [CandidateStep<'tcx>],
inherent_candidates: Vec<Candidate<'tcx>>,
extension_candidates: Vec<Candidate<'tcx>>,
impl_dups: FxHashSet<DefId>,
- /// Collects near misses when the candidate functions are missing a `self` keyword and is only
- /// used for error reporting
- static_candidates: Vec<CandidateSource>,
-
/// When probing for names, include names that are close to the
- /// requested name (by Levensthein distance)
+ /// requested name (by Levenshtein distance)
allow_similar_names: bool,
/// Some(candidate) if there is a private candidate
private_candidate: Option<(DefKind, DefId)>,
+ /// Collects near misses when the candidate functions are missing a `self` keyword and is only
+ /// used for error reporting
+ static_candidates: RefCell<Vec<CandidateSource>>,
+
/// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
/// for error reporting
- unsatisfied_predicates:
+ unsatisfied_predicates: RefCell<
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
+ >,
scope_expr_id: hir::HirId,
}
@@ -96,7 +97,7 @@ impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> {
}
#[derive(Debug, Clone)]
-struct Candidate<'tcx> {
+pub(crate) struct Candidate<'tcx> {
// Candidates are (I'm not quite sure, but they are mostly) basically
// some metadata on top of a `ty::AssocItem` (without substs).
//
@@ -130,13 +131,13 @@ struct Candidate<'tcx> {
// if `T: Sized`.
xform_self_ty: Ty<'tcx>,
xform_ret_ty: Option<Ty<'tcx>>,
- item: ty::AssocItem,
- kind: CandidateKind<'tcx>,
- import_ids: SmallVec<[LocalDefId; 1]>,
+ pub(crate) item: ty::AssocItem,
+ pub(crate) kind: CandidateKind<'tcx>,
+ pub(crate) import_ids: SmallVec<[LocalDefId; 1]>,
}
#[derive(Debug, Clone)]
-enum CandidateKind<'tcx> {
+pub(crate) enum CandidateKind<'tcx> {
InherentImplCandidate(
SubstsRef<'tcx>,
// Normalize obligations
@@ -231,7 +232,7 @@ pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError<'tcx>>;
pub enum Mode {
// An expression of the form `receiver.method_name(...)`.
// Autoderefs are performed on `receiver`, lookup is done based on the
- // `self` argument of the method, and static methods aren't considered.
+ // `self` argument of the method, and static methods aren't considered.
MethodCall,
// An expression of the form `Type::item` or `<T>::item`.
// No autoderefs are performed, lookup is done based on the type each
@@ -303,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
mode: Mode,
item_name: Ident,
+ return_type: Option<Ty<'tcx>>,
is_suggestion: IsSuggestion,
self_ty: Ty<'tcx>,
scope_expr_id: hir::HirId,
@@ -312,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name.span,
mode,
Some(item_name),
- None,
+ return_type,
is_suggestion,
self_ty,
scope_expr_id,
@@ -321,6 +323,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}
+ #[instrument(level = "debug", skip(self))]
+ pub(crate) fn probe_for_name_many(
+ &self,
+ mode: Mode,
+ item_name: Ident,
+ return_type: Option<Ty<'tcx>>,
+ is_suggestion: IsSuggestion,
+ self_ty: Ty<'tcx>,
+ scope_expr_id: hir::HirId,
+ scope: ProbeScope,
+ ) -> Vec<Candidate<'tcx>> {
+ self.probe_op(
+ item_name.span,
+ mode,
+ Some(item_name),
+ return_type,
+ is_suggestion,
+ self_ty,
+ scope_expr_id,
+ scope,
+ |probe_cx| {
+ Ok(probe_cx
+ .inherent_candidates
+ .into_iter()
+ .chain(probe_cx.extension_candidates)
+ .collect())
+ },
+ )
+ .unwrap()
+ }
+
fn probe_op<OP, R>(
&'a self,
span: Span,
@@ -334,7 +367,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
op: OP,
) -> Result<R, MethodError<'tcx>>
where
- OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>,
+ OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>,
{
let mut orig_values = OriginalQueryValues::default();
let param_env_and_self_ty = self.canonicalize_query(
@@ -445,7 +478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mode,
method_name,
return_type,
- orig_values,
+ &orig_values,
steps.steps,
scope_expr_id,
);
@@ -453,7 +486,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
probe_cx.assemble_inherent_candidates();
match scope {
ProbeScope::TraitsInScope => {
- probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)
+ probe_cx.assemble_extension_candidates_for_traits_in_scope()
}
ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
};
@@ -539,7 +572,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
mode: Mode,
method_name: Option<Ident>,
return_type: Option<Ty<'tcx>>,
- orig_steps_var_values: OriginalQueryValues<'tcx>,
+ orig_steps_var_values: &'a OriginalQueryValues<'tcx>,
steps: &'tcx [CandidateStep<'tcx>],
scope_expr_id: hir::HirId,
) -> ProbeContext<'a, 'tcx> {
@@ -554,10 +587,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
impl_dups: FxHashSet::default(),
orig_steps_var_values,
steps,
- static_candidates: Vec::new(),
allow_similar_names: false,
private_candidate: None,
- unsatisfied_predicates: Vec::new(),
+ static_candidates: RefCell::new(Vec::new()),
+ unsatisfied_predicates: RefCell::new(Vec::new()),
scope_expr_id,
}
}
@@ -566,8 +599,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.inherent_candidates.clear();
self.extension_candidates.clear();
self.impl_dups.clear();
- self.static_candidates.clear();
self.private_candidate = None;
+ self.static_candidates.borrow_mut().clear();
+ self.unsatisfied_predicates.borrow_mut().clear();
}
///////////////////////////////////////////////////////////////////////////
@@ -855,9 +889,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) {
+ fn assemble_extension_candidates_for_traits_in_scope(&mut self) {
let mut duplicates = FxHashSet::default();
- let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id);
+ let opt_applicable_traits = self.tcx.in_scope_traits(self.scope_expr_id);
if let Some(applicable_traits) = opt_applicable_traits {
for trait_candidate in applicable_traits.iter() {
let trait_did = trait_candidate.def_id;
@@ -918,7 +952,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
) {
debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id);
let trait_substs = self.fresh_item_substs(trait_def_id);
- let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs);
+ let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs);
if self.tcx.is_trait_alias(trait_def_id) {
// For trait aliases, assume all supertraits are relevant.
@@ -941,6 +975,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
});
} else {
debug_assert!(self.tcx.is_trait(trait_def_id));
+ if self.tcx.trait_is_auto(trait_def_id) {
+ return;
+ }
for item in self.impl_or_trait_item(trait_def_id) {
// Check whether `trait_def_id` defines a method with suitable name.
if !self.has_applicable_self(&item) {
@@ -1003,9 +1040,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
debug!("pick: actual search failed, assemble diagnostics");
- let static_candidates = mem::take(&mut self.static_candidates);
+ let static_candidates = std::mem::take(self.static_candidates.get_mut());
let private_candidate = self.private_candidate.take();
- let unsatisfied_predicates = mem::take(&mut self.unsatisfied_predicates);
+ let unsatisfied_predicates = std::mem::take(self.unsatisfied_predicates.get_mut());
// things failed, so lets look at all traits, for diagnostic purposes now:
self.reset();
@@ -1050,7 +1087,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}))
}
- fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
+ fn pick_core(&self) -> Option<PickResult<'tcx>> {
let pick = self.pick_all_method(Some(&mut vec![]));
// In this case unstable picking is done by `pick_method`.
@@ -1065,11 +1102,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
fn pick_all_method(
- &mut self,
+ &self,
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>> {
- let steps = self.steps.clone();
- steps
+ self.steps
.iter()
.filter(|step| {
debug!("pick_all_method: step={:?}", step);
@@ -1077,7 +1113,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// a raw pointer
!step.self_ty.references_error() && !step.from_unsafe_deref
})
- .flat_map(|step| {
+ .find_map(|step| {
let InferOk { value: self_ty, obligations: _ } = self
.fcx
.probe_instantiate_query_response(
@@ -1113,7 +1149,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
})
})
})
- .next()
}
/// For each type `T` in the step list, this attempts to find a method where
@@ -1123,7 +1158,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
/// to transparently pass `&mut` pointers, in particular, without consuming
/// them for their entire lifetime.
fn pick_by_value_method(
- &mut self,
+ &self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
@@ -1151,7 +1186,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
fn pick_autorefd_method(
- &mut self,
+ &self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
mutbl: hir::Mutability,
@@ -1177,7 +1212,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
/// special case for this is because going from `*mut T` to `*const T` with autoderefs and
/// autorefs would require dereferencing the pointer, which is not safe.
fn pick_const_ptr_method(
- &mut self,
+ &self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
@@ -1202,7 +1237,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
})
}
- fn pick_method_with_unstable(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
+ fn pick_method_with_unstable(&self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty));
let mut possibly_unsatisfied_predicates = Vec::new();
@@ -1213,7 +1248,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
debug!("searching {} candidates", kind);
let res = self.consider_candidates(
self_ty,
- candidates.iter(),
+ candidates,
&mut possibly_unsatisfied_predicates,
Some(&mut vec![]),
);
@@ -1222,21 +1257,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- debug!("searching unstable candidates");
- let res = self.consider_candidates(
- self_ty,
- self.inherent_candidates.iter().chain(&self.extension_candidates),
- &mut possibly_unsatisfied_predicates,
- None,
- );
- if res.is_none() {
- self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
+ for (kind, candidates) in
+ &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
+ {
+ debug!("searching unstable {kind} candidates");
+ let res = self.consider_candidates(
+ self_ty,
+ candidates,
+ &mut possibly_unsatisfied_predicates,
+ None,
+ );
+ if res.is_some() {
+ return res;
+ }
}
- res
+
+ self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates);
+ None
}
fn pick_method(
- &mut self,
+ &self,
self_ty: Ty<'tcx>,
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>> {
@@ -1254,7 +1295,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
debug!("searching {} candidates", kind);
let res = self.consider_candidates(
self_ty,
- candidates.iter(),
+ candidates,
&mut possibly_unsatisfied_predicates,
unstable_candidates.as_deref_mut(),
);
@@ -1266,28 +1307,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// `pick_method` may be called twice for the same self_ty if no stable methods
// match. Only extend once.
if unstable_candidates.is_some() {
- self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
+ self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates);
}
None
}
- fn consider_candidates<'b, ProbesIter>(
+ fn consider_candidates(
&self,
self_ty: Ty<'tcx>,
- probes: ProbesIter,
+ candidates: &[Candidate<'tcx>],
possibly_unsatisfied_predicates: &mut Vec<(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
- ) -> Option<PickResult<'tcx>>
- where
- ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone,
- 'tcx: 'b,
- {
- let mut applicable_candidates: Vec<_> = probes
- .clone()
+ ) -> Option<PickResult<'tcx>> {
+ let mut applicable_candidates: Vec<_> = candidates
+ .iter()
.map(|probe| {
(probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
})
@@ -1305,11 +1342,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
if let Some(uc) = &mut unstable_candidates {
- applicable_candidates.retain(|&(p, _)| {
+ applicable_candidates.retain(|&(candidate, _)| {
if let stability::EvalResult::Deny { feature, .. } =
- self.tcx.eval_stability(p.item.def_id, None, self.span, None)
+ self.tcx.eval_stability(candidate.item.def_id, None, self.span, None)
{
- uc.push((p.clone(), feature));
+ uc.push((candidate.clone(), feature));
return false;
}
true
@@ -1317,7 +1354,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
if applicable_candidates.len() > 1 {
- let sources = probes.map(|p| self.candidate_source(p, self_ty)).collect();
+ let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
return Some(Err(MethodError::Ambiguity(sources)));
}
@@ -1505,7 +1542,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let InferOk {
value: normalized_xform_ret_ty,
obligations: normalization_obligations,
- } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty);
+ } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty);
xform_ret_ty = normalized_xform_ret_ty;
debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
@@ -1519,7 +1556,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// Convert the bounds into obligations.
let impl_obligations = traits::predicates_for_generics(
- move |_, _| cause.clone(),
+ |_idx, span| {
+ let misc = traits::ObligationCause::misc(span, self.body_id);
+ let parent_trait_pred = ty::Binder::dummy(ty::TraitPredicate {
+ trait_ref: ty::TraitRef::from_method(self.tcx, impl_def_id, substs),
+ constness: ty::BoundConstness::NotConst,
+ polarity: ty::ImplPolarity::Positive,
+ });
+ misc.derived_cause(parent_trait_pred, |derived| {
+ traits::ImplDerivedObligation(Box::new(
+ traits::ImplDerivedObligationCause {
+ derived,
+ impl_def_id,
+ span,
+ },
+ ))
+ })
+ },
self.param_env,
impl_bounds,
);
@@ -1534,11 +1587,29 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let o = self.resolve_vars_if_possible(o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
- possibly_unsatisfied_predicates.push((
- o.predicate,
- None,
- Some(o.cause),
- ));
+ let parent_o = o.clone();
+ let implied_obligations =
+ traits::elaborate_obligations(self.tcx, vec![o]);
+ for o in implied_obligations {
+ let parent = if o == parent_o {
+ None
+ } else {
+ if o.predicate.to_opt_poly_trait_pred().map(|p| p.def_id())
+ == self.tcx.lang_items().sized_trait()
+ {
+ // We don't care to talk about implicit `Sized` bounds.
+ continue;
+ }
+ Some(parent_o.predicate)
+ };
+ if !self.predicate_may_hold(&o) {
+ possibly_unsatisfied_predicates.push((
+ o.predicate,
+ parent,
+ Some(o.cause),
+ ));
+ }
+ }
}
}
}
@@ -1562,7 +1633,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx);
parent_pred = Some(predicate);
let obligation =
- traits::Obligation::new(self.tcx, cause, self.param_env, predicate);
+ traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate);
if !self.predicate_may_hold(&obligation) {
result = ProbeResult::NoMatch;
if self.probe(|_| {
@@ -1621,22 +1692,48 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- if let ProbeResult::Match = result {
- if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) {
- let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty);
- debug!(
- "comparing return_ty {:?} with xform ret ty {:?}",
- return_ty, probe.xform_ret_ty
- );
- if self
- .at(&ObligationCause::dummy(), self.param_env)
- .define_opaque_types(false)
- .sup(return_ty, xform_ret_ty)
- .is_err()
- {
- return ProbeResult::BadReturnType;
+ if let ProbeResult::Match = result
+ && let Some(return_ty) = self.return_type
+ && let Some(mut xform_ret_ty) = xform_ret_ty
+ {
+ // `xform_ret_ty` has only been normalized for `InherentImplCandidate`.
+ // We don't normalize the other candidates for perf/backwards-compat reasons...
+ // but `self.return_type` is only set on the diagnostic-path, so we
+ // should be okay doing it here.
+ if !matches!(probe.kind, InherentImplCandidate(..)) {
+ let InferOk {
+ value: normalized_xform_ret_ty,
+ obligations: normalization_obligations,
+ } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty);
+ xform_ret_ty = normalized_xform_ret_ty;
+ debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
+ // Evaluate those obligations to see if they might possibly hold.
+ for o in normalization_obligations {
+ let o = self.resolve_vars_if_possible(o);
+ if !self.predicate_may_hold(&o) {
+ result = ProbeResult::NoMatch;
+ possibly_unsatisfied_predicates.push((
+ o.predicate,
+ None,
+ Some(o.cause),
+ ));
+ }
}
}
+
+ debug!(
+ "comparing return_ty {:?} with xform ret ty {:?}",
+ return_ty, xform_ret_ty
+ );
+ if let ProbeResult::Match = result
+ && self
+ .at(&ObligationCause::dummy(), self.param_env)
+ .define_opaque_types(false)
+ .sup(return_ty, xform_ret_ty)
+ .is_err()
+ {
+ result = ProbeResult::BadReturnType;
+ }
}
result
@@ -1650,7 +1747,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
/// probe. This will result in a pending obligation so when more type-info is available we can
/// make the final decision.
///
- /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`):
+ /// Example (`tests/ui/method-two-trait-defer-resolution-1.rs`):
///
/// ```ignore (illustrative)
/// trait Foo { ... }
@@ -1701,7 +1798,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.mode,
self.method_name,
self.return_type,
- self.orig_steps_var_values.clone(),
+ &self.orig_steps_var_values,
steps,
self.scope_expr_id,
);
@@ -1763,8 +1860,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// -- but this could be overcome.
}
- fn record_static_candidate(&mut self, source: CandidateSource) {
- self.static_candidates.push(source);
+ fn record_static_candidate(&self, source: CandidateSource) {
+ self.static_candidates.borrow_mut().push(source);
}
#[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index db93cfab2..2e1fc4c38 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2,6 +2,7 @@
//! found or is otherwise invalid.
use crate::errors;
+use crate::Expectation;
use crate::FnCtxt;
use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -25,7 +26,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
use rustc_middle::traits::util::supertraits;
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
-use rustc_middle::ty::print::with_crate_prefix;
+use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths};
use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitable};
use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
use rustc_span::symbol::{kw, sym, Ident};
@@ -100,982 +101,1099 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
}
+ #[instrument(level = "debug", skip(self))]
pub fn report_method_error(
&self,
- mut span: Span,
+ span: Span,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+ expected: Expectation<'tcx>,
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
// Avoid suggestions when we don't know what's going on.
if rcvr_ty.references_error() {
return None;
}
- let report_candidates = |span: Span,
- err: &mut Diagnostic,
- sources: &mut Vec<CandidateSource>,
- sugg_span: Option<Span>| {
- sources.sort();
- sources.dedup();
- // Dynamic limit to avoid hiding just one candidate, which is silly.
- let limit = if sources.len() == 5 { 5 } else { 4 };
-
- for (idx, source) in sources.iter().take(limit).enumerate() {
- match *source {
- CandidateSource::Impl(impl_did) => {
- // Provide the best span we can. Use the item, if local to crate, else
- // the impl, if local to crate (item may be defaulted), else nothing.
- let Some(item) = self.associated_value(impl_did, item_name).or_else(|| {
- let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
- self.associated_value(impl_trait_ref.def_id, item_name)
- }) else {
- continue;
- };
+ let sugg_span = if let SelfSource::MethodCall(expr) = source {
+ // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing.
+ self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)).span
+ } else {
+ span
+ };
- let note_span = if item.def_id.is_local() {
- Some(self.tcx.def_span(item.def_id))
- } else if impl_did.is_local() {
- Some(self.tcx.def_span(impl_did))
- } else {
- None
- };
+ match error {
+ MethodError::NoMatch(mut no_match_data) => {
+ return self.report_no_match_method_error(
+ span,
+ rcvr_ty,
+ item_name,
+ source,
+ args,
+ sugg_span,
+ &mut no_match_data,
+ expected,
+ );
+ }
- let impl_ty = self.tcx.at(span).type_of(impl_did);
+ MethodError::Ambiguity(mut sources) => {
+ let mut err = struct_span_err!(
+ self.sess(),
+ item_name.span,
+ E0034,
+ "multiple applicable items in scope"
+ );
+ err.span_label(item_name.span, format!("multiple `{}` found", item_name));
- let insertion = match self.tcx.impl_trait_ref(impl_did) {
- None => String::new(),
- Some(trait_ref) => format!(
- " of the trait `{}`",
- self.tcx.def_path_str(trait_ref.def_id)
- ),
- };
+ self.note_candidates_on_method_error(
+ rcvr_ty,
+ item_name,
+ args,
+ span,
+ &mut err,
+ &mut sources,
+ Some(sugg_span),
+ );
+ err.emit();
+ }
- let (note_str, idx) = if sources.len() > 1 {
- (
- format!(
- "candidate #{} is defined in an impl{} for the type `{}`",
- idx + 1,
- insertion,
- impl_ty,
- ),
- Some(idx + 1),
- )
- } else {
- (
- format!(
- "the candidate is defined in an impl{} for the type `{}`",
- insertion, impl_ty,
- ),
- None,
- )
- };
- if let Some(note_span) = note_span {
- // We have a span pointing to the method. Show note with snippet.
- err.span_note(note_span, &note_str);
- } else {
- err.note(&note_str);
- }
- if let Some(sugg_span) = sugg_span
- && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
- let path = self.tcx.def_path_str(trait_ref.def_id);
-
- let ty = match item.kind {
- ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty,
- ty::AssocKind::Fn => self
- .tcx
- .fn_sig(item.def_id)
- .inputs()
- .skip_binder()
- .get(0)
- .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr())
- .copied()
- .unwrap_or(rcvr_ty),
- };
- print_disambiguation_help(
- item_name,
- args,
- err,
- path,
- ty,
- item.kind,
- item.def_id,
- sugg_span,
- idx,
- self.tcx.sess.source_map(),
- item.fn_has_self_parameter,
- );
+ MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
+ let kind = kind.descr(def_id);
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ item_name.span,
+ E0624,
+ "{} `{}` is private",
+ kind,
+ item_name
+ );
+ err.span_label(item_name.span, &format!("private {}", kind));
+ let sp = self
+ .tcx
+ .hir()
+ .span_if_local(def_id)
+ .unwrap_or_else(|| self.tcx.def_span(def_id));
+ err.span_label(sp, &format!("private {} defined here", kind));
+ self.suggest_valid_traits(&mut err, out_of_scope_traits);
+ err.emit();
+ }
+
+ MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => {
+ let msg = if needs_mut {
+ with_forced_trimmed_paths!(format!(
+ "the `{item_name}` method cannot be invoked on `{rcvr_ty}`"
+ ))
+ } else {
+ format!("the `{item_name}` method cannot be invoked on a trait object")
+ };
+ let mut err = self.sess().struct_span_err(span, &msg);
+ if !needs_mut {
+ err.span_label(bound_span, "this has a `Sized` requirement");
+ }
+ if !candidates.is_empty() {
+ let help = format!(
+ "{an}other candidate{s} {were} found in the following trait{s}, perhaps \
+ add a `use` for {one_of_them}:",
+ an = if candidates.len() == 1 { "an" } else { "" },
+ s = pluralize!(candidates.len()),
+ were = pluralize!("was", candidates.len()),
+ one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
+ );
+ self.suggest_use_candidates(&mut err, help, candidates);
+ }
+ if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
+ if needs_mut {
+ let trait_type = self.tcx.mk_ref(
+ *region,
+ ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
+ );
+ let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty);
+ let mut kind = &self_expr.kind;
+ while let hir::ExprKind::AddrOf(_, _, expr)
+ | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind
+ {
+ kind = &expr.kind;
}
- }
- CandidateSource::Trait(trait_did) => {
- let Some(item) = self.associated_value(trait_did, item_name) else { continue };
- let item_span = self.tcx.def_span(item.def_id);
- let idx = if sources.len() > 1 {
- let msg = &format!(
- "candidate #{} is defined in the trait `{}`",
- idx + 1,
- self.tcx.def_path_str(trait_did)
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind
+ && let hir::def::Res::Local(hir_id) = path.res
+ && let Some(hir::Node::Pat(b)) = self.tcx.hir().find(hir_id)
+ && let Some(hir::Node::Param(p)) = self.tcx.hir().find_parent(b.hir_id)
+ && let Some(node) = self.tcx.hir().find_parent(p.hir_id)
+ && let Some(decl) = node.fn_decl()
+ && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span)
+ && let hir::TyKind::Ref(_, mut_ty) = &ty.kind
+ && let hir::Mutability::Not = mut_ty.mutbl
+ {
+ err.span_suggestion_verbose(
+ mut_ty.ty.span.shrink_to_lo(),
+ &msg,
+ "mut ",
+ Applicability::MachineApplicable,
);
- err.span_note(item_span, msg);
- Some(idx + 1)
} else {
- let msg = &format!(
- "the candidate is defined in the trait `{}`",
- self.tcx.def_path_str(trait_did)
- );
- err.span_note(item_span, msg);
- None
- };
- if let Some(sugg_span) = sugg_span {
- let path = self.tcx.def_path_str(trait_did);
- print_disambiguation_help(
- item_name,
- args,
- err,
- path,
- rcvr_ty,
- item.kind,
- item.def_id,
- sugg_span,
- idx,
- self.tcx.sess.source_map(),
- item.fn_has_self_parameter,
- );
+ err.help(&msg);
}
}
}
+ err.emit();
}
- if sources.len() > limit {
- err.note(&format!("and {} others", sources.len() - limit));
- }
- };
- let sugg_span = if let SelfSource::MethodCall(expr) = source {
- // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing.
- self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span
+ MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"),
+ }
+ None
+ }
+
+ pub fn report_no_match_method_error(
+ &self,
+ mut span: Span,
+ rcvr_ty: Ty<'tcx>,
+ item_name: Ident,
+ source: SelfSource<'tcx>,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+ sugg_span: Span,
+ no_match_data: &mut NoMatchData<'tcx>,
+ expected: Expectation<'tcx>,
+ ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
+ let mode = no_match_data.mode;
+ let tcx = self.tcx;
+ let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
+ let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty));
+ let is_method = mode == Mode::MethodCall;
+ let unsatisfied_predicates = &no_match_data.unsatisfied_predicates;
+ let lev_candidate = no_match_data.lev_candidate;
+ let item_kind = if is_method {
+ "method"
+ } else if rcvr_ty.is_enum() {
+ "variant or associated item"
} else {
- span
+ match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) {
+ (Some(name), false) if name.is_lowercase() => "function or associated item",
+ (Some(_), false) => "associated item",
+ (Some(_), true) | (None, false) => "variant or associated item",
+ (None, true) => "variant",
+ }
};
- match error {
- MethodError::NoMatch(NoMatchData {
- mut static_candidates,
- unsatisfied_predicates,
- out_of_scope_traits,
- lev_candidate,
- mode,
- }) => {
- let tcx = self.tcx;
-
- let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
- let ty_str = self.ty_to_string(rcvr_ty);
- let is_method = mode == Mode::MethodCall;
- let item_kind = if is_method {
- "method"
- } else if rcvr_ty.is_enum() {
- "variant or associated item"
- } else {
- match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) {
- (Some(name), false) if name.is_lowercase() => "function or associated item",
- (Some(_), false) => "associated item",
- (Some(_), true) | (None, false) => "variant or associated item",
- (None, true) => "variant",
+ if self.suggest_wrapping_range_with_parens(tcx, rcvr_ty, source, span, item_name, &ty_str)
+ || self.suggest_constraining_numerical_ty(
+ tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str,
+ )
+ {
+ return None;
+ }
+ span = item_name.span;
+
+ // Don't show generic arguments when the method can't be found in any implementation (#81576).
+ let mut ty_str_reported = ty_str.clone();
+ if let ty::Adt(_, generics) = rcvr_ty.kind() {
+ if generics.len() > 0 {
+ let mut autoderef = self.autoderef(span, rcvr_ty);
+ let candidate_found = autoderef.any(|(ty, _)| {
+ if let ty::Adt(adt_def, _) = ty.kind() {
+ self.tcx
+ .inherent_impls(adt_def.did())
+ .iter()
+ .any(|def_id| self.associated_value(*def_id, item_name).is_some())
+ } else {
+ false
}
- };
-
- if self.suggest_wrapping_range_with_parens(
- tcx, rcvr_ty, source, span, item_name, &ty_str,
- ) || self.suggest_constraining_numerical_ty(
- tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str,
- ) {
- return None;
- }
- span = item_name.span;
-
- // Don't show generic arguments when the method can't be found in any implementation (#81576).
- let mut ty_str_reported = ty_str.clone();
- if let ty::Adt(_, generics) = rcvr_ty.kind() {
- if generics.len() > 0 {
- let mut autoderef = self.autoderef(span, rcvr_ty);
- let candidate_found = autoderef.any(|(ty, _)| {
- if let ty::Adt(adt_def, _) = ty.kind() {
- self.tcx
- .inherent_impls(adt_def.did())
- .iter()
- .filter_map(|def_id| self.associated_value(*def_id, item_name))
- .count()
- >= 1
- } else {
- false
- }
- });
- let has_deref = autoderef.step_count() > 0;
- if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
- if let Some((path_string, _)) = ty_str.split_once('<') {
- ty_str_reported = path_string.to_string();
- }
- }
+ });
+ let has_deref = autoderef.step_count() > 0;
+ if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
+ if let Some((path_string, _)) = ty_str.split_once('<') {
+ ty_str_reported = path_string.to_string();
}
}
+ }
+ }
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0599,
- "no {} named `{}` found for {} `{}` in the current scope",
- item_kind,
- item_name,
- rcvr_ty.prefix_string(self.tcx),
- ty_str_reported,
- );
- if rcvr_ty.references_error() {
- err.downgrade_to_delayed_bug();
- }
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0599,
+ "no {} named `{}` found for {} `{}` in the current scope",
+ item_kind,
+ item_name,
+ rcvr_ty.prefix_string(self.tcx),
+ ty_str_reported,
+ );
+ if rcvr_ty.references_error() {
+ err.downgrade_to_delayed_bug();
+ }
- if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
- self.suggest_await_before_method(
- &mut err, item_name, rcvr_ty, cal, span,
- );
- }
- if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
- err.span_suggestion(
- span.shrink_to_lo(),
- "you are looking for the module in `std`, not the primitive type",
- "std::",
- Applicability::MachineApplicable,
- );
- }
- if let ty::RawPtr(_) = &rcvr_ty.kind() {
- err.note(
- "try using `<*const T>::as_ref()` to get a reference to the \
- type behind the pointer: https://doc.rust-lang.org/std/\
- primitive.pointer.html#method.as_ref",
- );
- err.note(
- "using `<*const T>::as_ref()` on a pointer which is unaligned or points \
- to invalid or uninitialized memory is undefined behavior",
- );
- }
+ if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
+ self.suggest_await_before_method(
+ &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self),
+ );
+ }
+ if let Some(span) =
+ tcx.resolutions(()).confused_type_with_std_module.get(&span.with_parent(None))
+ {
+ err.span_suggestion(
+ span.shrink_to_lo(),
+ "you are looking for the module in `std`, not the primitive type",
+ "std::",
+ Applicability::MachineApplicable,
+ );
+ }
+ if let ty::RawPtr(_) = &rcvr_ty.kind() {
+ err.note(
+ "try using `<*const T>::as_ref()` to get a reference to the \
+ type behind the pointer: https://doc.rust-lang.org/std/\
+ primitive.pointer.html#method.as_ref",
+ );
+ err.note(
+ "using `<*const T>::as_ref()` on a pointer which is unaligned or points \
+ to invalid or uninitialized memory is undefined behavior",
+ );
+ }
- let ty_span = match rcvr_ty.kind() {
- ty::Param(param_type) => Some(
- param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()),
- ),
- ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())),
- _ => None,
- };
- if let Some(span) = ty_span {
- err.span_label(
- span,
- format!(
- "{item_kind} `{item_name}` not found for this {}",
- rcvr_ty.prefix_string(self.tcx)
- ),
- );
- }
+ let ty_span = match rcvr_ty.kind() {
+ ty::Param(param_type) => {
+ Some(param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()))
+ }
+ ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())),
+ _ => None,
+ };
+ if let Some(span) = ty_span {
+ err.span_label(
+ span,
+ format!(
+ "{item_kind} `{item_name}` not found for this {}",
+ rcvr_ty.prefix_string(self.tcx)
+ ),
+ );
+ }
- if let SelfSource::MethodCall(rcvr_expr) = source {
- self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
- let call_expr = self
- .tcx
- .hir()
- .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
- let probe = self.lookup_probe(
- item_name,
- output_ty,
- call_expr,
- ProbeScope::AllTraits,
- );
- probe.is_ok()
- });
- }
+ if let SelfSource::MethodCall(rcvr_expr) = source {
+ self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
+ let call_expr =
+ self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id));
+ let probe = self.lookup_probe_for_diagnostic(
+ item_name,
+ output_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ expected.only_has_type(self),
+ );
+ probe.is_ok()
+ });
+ }
- let mut custom_span_label = false;
+ let mut custom_span_label = false;
- if !static_candidates.is_empty() {
- err.note(
- "found the following associated functions; to be used as methods, \
- functions must have a `self` parameter",
- );
- err.span_label(span, "this is an associated function, not a method");
- custom_span_label = true;
- }
- if static_candidates.len() == 1 {
- self.suggest_associated_call_syntax(
- &mut err,
- &static_candidates,
- rcvr_ty,
- source,
- item_name,
- args,
- sugg_span,
- );
+ let static_candidates = &mut no_match_data.static_candidates;
+ if !static_candidates.is_empty() {
+ err.note(
+ "found the following associated functions; to be used as methods, \
+ functions must have a `self` parameter",
+ );
+ err.span_label(span, "this is an associated function, not a method");
+ custom_span_label = true;
+ }
+ if static_candidates.len() == 1 {
+ self.suggest_associated_call_syntax(
+ &mut err,
+ &static_candidates,
+ rcvr_ty,
+ source,
+ item_name,
+ args,
+ sugg_span,
+ );
- report_candidates(span, &mut err, &mut static_candidates, None);
- } else if static_candidates.len() > 1 {
- report_candidates(span, &mut err, &mut static_candidates, Some(sugg_span));
- }
+ self.note_candidates_on_method_error(
+ rcvr_ty,
+ item_name,
+ args,
+ span,
+ &mut err,
+ static_candidates,
+ None,
+ );
+ } else if static_candidates.len() > 1 {
+ self.note_candidates_on_method_error(
+ rcvr_ty,
+ item_name,
+ args,
+ span,
+ &mut err,
+ static_candidates,
+ Some(sugg_span),
+ );
+ }
- let mut bound_spans = vec![];
- let mut restrict_type_params = false;
- let mut unsatisfied_bounds = false;
- if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
- let msg = "consider using `len` instead";
- if let SelfSource::MethodCall(_expr) = source {
- err.span_suggestion_short(
- span,
- msg,
- "len",
- Applicability::MachineApplicable,
- );
- } else {
- err.span_label(span, msg);
- }
- if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
- let iterator_trait = self.tcx.def_path_str(iterator_trait);
- err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"));
- }
- } else if !unsatisfied_predicates.is_empty() {
- let mut type_params = FxHashMap::default();
-
- // Pick out the list of unimplemented traits on the receiver.
- // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute.
- let mut unimplemented_traits = FxHashMap::default();
- let mut unimplemented_traits_only = true;
- for (predicate, _parent_pred, cause) in &unsatisfied_predicates {
- if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) =
- (predicate.kind().skip_binder(), cause.as_ref())
- {
- if p.trait_ref.self_ty() != rcvr_ty {
- // This is necessary, not just to keep the errors clean, but also
- // because our derived obligations can wind up with a trait ref that
- // requires a different param_env to be correctly compared.
- continue;
- }
- unimplemented_traits.entry(p.trait_ref.def_id).or_insert((
- predicate.kind().rebind(p.trait_ref),
- Obligation {
- cause: cause.clone(),
- param_env: self.param_env,
- predicate: *predicate,
- recursion_depth: 0,
- },
- ));
- }
+ let mut bound_spans = vec![];
+ let mut restrict_type_params = false;
+ let mut unsatisfied_bounds = false;
+ if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
+ let msg = "consider using `len` instead";
+ if let SelfSource::MethodCall(_expr) = source {
+ err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable);
+ } else {
+ err.span_label(span, msg);
+ }
+ if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
+ let iterator_trait = self.tcx.def_path_str(iterator_trait);
+ err.note(&format!(
+ "`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
+ ));
+ }
+ } else if !unsatisfied_predicates.is_empty() {
+ let mut type_params = FxHashMap::default();
+
+ // Pick out the list of unimplemented traits on the receiver.
+ // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute.
+ let mut unimplemented_traits = FxHashMap::default();
+ let mut unimplemented_traits_only = true;
+ for (predicate, _parent_pred, cause) in unsatisfied_predicates {
+ if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) =
+ (predicate.kind().skip_binder(), cause.as_ref())
+ {
+ if p.trait_ref.self_ty() != rcvr_ty {
+ // This is necessary, not just to keep the errors clean, but also
+ // because our derived obligations can wind up with a trait ref that
+ // requires a different param_env to be correctly compared.
+ continue;
}
+ unimplemented_traits.entry(p.trait_ref.def_id).or_insert((
+ predicate.kind().rebind(p.trait_ref),
+ Obligation {
+ cause: cause.clone(),
+ param_env: self.param_env,
+ predicate: *predicate,
+ recursion_depth: 0,
+ },
+ ));
+ }
+ }
- // Make sure that, if any traits other than the found ones were involved,
- // we don't don't report an unimplemented trait.
- // We don't want to say that `iter::Cloned` is not an iterator, just
- // because of some non-Clone item being iterated over.
- for (predicate, _parent_pred, _cause) in &unsatisfied_predicates {
- match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(p))
- if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {}
- _ => {
- unimplemented_traits_only = false;
- break;
- }
- }
+ // Make sure that, if any traits other than the found ones were involved,
+ // we don't don't report an unimplemented trait.
+ // We don't want to say that `iter::Cloned` is not an iterator, just
+ // because of some non-Clone item being iterated over.
+ for (predicate, _parent_pred, _cause) in unsatisfied_predicates {
+ match predicate.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::Clause::Trait(p))
+ if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {}
+ _ => {
+ unimplemented_traits_only = false;
+ break;
}
+ }
+ }
- let mut collect_type_param_suggestions =
- |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
- // We don't care about regions here, so it's fine to skip the binder here.
- if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) =
- (self_ty.kind(), parent_pred.kind().skip_binder())
- {
- let hir = self.tcx.hir();
- let node = match p.trait_ref.self_ty().kind() {
- ty::Param(_) => {
- // Account for `fn` items like in `issue-35677.rs` to
- // suggest restricting its type params.
- let parent_body =
- hir.body_owner(hir::BodyId { hir_id: self.body_id });
- Some(hir.get(parent_body))
- }
- ty::Adt(def, _) => {
- def.did().as_local().map(|def_id| hir.get_by_def_id(def_id))
- }
- _ => None,
- };
- if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
- if let Some(g) = kind.generics() {
- let key = (
- g.tail_span_for_predicate_suggestion(),
- g.add_where_or_trailing_comma(),
- );
- type_params
- .entry(key)
- .or_insert_with(FxHashSet::default)
- .insert(obligation.to_owned());
- }
- }
+ let mut collect_type_param_suggestions =
+ |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
+ // We don't care about regions here, so it's fine to skip the binder here.
+ if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) =
+ (self_ty.kind(), parent_pred.kind().skip_binder())
+ {
+ let hir = self.tcx.hir();
+ let node = match p.trait_ref.self_ty().kind() {
+ ty::Param(_) => {
+ // Account for `fn` items like in `issue-35677.rs` to
+ // suggest restricting its type params.
+ let parent_body =
+ hir.body_owner(hir::BodyId { hir_id: self.body_id });
+ Some(hir.get(parent_body))
}
- };
- let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
- let msg = format!(
- "doesn't satisfy `{}`",
- if obligation.len() > 50 { quiet } else { obligation }
- );
- match &self_ty.kind() {
- // Point at the type that couldn't satisfy the bound.
ty::Adt(def, _) => {
- bound_spans.push((self.tcx.def_span(def.did()), msg))
+ def.did().as_local().map(|def_id| hir.get_by_def_id(def_id))
}
- // Point at the trait object that couldn't satisfy the bound.
- ty::Dynamic(preds, _, _) => {
- for pred in preds.iter() {
- match pred.skip_binder() {
- ty::ExistentialPredicate::Trait(tr) => bound_spans
- .push((self.tcx.def_span(tr.def_id), msg.clone())),
- ty::ExistentialPredicate::Projection(_)
- | ty::ExistentialPredicate::AutoTrait(_) => {}
- }
+ _ => None,
+ };
+ if let Some(hir::Node::Item(hir::Item { kind, .. })) = node
+ && let Some(g) = kind.generics()
+ {
+ let key = (
+ g.tail_span_for_predicate_suggestion(),
+ g.add_where_or_trailing_comma(),
+ );
+ type_params
+ .entry(key)
+ .or_insert_with(FxHashSet::default)
+ .insert(obligation.to_owned());
+ return true;
+ }
+ }
+ false
+ };
+ let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
+ let msg = format!(
+ "doesn't satisfy `{}`",
+ if obligation.len() > 50 { quiet } else { obligation }
+ );
+ match &self_ty.kind() {
+ // Point at the type that couldn't satisfy the bound.
+ ty::Adt(def, _) => bound_spans.push((self.tcx.def_span(def.did()), msg)),
+ // Point at the trait object that couldn't satisfy the bound.
+ ty::Dynamic(preds, _, _) => {
+ for pred in preds.iter() {
+ match pred.skip_binder() {
+ ty::ExistentialPredicate::Trait(tr) => {
+ bound_spans.push((self.tcx.def_span(tr.def_id), msg.clone()))
}
+ ty::ExistentialPredicate::Projection(_)
+ | ty::ExistentialPredicate::AutoTrait(_) => {}
}
- // Point at the closure that couldn't satisfy the bound.
- ty::Closure(def_id, _) => bound_spans.push((
- tcx.def_span(*def_id),
- format!("doesn't satisfy `{}`", quiet),
- )),
- _ => {}
}
- };
- let mut format_pred = |pred: ty::Predicate<'tcx>| {
- let bound_predicate = pred.kind();
- match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
- let pred = bound_predicate.rebind(pred);
- // `<Foo as Iterator>::Item = String`.
- let projection_ty = pred.skip_binder().projection_ty;
-
- let substs_with_infer_self = tcx.mk_substs(
- iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
- .chain(projection_ty.substs.iter().skip(1)),
- );
+ }
+ // Point at the closure that couldn't satisfy the bound.
+ ty::Closure(def_id, _) => bound_spans
+ .push((tcx.def_span(*def_id), format!("doesn't satisfy `{}`", quiet))),
+ _ => {}
+ }
+ };
+ let mut format_pred = |pred: ty::Predicate<'tcx>| {
+ let bound_predicate = pred.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
+ let pred = bound_predicate.rebind(pred);
+ // `<Foo as Iterator>::Item = String`.
+ let projection_ty = pred.skip_binder().projection_ty;
+
+ let substs_with_infer_self = tcx.mk_substs(
+ iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+ .chain(projection_ty.substs.iter().skip(1)),
+ );
- let quiet_projection_ty = ty::ProjectionTy {
- substs: substs_with_infer_self,
- item_def_id: projection_ty.item_def_id,
- };
+ let quiet_projection_ty =
+ tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self);
- let term = pred.skip_binder().term;
+ let term = pred.skip_binder().term;
- let obligation = format!("{} = {}", projection_ty, term);
- let quiet = format!("{} = {}", quiet_projection_ty, term);
+ let obligation = format!("{} = {}", projection_ty, term);
+ let quiet = with_forced_trimmed_paths!(format!(
+ "{} = {}",
+ quiet_projection_ty, term
+ ));
- bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
- Some((obligation, projection_ty.self_ty()))
- }
- ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
- let p = poly_trait_ref.trait_ref;
- let self_ty = p.self_ty();
- let path = p.print_only_trait_path();
- let obligation = format!("{}: {}", self_ty, path);
- let quiet = format!("_: {}", path);
- bound_span_label(self_ty, &obligation, &quiet);
- Some((obligation, self_ty))
- }
- _ => None,
- }
- };
+ bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
+ Some((obligation, projection_ty.self_ty()))
+ }
+ ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
+ let p = poly_trait_ref.trait_ref;
+ let self_ty = p.self_ty();
+ let path = p.print_only_trait_path();
+ let obligation = format!("{}: {}", self_ty, path);
+ let quiet = with_forced_trimmed_paths!(format!("_: {}", path));
+ bound_span_label(self_ty, &obligation, &quiet);
+ Some((obligation, self_ty))
+ }
+ _ => None,
+ }
+ };
- // Find all the requirements that come from a local `impl` block.
- let mut skip_list: FxHashSet<_> = Default::default();
- let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default();
- for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates
- .iter()
- .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c)))
- .filter_map(|(p, parent, c)| match c.code() {
- ObligationCauseCode::ImplDerivedObligation(data) => {
- Some((&data.derived, p, parent, data.impl_def_id, data))
- }
- _ => None,
- })
+ // Find all the requirements that come from a local `impl` block.
+ let mut skip_list: FxHashSet<_> = Default::default();
+ let mut spanned_predicates = FxHashMap::default();
+ for (p, parent_p, impl_def_id, cause) in unsatisfied_predicates
+ .iter()
+ .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c)))
+ .filter_map(|(p, parent, c)| match c.code() {
+ ObligationCauseCode::ImplDerivedObligation(data)
+ if matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) =>
{
- let parent_trait_ref = data.parent_trait_pred;
- let path = parent_trait_ref.print_modifiers_and_trait_path();
- let tr_self_ty = parent_trait_ref.skip_binder().self_ty();
- let unsatisfied_msg = "unsatisfied trait bound introduced here";
- let derive_msg =
- "unsatisfied trait bound introduced in this `derive` macro";
- match self.tcx.hir().get_if_local(impl_def_id) {
- // Unmet obligation comes from a `derive` macro, point at it once to
- // avoid multiple span labels pointing at the same place.
- Some(Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
- ..
- })) if matches!(
- self_ty.span.ctxt().outer_expn_data().kind,
- ExpnKind::Macro(MacroKind::Derive, _)
- ) || matches!(
- of_trait.as_ref().map(|t| t
- .path
- .span
- .ctxt()
- .outer_expn_data()
- .kind),
- Some(ExpnKind::Macro(MacroKind::Derive, _))
- ) =>
- {
- let span = self_ty.span.ctxt().outer_expn_data().call_site;
- let mut spans: MultiSpan = span.into();
- spans.push_span_label(span, derive_msg);
- let entry = spanned_predicates.entry(spans);
- entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
- }
+ Some((p, parent, data.impl_def_id, data))
+ }
+ _ => None,
+ })
+ {
+ match self.tcx.hir().get_if_local(impl_def_id) {
+ // Unmet obligation comes from a `derive` macro, point at it once to
+ // avoid multiple span labels pointing at the same place.
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
+ ..
+ })) if matches!(
+ self_ty.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) || matches!(
+ of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind),
+ Some(ExpnKind::Macro(MacroKind::Derive, _))
+ ) =>
+ {
+ let span = self_ty.span.ctxt().outer_expn_data().call_site;
+ let entry = spanned_predicates.entry(span);
+ let entry = entry.or_insert_with(|| {
+ (FxHashSet::default(), FxHashSet::default(), Vec::new())
+ });
+ entry.0.insert(span);
+ entry.1.insert((
+ span,
+ "unsatisfied trait bound introduced in this `derive` macro",
+ ));
+ entry.2.push(p);
+ skip_list.insert(p);
+ }
- // Unmet obligation coming from an `impl`.
- Some(Node::Item(hir::Item {
- kind:
- hir::ItemKind::Impl(hir::Impl {
- of_trait, self_ty, generics, ..
- }),
- span: item_span,
- ..
- })) => {
- let sized_pred =
- unsatisfied_predicates.iter().any(|(pred, _, _)| {
- match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
- Some(pred.def_id())
- == self.tcx.lang_items().sized_trait()
- && pred.polarity == ty::ImplPolarity::Positive
- }
- _ => false,
- }
- });
- for param in generics.params {
- if param.span == cause.span && sized_pred {
- let (sp, sugg) = match param.colon_span {
- Some(sp) => (sp.shrink_to_hi(), " ?Sized +"),
- None => (param.span.shrink_to_hi(), ": ?Sized"),
- };
- err.span_suggestion_verbose(
- sp,
- "consider relaxing the type parameter's implicit \
- `Sized` bound",
- sugg,
- Applicability::MachineApplicable,
- );
+ // Unmet obligation coming from an `impl`.
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }),
+ span: item_span,
+ ..
+ })) => {
+ let sized_pred =
+ unsatisfied_predicates.iter().any(|(pred, _, _)| {
+ match pred.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
+ && pred.polarity == ty::ImplPolarity::Positive
}
+ _ => false,
}
- if let Some(pred) = parent_p {
- // Done to add the "doesn't satisfy" `span_label`.
- let _ = format_pred(*pred);
- }
- skip_list.insert(p);
- let mut spans = if cause.span != *item_span {
- let mut spans: MultiSpan = cause.span.into();
- spans.push_span_label(cause.span, unsatisfied_msg);
- spans
- } else {
- let mut spans = Vec::with_capacity(2);
- if let Some(trait_ref) = of_trait {
- spans.push(trait_ref.path.span);
- }
- spans.push(self_ty.span);
- spans.into()
+ });
+ for param in generics.params {
+ if param.span == cause.span && sized_pred {
+ let (sp, sugg) = match param.colon_span {
+ Some(sp) => (sp.shrink_to_hi(), " ?Sized +"),
+ None => (param.span.shrink_to_hi(), ": ?Sized"),
};
- if let Some(trait_ref) = of_trait {
- spans.push_span_label(trait_ref.path.span, "");
- }
- spans.push_span_label(self_ty.span, "");
-
- let entry = spanned_predicates.entry(spans);
- entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ err.span_suggestion_verbose(
+ sp,
+ "consider relaxing the type parameter's implicit `Sized` bound",
+ sugg,
+ Applicability::MachineApplicable,
+ );
}
- Some(_) => unreachable!(),
- None => (),
}
- }
- let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
- spanned_predicates.sort_by_key(|(span, (_, _, _))| span.primary_span());
- for (span, (_path, _self_ty, preds)) in spanned_predicates {
- let mut preds: Vec<_> = preds
- .into_iter()
- .filter_map(|pred| format_pred(*pred))
- .map(|(p, _)| format!("`{}`", p))
- .collect();
- preds.sort();
- preds.dedup();
- let msg = if let [pred] = &preds[..] {
- format!("trait bound {} was not satisfied", pred)
+ if let Some(pred) = parent_p {
+ // Done to add the "doesn't satisfy" `span_label`.
+ let _ = format_pred(*pred);
+ }
+ skip_list.insert(p);
+ let entry = spanned_predicates.entry(self_ty.span);
+ let entry = entry.or_insert_with(|| {
+ (FxHashSet::default(), FxHashSet::default(), Vec::new())
+ });
+ entry.2.push(p);
+ if cause.span != *item_span {
+ entry.0.insert(cause.span);
+ entry.1.insert((cause.span, "unsatisfied trait bound introduced here"));
} else {
- format!(
- "the following trait bounds were not satisfied:\n{}",
- preds.join("\n"),
- )
+ if let Some(trait_ref) = of_trait {
+ entry.0.insert(trait_ref.path.span);
+ }
+ entry.0.insert(self_ty.span);
};
- err.span_note(span, &msg);
- unsatisfied_bounds = true;
+ if let Some(trait_ref) = of_trait {
+ entry.1.insert((trait_ref.path.span, ""));
+ }
+ entry.1.insert((self_ty.span, ""));
}
-
- // The requirements that didn't have an `impl` span to show.
- let mut bound_list = unsatisfied_predicates
- .iter()
- .filter_map(|(pred, parent_pred, _cause)| {
- format_pred(*pred).map(|(p, self_ty)| {
- collect_type_param_suggestions(self_ty, *pred, &p);
- (
- match parent_pred {
- None => format!("`{}`", &p),
- Some(parent_pred) => match format_pred(*parent_pred) {
- None => format!("`{}`", &p),
- Some((parent_p, _)) => {
- collect_type_param_suggestions(
- self_ty,
- *parent_pred,
- &p,
- );
- format!(
- "`{}`\nwhich is required by `{}`",
- p, parent_p
- )
- }
- },
- },
- *pred,
- )
- })
- })
- .filter(|(_, pred)| !skip_list.contains(&pred))
- .map(|(t, _)| t)
- .enumerate()
- .collect::<Vec<(usize, String)>>();
-
- for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
- restrict_type_params = true;
- // #74886: Sort here so that the output is always the same.
- let mut obligations = obligations.into_iter().collect::<Vec<_>>();
- obligations.sort();
- err.span_suggestion_verbose(
- span,
- &format!(
- "consider restricting the type parameter{s} to satisfy the \
- trait bound{s}",
- s = pluralize!(obligations.len())
- ),
- format!("{} {}", add_where_or_comma, obligations.join(", ")),
- Applicability::MaybeIncorrect,
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..),
+ span: item_span,
+ ..
+ })) => {
+ tcx.sess.delay_span_bug(
+ *item_span,
+ "auto trait is invoked with no method error, but no error reported?",
);
}
-
- bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
- bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677
- bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order.
-
- if !bound_list.is_empty() || !skip_list.is_empty() {
- let bound_list = bound_list
- .into_iter()
- .map(|(_, path)| path)
- .collect::<Vec<_>>()
- .join("\n");
- let actual_prefix = rcvr_ty.prefix_string(self.tcx);
- info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
- let (primary_message, label) =
- if unimplemented_traits.len() == 1 && unimplemented_traits_only {
- unimplemented_traits
- .into_iter()
- .next()
- .map(|(_, (trait_ref, obligation))| {
- if trait_ref.self_ty().references_error()
- || rcvr_ty.references_error()
- {
- // Avoid crashing.
- return (None, None);
- }
- let OnUnimplementedNote { message, label, .. } = self
- .err_ctxt()
- .on_unimplemented_note(trait_ref, &obligation);
- (message, label)
- })
- .unwrap()
- } else {
- (None, None)
- };
- let primary_message = primary_message.unwrap_or_else(|| format!(
- "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied"
- ));
- err.set_primary_message(&primary_message);
- if let Some(label) = label {
- custom_span_label = true;
- err.span_label(span, label);
- }
- if !bound_list.is_empty() {
- err.note(&format!(
- "the following trait bounds were not satisfied:\n{bound_list}"
- ));
- }
- self.suggest_derive(&mut err, &unsatisfied_predicates);
-
- unsatisfied_bounds = true;
+ Some(Node::Item(hir::Item {
+ ident, kind: hir::ItemKind::Trait(..), ..
+ })) => {
+ skip_list.insert(p);
+ let entry = spanned_predicates.entry(ident.span);
+ let entry = entry.or_insert_with(|| {
+ (FxHashSet::default(), FxHashSet::default(), Vec::new())
+ });
+ entry.0.insert(cause.span);
+ entry.1.insert((ident.span, ""));
+ entry.1.insert((cause.span, "unsatisfied trait bound introduced here"));
+ entry.2.push(p);
}
+ Some(node) => unreachable!("encountered `{node:?}`"),
+ None => (),
+ }
+ }
+ let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
+ spanned_predicates.sort_by_key(|(span, _)| *span);
+ for (_, (primary_spans, span_labels, predicates)) in spanned_predicates {
+ let mut preds: Vec<_> = predicates
+ .iter()
+ .filter_map(|pred| format_pred(**pred))
+ .map(|(p, _)| format!("`{}`", p))
+ .collect();
+ preds.sort();
+ preds.dedup();
+ let msg = if let [pred] = &preds[..] {
+ format!("trait bound {} was not satisfied", pred)
+ } else {
+ format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),)
+ };
+ let mut span: MultiSpan = primary_spans.into_iter().collect::<Vec<_>>().into();
+ for (sp, label) in span_labels {
+ span.push_span_label(sp, label);
}
+ err.span_note(span, &msg);
+ unsatisfied_bounds = true;
+ }
- let label_span_not_found = |err: &mut Diagnostic| {
- if unsatisfied_predicates.is_empty() {
- err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
- let is_string_or_ref_str = match rcvr_ty.kind() {
- ty::Ref(_, ty, _) => {
- ty.is_str()
- || matches!(
- ty.kind(),
- ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string()
- )
+ let mut suggested_bounds = FxHashSet::default();
+ // The requirements that didn't have an `impl` span to show.
+ let mut bound_list = unsatisfied_predicates
+ .iter()
+ .filter_map(|(pred, parent_pred, _cause)| {
+ let mut suggested = false;
+ format_pred(*pred).map(|(p, self_ty)| {
+ if let Some(parent) = parent_pred && suggested_bounds.contains(parent) {
+ // We don't suggest `PartialEq` when we already suggest `Eq`.
+ } else if !suggested_bounds.contains(pred) {
+ if collect_type_param_suggestions(self_ty, *pred, &p) {
+ suggested = true;
+ suggested_bounds.insert(pred);
}
- ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(),
- _ => false,
- };
- if is_string_or_ref_str && item_name.name == sym::iter {
- err.span_suggestion_verbose(
- item_name.span,
- "because of the in-memory representation of `&str`, to obtain \
- an `Iterator` over each of its codepoint use method `chars`",
- "chars",
- Applicability::MachineApplicable,
- );
}
- if let ty::Adt(adt, _) = rcvr_ty.kind() {
- let mut inherent_impls_candidate = self
- .tcx
- .inherent_impls(adt.did())
- .iter()
- .copied()
- .filter(|def_id| {
- if let Some(assoc) = self.associated_value(*def_id, item_name) {
- // Check for both mode is the same so we avoid suggesting
- // incorrect associated item.
- match (mode, assoc.fn_has_self_parameter, source) {
- (Mode::MethodCall, true, SelfSource::MethodCall(_)) => {
- // We check that the suggest type is actually
- // different from the received one
- // So we avoid suggestion method with Box<Self>
- // for instance
- self.tcx.at(span).type_of(*def_id) != rcvr_ty
- && self.tcx.at(span).type_of(*def_id) != rcvr_ty
+ (
+ match parent_pred {
+ None => format!("`{}`", &p),
+ Some(parent_pred) => match format_pred(*parent_pred) {
+ None => format!("`{}`", &p),
+ Some((parent_p, _)) => {
+ if !suggested
+ && !suggested_bounds.contains(pred)
+ && !suggested_bounds.contains(parent_pred)
+ {
+ if collect_type_param_suggestions(
+ self_ty,
+ *parent_pred,
+ &p,
+ ) {
+ suggested_bounds.insert(pred);
}
- (Mode::Path, false, _) => true,
- _ => false,
}
- } else {
- false
+ format!("`{}`\nwhich is required by `{}`", p, parent_p)
}
- })
- .collect::<Vec<_>>();
- if !inherent_impls_candidate.is_empty() {
- inherent_impls_candidate.sort();
- inherent_impls_candidate.dedup();
-
- // number of type to shows at most.
- let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
- let type_candidates = inherent_impls_candidate
- .iter()
- .take(limit)
- .map(|impl_item| {
- format!("- `{}`", self.tcx.at(span).type_of(*impl_item))
- })
- .collect::<Vec<_>>()
- .join("\n");
- let additional_types = if inherent_impls_candidate.len() > limit {
- format!(
- "\nand {} more types",
- inherent_impls_candidate.len() - limit
- )
- } else {
- "".to_string()
- };
- err.note(&format!(
- "the {item_kind} was found for\n{}{}",
- type_candidates, additional_types
- ));
+ },
+ },
+ *pred,
+ )
+ })
+ })
+ .filter(|(_, pred)| !skip_list.contains(&pred))
+ .map(|(t, _)| t)
+ .enumerate()
+ .collect::<Vec<(usize, String)>>();
+
+ for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
+ restrict_type_params = true;
+ // #74886: Sort here so that the output is always the same.
+ let mut obligations = obligations.into_iter().collect::<Vec<_>>();
+ obligations.sort();
+ err.span_suggestion_verbose(
+ span,
+ &format!(
+ "consider restricting the type parameter{s} to satisfy the \
+ trait bound{s}",
+ s = pluralize!(obligations.len())
+ ),
+ format!("{} {}", add_where_or_comma, obligations.join(", ")),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
+ bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677
+ bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order.
+
+ if !bound_list.is_empty() || !skip_list.is_empty() {
+ let bound_list =
+ bound_list.into_iter().map(|(_, path)| path).collect::<Vec<_>>().join("\n");
+ let actual_prefix = rcvr_ty.prefix_string(self.tcx);
+ info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
+ let (primary_message, label) = if unimplemented_traits.len() == 1
+ && unimplemented_traits_only
+ {
+ unimplemented_traits
+ .into_iter()
+ .next()
+ .map(|(_, (trait_ref, obligation))| {
+ if trait_ref.self_ty().references_error() || rcvr_ty.references_error()
+ {
+ // Avoid crashing.
+ return (None, None);
}
- }
- } else {
- err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
- }
+ let OnUnimplementedNote { message, label, .. } =
+ self.err_ctxt().on_unimplemented_note(trait_ref, &obligation);
+ (message, label)
+ })
+ .unwrap()
+ } else {
+ (None, None)
};
-
- // If the method name is the name of a field with a function or closure type,
- // give a helping note that it has to be called as `(x.f)(...)`.
- if let SelfSource::MethodCall(expr) = source {
- if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err)
- && lev_candidate.is_none()
- && !custom_span_label
- {
- label_span_not_found(&mut err);
- }
- } else if !custom_span_label {
- label_span_not_found(&mut err);
- }
-
- // Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
- // can't be called due to `typeof(expr): Clone` not holding.
- if unsatisfied_predicates.is_empty() {
- self.suggest_calling_method_on_field(
- &mut err, source, span, rcvr_ty, item_name,
- );
+ let primary_message = primary_message.unwrap_or_else(|| {
+ format!(
+ "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \
+ but its trait bounds were not satisfied"
+ )
+ });
+ err.set_primary_message(&primary_message);
+ if let Some(label) = label {
+ custom_span_label = true;
+ err.span_label(span, label);
}
-
- self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
-
- bound_spans.sort();
- bound_spans.dedup();
- for (span, msg) in bound_spans.into_iter() {
- err.span_label(span, &msg);
+ if !bound_list.is_empty() {
+ err.note(&format!(
+ "the following trait bounds were not satisfied:\n{bound_list}"
+ ));
}
+ self.suggest_derive(&mut err, &unsatisfied_predicates);
- if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
- } else {
- self.suggest_traits_to_import(
- &mut err,
- span,
- rcvr_ty,
- item_name,
- args.map(|(_, args)| args.len() + 1),
- source,
- out_of_scope_traits,
- &unsatisfied_predicates,
- &static_candidates,
- unsatisfied_bounds,
- );
- }
+ unsatisfied_bounds = true;
+ }
+ }
- // Don't emit a suggestion if we found an actual method
- // that had unsatisfied trait bounds
- if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
- let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
- if let Some(suggestion) = lev_distance::find_best_match_for_name(
- &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
- item_name.name,
- None,
- ) {
- err.span_suggestion(
- span,
- "there is a variant with a similar name",
- suggestion,
- Applicability::MaybeIncorrect,
- );
+ let label_span_not_found = |err: &mut Diagnostic| {
+ if unsatisfied_predicates.is_empty() {
+ err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
+ let is_string_or_ref_str = match rcvr_ty.kind() {
+ ty::Ref(_, ty, _) => {
+ ty.is_str()
+ || matches!(
+ ty.kind(),
+ ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string()
+ )
}
+ ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(),
+ _ => false,
+ };
+ if is_string_or_ref_str && item_name.name == sym::iter {
+ err.span_suggestion_verbose(
+ item_name.span,
+ "because of the in-memory representation of `&str`, to obtain \
+ an `Iterator` over each of its codepoint use method `chars`",
+ "chars",
+ Applicability::MachineApplicable,
+ );
}
-
- if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() {
- let msg = "remove this method call";
- let mut fallback_span = true;
- if let SelfSource::MethodCall(expr) = source {
- let call_expr =
- self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
- if let Some(span) = call_expr.span.trim_start(expr.span) {
- err.span_suggestion(span, msg, "", Applicability::MachineApplicable);
- fallback_span = false;
- }
- }
- if fallback_span {
- err.span_label(span, msg);
- }
- } else if let Some(lev_candidate) = lev_candidate {
- // Don't emit a suggestion if we found an actual method
- // that had unsatisfied trait bounds
- if unsatisfied_predicates.is_empty() {
- let def_kind = lev_candidate.kind.as_def_kind();
- // Methods are defined within the context of a struct and their first parameter is always self,
- // which represents the instance of the struct the method is being called on
- // Associated functions don’t take self as a parameter and
- // they are not methods because they don’t have an instance of the struct to work with.
- if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
- err.span_suggestion(
- span,
- &format!("there is a method with a similar name",),
- lev_candidate.name,
- Applicability::MaybeIncorrect,
- );
+ if let ty::Adt(adt, _) = rcvr_ty.kind() {
+ let mut inherent_impls_candidate = self
+ .tcx
+ .inherent_impls(adt.did())
+ .iter()
+ .copied()
+ .filter(|def_id| {
+ if let Some(assoc) = self.associated_value(*def_id, item_name) {
+ // Check for both mode is the same so we avoid suggesting
+ // incorrect associated item.
+ match (mode, assoc.fn_has_self_parameter, source) {
+ (Mode::MethodCall, true, SelfSource::MethodCall(_)) => {
+ // We check that the suggest type is actually
+ // different from the received one
+ // So we avoid suggestion method with Box<Self>
+ // for instance
+ self.tcx.at(span).type_of(*def_id) != rcvr_ty
+ && self.tcx.at(span).type_of(*def_id) != rcvr_ty
+ }
+ (Mode::Path, false, _) => true,
+ _ => false,
+ }
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>();
+ if !inherent_impls_candidate.is_empty() {
+ inherent_impls_candidate.sort();
+ inherent_impls_candidate.dedup();
+
+ // number of type to shows at most.
+ let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
+ let type_candidates = inherent_impls_candidate
+ .iter()
+ .take(limit)
+ .map(|impl_item| {
+ format!("- `{}`", self.tcx.at(span).type_of(*impl_item))
+ })
+ .collect::<Vec<_>>()
+ .join("\n");
+ let additional_types = if inherent_impls_candidate.len() > limit {
+ format!("\nand {} more types", inherent_impls_candidate.len() - limit)
} else {
- err.span_suggestion(
- span,
- &format!(
- "there is {} {} with a similar name",
- def_kind.article(),
- def_kind.descr(lev_candidate.def_id),
- ),
- lev_candidate.name,
- Applicability::MaybeIncorrect,
- );
- }
+ "".to_string()
+ };
+ err.note(&format!(
+ "the {item_kind} was found for\n{}{}",
+ type_candidates, additional_types
+ ));
}
}
+ } else {
+ let ty_str =
+ if ty_str.len() > 50 { String::new() } else { format!("on `{ty_str}` ") };
+ err.span_label(
+ span,
+ format!("{item_kind} cannot be called {ty_str}due to unsatisfied trait bounds"),
+ );
+ }
+ };
- self.check_for_deref_method(&mut err, source, rcvr_ty, item_name);
-
- return Some(err);
+ // If the method name is the name of a field with a function or closure type,
+ // give a helping note that it has to be called as `(x.f)(...)`.
+ if let SelfSource::MethodCall(expr) = source {
+ if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err)
+ && lev_candidate.is_none()
+ && !custom_span_label
+ {
+ label_span_not_found(&mut err);
}
+ } else if !custom_span_label {
+ label_span_not_found(&mut err);
+ }
- MethodError::Ambiguity(mut sources) => {
- let mut err = struct_span_err!(
- self.sess(),
- item_name.span,
- E0034,
- "multiple applicable items in scope"
- );
- err.span_label(item_name.span, format!("multiple `{}` found", item_name));
+ // Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
+ // can't be called due to `typeof(expr): Clone` not holding.
+ if unsatisfied_predicates.is_empty() {
+ self.suggest_calling_method_on_field(
+ &mut err,
+ source,
+ span,
+ rcvr_ty,
+ item_name,
+ expected.only_has_type(self),
+ );
+ }
- report_candidates(span, &mut err, &mut sources, Some(sugg_span));
- err.emit();
- }
+ self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
- MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
- let kind = kind.descr(def_id);
- let mut err = struct_span_err!(
- self.tcx.sess,
- item_name.span,
- E0624,
- "{} `{}` is private",
- kind,
- item_name
+ bound_spans.sort();
+ bound_spans.dedup();
+ for (span, msg) in bound_spans.into_iter() {
+ err.span_label(span, &msg);
+ }
+
+ if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
+ } else {
+ self.suggest_traits_to_import(
+ &mut err,
+ span,
+ rcvr_ty,
+ item_name,
+ args.map(|(_, args)| args.len() + 1),
+ source,
+ no_match_data.out_of_scope_traits.clone(),
+ &unsatisfied_predicates,
+ &static_candidates,
+ unsatisfied_bounds,
+ expected.only_has_type(self),
+ );
+ }
+
+ // Don't emit a suggestion if we found an actual method
+ // that had unsatisfied trait bounds
+ if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
+ let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
+ if let Some(suggestion) = lev_distance::find_best_match_for_name(
+ &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
+ item_name.name,
+ None,
+ ) {
+ err.span_suggestion(
+ span,
+ "there is a variant with a similar name",
+ suggestion,
+ Applicability::MaybeIncorrect,
);
- err.span_label(item_name.span, &format!("private {}", kind));
- let sp = self
- .tcx
- .hir()
- .span_if_local(def_id)
- .unwrap_or_else(|| self.tcx.def_span(def_id));
- err.span_label(sp, &format!("private {} defined here", kind));
- self.suggest_valid_traits(&mut err, out_of_scope_traits);
- err.emit();
}
+ }
- MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => {
- let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
- let mut err = self.sess().struct_span_err(span, &msg);
- err.span_label(bound_span, "this has a `Sized` requirement");
- if !candidates.is_empty() {
- let help = format!(
- "{an}other candidate{s} {were} found in the following trait{s}, perhaps \
- add a `use` for {one_of_them}:",
- an = if candidates.len() == 1 { "an" } else { "" },
- s = pluralize!(candidates.len()),
- were = pluralize!("was", candidates.len()),
- one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
+ if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() {
+ let msg = "remove this method call";
+ let mut fallback_span = true;
+ if let SelfSource::MethodCall(expr) = source {
+ let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id));
+ if let Some(span) = call_expr.span.trim_start(expr.span) {
+ err.span_suggestion(span, msg, "", Applicability::MachineApplicable);
+ fallback_span = false;
+ }
+ }
+ if fallback_span {
+ err.span_label(span, msg);
+ }
+ } else if let Some(lev_candidate) = lev_candidate {
+ // Don't emit a suggestion if we found an actual method
+ // that had unsatisfied trait bounds
+ if unsatisfied_predicates.is_empty() {
+ let def_kind = lev_candidate.kind.as_def_kind();
+ // Methods are defined within the context of a struct and their first parameter is always self,
+ // which represents the instance of the struct the method is being called on
+ // Associated functions don’t take self as a parameter and
+ // they are not methods because they don’t have an instance of the struct to work with.
+ if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
+ err.span_suggestion(
+ span,
+ "there is a method with a similar name",
+ lev_candidate.name,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion(
+ span,
+ &format!(
+ "there is {} {} with a similar name",
+ def_kind.article(),
+ def_kind.descr(lev_candidate.def_id),
+ ),
+ lev_candidate.name,
+ Applicability::MaybeIncorrect,
);
- self.suggest_use_candidates(&mut err, help, candidates);
}
- if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
- if needs_mut {
- let trait_type = self.tcx.mk_ref(
- *region,
- ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
+ }
+ }
+
+ self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected);
+ return Some(err);
+ }
+
+ fn note_candidates_on_method_error(
+ &self,
+ rcvr_ty: Ty<'tcx>,
+ item_name: Ident,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+ span: Span,
+ err: &mut Diagnostic,
+ sources: &mut Vec<CandidateSource>,
+ sugg_span: Option<Span>,
+ ) {
+ sources.sort();
+ sources.dedup();
+ // Dynamic limit to avoid hiding just one candidate, which is silly.
+ let limit = if sources.len() == 5 { 5 } else { 4 };
+
+ for (idx, source) in sources.iter().take(limit).enumerate() {
+ match *source {
+ CandidateSource::Impl(impl_did) => {
+ // Provide the best span we can. Use the item, if local to crate, else
+ // the impl, if local to crate (item may be defaulted), else nothing.
+ let Some(item) = self.associated_value(impl_did, item_name).or_else(|| {
+ let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
+ self.associated_value(impl_trait_ref.skip_binder().def_id, item_name)
+ }) else {
+ continue;
+ };
+
+ let note_span = if item.def_id.is_local() {
+ Some(self.tcx.def_span(item.def_id))
+ } else if impl_did.is_local() {
+ Some(self.tcx.def_span(impl_did))
+ } else {
+ None
+ };
+
+ let impl_ty = self.tcx.at(span).type_of(impl_did);
+
+ let insertion = match self.tcx.impl_trait_ref(impl_did) {
+ None => String::new(),
+ Some(trait_ref) => {
+ format!(
+ " of the trait `{}`",
+ self.tcx.def_path_str(trait_ref.skip_binder().def_id)
+ )
+ }
+ };
+
+ let (note_str, idx) = if sources.len() > 1 {
+ (
+ format!(
+ "candidate #{} is defined in an impl{} for the type `{}`",
+ idx + 1,
+ insertion,
+ impl_ty,
+ ),
+ Some(idx + 1),
+ )
+ } else {
+ (
+ format!(
+ "the candidate is defined in an impl{} for the type `{}`",
+ insertion, impl_ty,
+ ),
+ None,
+ )
+ };
+ if let Some(note_span) = note_span {
+ // We have a span pointing to the method. Show note with snippet.
+ err.span_note(note_span, &note_str);
+ } else {
+ err.note(&note_str);
+ }
+ if let Some(sugg_span) = sugg_span
+ && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
+ let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id);
+
+ let ty = match item.kind {
+ ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty,
+ ty::AssocKind::Fn => self
+ .tcx
+ .fn_sig(item.def_id)
+ .inputs()
+ .skip_binder()
+ .get(0)
+ .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr())
+ .copied()
+ .unwrap_or(rcvr_ty),
+ };
+ print_disambiguation_help(
+ item_name,
+ args,
+ err,
+ path,
+ ty,
+ item.kind,
+ item.def_id,
+ sugg_span,
+ idx,
+ self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
+ );
+ }
+ }
+ CandidateSource::Trait(trait_did) => {
+ let Some(item) = self.associated_value(trait_did, item_name) else { continue };
+ let item_span = self.tcx.def_span(item.def_id);
+ let idx = if sources.len() > 1 {
+ let msg = &format!(
+ "candidate #{} is defined in the trait `{}`",
+ idx + 1,
+ self.tcx.def_path_str(trait_did)
+ );
+ err.span_note(item_span, msg);
+ Some(idx + 1)
+ } else {
+ let msg = &format!(
+ "the candidate is defined in the trait `{}`",
+ self.tcx.def_path_str(trait_did)
+ );
+ err.span_note(item_span, msg);
+ None
+ };
+ if let Some(sugg_span) = sugg_span {
+ let path = self.tcx.def_path_str(trait_did);
+ print_disambiguation_help(
+ item_name,
+ args,
+ err,
+ path,
+ rcvr_ty,
+ item.kind,
+ item.def_id,
+ sugg_span,
+ idx,
+ self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
);
- err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty));
}
}
- err.emit();
}
-
- MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"),
}
- None
+ if sources.len() > limit {
+ err.note(&format!("and {} others", sources.len() - limit));
+ }
}
/// Suggest calling `Ty::method` if `.method()` isn't found because the method
@@ -1244,7 +1362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
} else {
- let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+ let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id));
if let Some(span) = call_expr.span.trim_start(item_name.span) {
err.span_suggestion(
@@ -1318,13 +1436,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let range_ty =
self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
- let pick = self.probe_for_name(
- Mode::MethodCall,
+ let pick = self.lookup_probe_for_diagnostic(
item_name,
- IsSuggestion(true),
range_ty,
- expr.hir_id,
+ expr,
ProbeScope::AllTraits,
+ None,
);
if pick.is_ok() {
let range_span = parent_expr.span.with_hi(expr.span.hi());
@@ -1426,7 +1543,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let filename = tcx.sess.source_map().span_to_filename(span);
let parent_node =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
+ self.tcx.hir().get_parent(hir_id);
let msg = format!(
"you must specify a type for this binding, like `{}`",
concrete_type,
@@ -1499,16 +1616,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
visitor.visit_body(&body);
- let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
+ let parent = self.tcx.hir().parent_id(seg1.hir_id);
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent)
&& let Some(expr) = visitor.result
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
{
- let probe = self.lookup_probe(
+ let probe = self.lookup_probe_for_diagnostic(
seg2.ident,
self_ty,
call_expr,
ProbeScope::TraitsInScope,
+ None,
);
if probe.is_ok() {
let sm = self.infcx.tcx.sess.source_map();
@@ -1531,13 +1649,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
actual: Ty<'tcx>,
item_name: Ident,
+ return_type: Option<Ty<'tcx>>,
) {
if let SelfSource::MethodCall(expr) = source
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
&& let Some((fields, substs)) =
self.get_field_candidates_considering_privacy(span, actual, mod_id)
{
- let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
+ let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id));
let lang_items = self.tcx.lang_items();
let never_mention_traits = [
@@ -1554,11 +1673,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_for_nested_field_satisfying(
span,
&|_, field_ty| {
- self.lookup_probe(
+ self.lookup_probe_for_diagnostic(
item_name,
field_ty,
call_expr,
ProbeScope::TraitsInScope,
+ return_type,
)
.map_or(false, |pick| {
!never_mention_traits
@@ -1607,7 +1727,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let tcx = self.tcx;
let SelfSource::MethodCall(expr) = source else { return; };
- let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+ let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id));
let ty::Adt(kind, substs) = actual.kind() else { return; };
match kind.adt_kind() {
@@ -1624,9 +1744,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}
- self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope)
- .ok()
- .map(|pick| (variant, field, pick))
+ self.lookup_probe_for_diagnostic(
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ None,
+ )
+ .ok()
+ .map(|pick| (variant, field, pick))
})
.collect();
@@ -1690,11 +1816,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::AdtKind::Struct | ty::AdtKind::Union => {
let [first] = ***substs else { return; };
let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
- let Ok(pick) = self.lookup_probe(
+ let Ok(pick) = self.lookup_probe_for_diagnostic(
item_name,
ty,
call_expr,
ProbeScope::TraitsInScope,
+ None,
) else { return; };
let name = self.ty_to_value_string(actual);
@@ -1843,7 +1970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_derive(err, &preds);
}
- fn suggest_derive(
+ pub fn suggest_derive(
&self,
err: &mut Diagnostic,
unsatisfied_predicates: &[(
@@ -1853,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)],
) {
let mut derives = Vec::<(String, Span, Symbol)>::new();
- let mut traits = Vec::<Span>::new();
+ let mut traits = Vec::new();
for (pred, _, _) in unsatisfied_predicates {
let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue };
let adt = match trait_pred.self_ty().ty_adt_def() {
@@ -1892,10 +2019,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
derives.push((self_name, self_span, diagnostic_name));
} else {
- traits.push(self.tcx.def_span(trait_pred.def_id()));
+ traits.push(trait_pred.def_id());
}
} else {
- traits.push(self.tcx.def_span(trait_pred.def_id()));
+ traits.push(trait_pred.def_id());
}
}
traits.sort();
@@ -1918,10 +2045,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let len = traits.len();
if len > 0 {
- let span: MultiSpan = traits.into();
+ let span =
+ MultiSpan::from_spans(traits.iter().map(|&did| self.tcx.def_span(did)).collect());
+ let mut names = format!("`{}`", self.tcx.def_path_str(traits[0]));
+ for (i, &did) in traits.iter().enumerate().skip(1) {
+ if len > 2 {
+ names.push_str(", ");
+ }
+ if i == len - 1 {
+ names.push_str(" and ");
+ }
+ names.push('`');
+ names.push_str(&self.tcx.def_path_str(did));
+ names.push('`');
+ }
err.span_note(
span,
- &format!("the following trait{} must be implemented", pluralize!(len),),
+ &format!("the trait{} {} must be implemented", pluralize!(len), names),
);
}
@@ -1941,12 +2081,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_source: SelfSource<'tcx>,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
+ expected: Expectation<'tcx>,
) {
let SelfSource::QPath(ty) = self_source else { return; };
for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) {
if let Ok(pick) = self.probe_for_name(
Mode::Path,
item_name,
+ expected.only_has_type(self),
IsSuggestion(true),
deref_ty,
ty.hir_id,
@@ -1969,7 +2111,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::Float(_)
| ty::Adt(_, _)
| ty::Str
- | ty::Projection(_)
+ | ty::Alias(ty::Projection, _)
| ty::Param(_) => format!("{deref_ty}"),
// we need to test something like <&[_]>::len or <(&[u32])>::len
// and Vec::function();
@@ -2011,12 +2153,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty: Ty<'tcx>,
call: &hir::Expr<'_>,
span: Span,
+ return_type: Option<Ty<'tcx>>,
) {
let output_ty = match self.get_impl_future_output_ty(ty) {
Some(output_ty) => self.resolve_vars_if_possible(output_ty),
_ => return,
};
- let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
+ let method_exists =
+ self.method_exists(item_name, output_ty, call.hir_id, true, return_type);
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
if method_exists {
err.span_suggestion_verbose(
@@ -2130,6 +2274,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)],
static_candidates: &[CandidateSource],
unsatisfied_bounds: bool,
+ return_type: Option<Ty<'tcx>>,
) {
let mut alt_rcvr_sugg = false;
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
@@ -2152,7 +2297,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
] {
- match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
+ match self.lookup_probe_for_diagnostic(
+ item_name,
+ *rcvr_ty,
+ rcvr,
+ ProbeScope::AllTraits,
+ return_type,
+ ) {
Ok(pick) => {
// If the method is defined for the receiver we have, it likely wasn't `use`d.
// We point at the method, but we just skip the rest of the check for arbitrary
@@ -2185,11 +2336,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
] {
if let Some(new_rcvr_t) = *rcvr_ty
- && let Ok(pick) = self.lookup_probe(
+ && let Ok(pick) = self.lookup_probe_for_diagnostic(
item_name,
new_rcvr_t,
rcvr,
ProbeScope::AllTraits,
+ return_type,
)
{
debug!("try_alt_rcvr: pick candidate {:?}", pick);
@@ -2269,11 +2421,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
t.def_id() == info.def_id
}
ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
- p.projection_ty.item_def_id == info.def_id
+ p.projection_ty.def_id == info.def_id
}
_ => false,
}
}) && (type_is_local || info.def_id.is_local())
+ && !self.tcx.trait_is_auto(info.def_id)
&& self
.associated_value(info.def_id, item_name)
.filter(|item| {
@@ -2466,7 +2619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
})
.any(|imp_did| {
- let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
+ let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity();
let imp_simp =
simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder);
imp_simp.map_or(false, |s| s == simp_rcvr_ty)
@@ -2547,14 +2700,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
found: Ty<'tcx>,
expected: Ty<'tcx>,
) -> bool {
- let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
- else { return false; };
+ let Some((_def_id_or_name, output, _inputs)) =
+ self.extract_callable_info(found) else {
+ return false;
+ };
if !self.can_coerce(output, expected) {
return false;
}
- let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+ let parent = self.tcx.hir().parent_id(expr.hir_id);
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
let hir::ExprKind::MethodCall(
hir::PathSegment { ident: method_name, .. },
@@ -2567,11 +2722,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
name: Symbol::intern(&format!("{}_else", method_name.as_str())),
span: method_name.span,
};
- let probe = self.lookup_probe(
+ let probe = self.lookup_probe_for_diagnostic(
new_name,
self_ty,
self_expr,
ProbeScope::TraitsInScope,
+ Some(expected),
);
// check the method arguments number
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index b12d84af4..78cea1f4d 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -12,14 +12,16 @@ use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
+use rustc_middle::ty::{
+ self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
+};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
-use rustc_trait_selection::traits::FulfillmentError;
+use rustc_trait_selection::traits::{self, FulfillmentError};
use rustc_type_ir::sty::TyKind::*;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -48,8 +50,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self
.lookup_op_method(
lhs_deref_ty,
- Some(rhs_ty),
- Some(rhs),
+ Some((rhs, rhs_ty)),
Op::Binary(op, IsAssign::Yes),
expected,
)
@@ -60,8 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self
.lookup_op_method(
lhs_ty,
- Some(rhs_ty),
- Some(rhs),
+ Some((rhs, rhs_ty)),
Op::Binary(op, IsAssign::Yes),
expected,
)
@@ -248,8 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let result = self.lookup_op_method(
lhs_ty,
- Some(rhs_ty_var),
- Some(rhs_expr),
+ Some((rhs_expr, rhs_ty_var)),
Op::Binary(op, is_assign),
expected,
);
@@ -382,8 +381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self
.lookup_op_method(
lhs_deref_ty,
- Some(rhs_ty),
- Some(rhs_expr),
+ Some((rhs_expr, rhs_ty)),
Op::Binary(op, is_assign),
expected,
)
@@ -410,8 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let is_compatible = |lhs_ty, rhs_ty| {
self.lookup_op_method(
lhs_ty,
- Some(rhs_ty),
- Some(rhs_expr),
+ Some((rhs_expr, rhs_ty)),
Op::Binary(op, is_assign),
expected,
)
@@ -471,8 +468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let errors = self
.lookup_op_method(
lhs_ty,
- Some(rhs_ty),
- Some(rhs_expr),
+ Some((rhs_expr, rhs_ty)),
Op::Binary(op, is_assign),
expected,
)
@@ -492,6 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(output_def_id) = output_def_id
&& let Some(trait_def_id) = trait_def_id
&& self.tcx.parent(output_def_id) == trait_def_id
+ && output_ty.is_suggestable(self.tcx, false)
{
Some(("Output", *output_ty))
} else {
@@ -625,7 +622,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
assert!(op.is_by_value());
- match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) {
+ match self.lookup_op_method(operand_ty, None, Op::Unary(op, ex.span), expected) {
Ok(method) => {
self.write_method_call(ex.hir_id, method);
method.sig.output()
@@ -660,7 +657,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- let sp = self.tcx.sess.source_map().start_point(ex.span);
+ let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
if let Some(sp) =
self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
{
@@ -712,8 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn lookup_op_method(
&self,
lhs_ty: Ty<'tcx>,
- other_ty: Option<Ty<'tcx>>,
- other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+ opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
op: Op,
expected: Expectation<'tcx>,
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
@@ -742,20 +738,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Op::Unary(..) => 0,
},
) {
+ self.tcx
+ .sess
+ .delay_span_bug(span, "operator didn't have the right number of generic args");
return Err(vec![]);
}
let opname = Ident::with_dummy_span(opname);
+ let input_types =
+ opt_rhs.as_ref().map(|(_, ty)| std::slice::from_ref(ty)).unwrap_or_default();
+ let cause = self.cause(
+ span,
+ traits::BinOp {
+ rhs_span: opt_rhs.map(|(expr, _)| expr.span),
+ is_lit: opt_rhs
+ .map_or(false, |(expr, _)| matches!(expr.kind, hir::ExprKind::Lit(_))),
+ output_ty: expected.only_has_type(self),
+ },
+ );
+
let method = trait_did.and_then(|trait_did| {
- self.lookup_op_method_in_trait(
- span,
- opname,
- trait_did,
- lhs_ty,
- other_ty,
- other_ty_expr,
- expected,
- )
+ self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, Some(input_types))
});
match (method, trait_did) {
@@ -766,14 +769,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
(None, None) => Err(vec![]),
(None, Some(trait_did)) => {
- let (obligation, _) = self.obligation_for_op_method(
- span,
- trait_did,
- lhs_ty,
- other_ty,
- other_ty_expr,
- expected,
- );
+ let (obligation, _) =
+ self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types));
Err(rustc_trait_selection::traits::fully_solve_obligation(self, obligation))
}
}
@@ -904,7 +901,7 @@ enum Op {
}
/// Dereferences a single level of immutable referencing.
-fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
+fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
match ty.kind() {
ty::Ref(_, ty, hir::Mutability::Not) => *ty,
_ => ty,
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index decd317d9..467992452 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1,4 +1,4 @@
-use crate::FnCtxt;
+use crate::{FnCtxt, RawTy};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
@@ -386,7 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Byte string patterns behave the same way as array patterns
// They can denote both statically and dynamically-sized byte arrays.
let mut pat_ty = ty;
- if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(_), .. }) = lt.kind {
+ if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind {
let expected = self.structurally_resolved_type(span, expected);
if let ty::Ref(_, inner_ty, _) = expected.kind()
&& matches!(inner_ty.kind(), ty::Slice(_))
@@ -553,6 +553,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs),
_ => span_bug!(span, "Impossible, verified above."),
}
+ if (lhs, rhs).references_error() {
+ err.downgrade_to_delayed_bug();
+ }
if self.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"In a match expression, only numbers and characters can be matched \
@@ -692,7 +695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
if let PatKind::Ref(inner, mutbl) = pat.kind
&& let PatKind::Binding(_, _, binding, ..) = inner.kind {
- let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id);
+ let binding_parent_id = tcx.hir().parent_id(pat.hir_id);
let binding_parent = tcx.hir().get(binding_parent_id);
debug!(?inner, ?pat, ?binding_parent);
@@ -758,6 +761,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_note(sp, format!("{msg}: `{sugg}`"));
}
}
+ hir::Node::Pat(pt) if let PatKind::TupleStruct(_, pat_arr, _) = pt.kind => {
+ for i in pat_arr.iter() {
+ if let PatKind::Ref(the_ref, _) = i.kind
+ && let PatKind::Binding(mt, _, ident, _) = the_ref.kind {
+ let hir::BindingAnnotation(_, mtblty) = mt;
+ err.span_suggestion_verbose(
+ i.span,
+ format!("consider removing `&{mutability}` from the pattern"),
+ mtblty.prefix_str().to_string() + &ident.name.to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ if let Some((sp, msg, sugg)) = mut_var_suggestion {
+ err.span_note(sp, format!("{msg}: `{sugg}`"));
+ }
+ }
hir::Node::Param(_) | hir::Node::Arm(_) | hir::Node::Pat(_) => {
// rely on match ergonomics or it might be nested `&&pat`
err.span_suggestion_verbose(
@@ -839,7 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &Pat<'tcx>,
qpath: &hir::QPath<'_>,
- path_resolution: (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
+ path_resolution: (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
@@ -936,7 +956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
res.descr(),
),
);
- match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) {
+ match self.tcx.hir().get_parent(pat.hir_id) {
hir::Node::PatField(..) => {
e.span_suggestion_verbose(
ident.span.shrink_to_hi(),
@@ -1013,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (res, opt_ty, segments) =
self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span);
if res == Res::Err {
- let e = tcx.sess.delay_span_bug(pat.span, "`Res:Err` but no error emitted");
+ let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
on_error(e);
return tcx.ty_error_with_guaranteed(e);
@@ -1921,7 +1941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let tcx = self.tcx;
let expected = self.shallow_resolve(expected);
- let (rptr_ty, inner_ty) = if self.check_dereferenceable(pat.span, expected, inner) {
+ let (ref_ty, inner_ty) = if self.check_dereferenceable(pat.span, expected, inner) {
// `demand::subtype` would be good enough, but using `eqtype` turns
// out to be equally general. See (note_1) for details.
@@ -1936,9 +1956,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
kind: TypeVariableOriginKind::TypeInference,
span: inner.span,
});
- let rptr_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
- debug!("check_pat_ref: demanding {:?} = {:?}", expected, rptr_ty);
- let err = self.demand_eqtype_pat_diag(pat.span, expected, rptr_ty, ti);
+ let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
+ debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
+ let err = self.demand_eqtype_pat_diag(pat.span, expected, ref_ty, ti);
// Look for a case like `fn foo(&foo: u32)` and suggest
// `fn foo(foo: &u32)`
@@ -1946,7 +1966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.borrow_pat_suggestion(&mut err, pat);
err.emit();
}
- (rptr_ty, inner_ty)
+ (ref_ty, inner_ty)
}
}
} else {
@@ -1954,7 +1974,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(err, err)
};
self.check_pat(inner, inner_ty, def_bm, ti);
- rptr_ty
+ ref_ty
}
/// Create a reference type with a fresh region variable.
@@ -2130,7 +2150,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
{
let ty = self.resolve_vars_if_possible(ti.expected);
- let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(&mut err, snippet.clone(), ty);
+ let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(ty);
match is_slice_or_array_or_vector.1.kind() {
ty::Adt(adt_def, _)
if self.tcx.is_diagnostic_item(sym::Option, adt_def.did())
@@ -2159,17 +2179,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}
- fn is_slice_or_array_or_vector(
- &self,
- err: &mut Diagnostic,
- snippet: String,
- ty: Ty<'tcx>,
- ) -> (bool, Ty<'tcx>) {
+ fn is_slice_or_array_or_vector(&self, ty: Ty<'tcx>) -> (bool, Ty<'tcx>) {
match ty.kind() {
ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) => {
(true, ty)
}
- ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(err, snippet, *ty),
+ ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(*ty),
ty::Slice(..) | ty::Array(..) => (true, ty),
_ => (false, ty),
}
diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index 952ea1488..ae0df5aa8 100644
--- a/compiler/rustc_hir_typeck/src/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -3,6 +3,7 @@ use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp};
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir_analysis::autoderef::Autoderef;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
@@ -10,7 +11,6 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
-use rustc_trait_selection::autoderef::Autoderef;
use std::slice;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -225,7 +225,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
imm_tr.and_then(|trait_did| {
self.lookup_method_in_trait(
- span,
+ self.misc(span),
Ident::with_dummy_span(imm_op),
trait_did,
base_ty,
@@ -264,7 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mut_tr.and_then(|trait_did| {
self.lookup_method_in_trait(
- span,
+ self.misc(span),
Ident::with_dummy_span(mut_op),
trait_did,
base_ty,
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 0f4697201..e12a575d5 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -663,7 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// fields of some type, the observable drop order will remain the same as it previously
// was even though we're dropping each capture individually.
// See https://github.com/rust-lang/project-rfc-2229/issues/42 and
- // `src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`.
+ // `tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`.
for (_, captures) in &mut root_var_min_capture_list {
captures.sort_by(|capture1, capture2| {
for (p1, p2) in capture1.place.projections.iter().zip(&capture2.place.projections) {
@@ -1675,7 +1675,7 @@ fn apply_capture_kind_on_capture_ty<'tcx>(
}
/// Returns the Span of where the value with the provided HirId would be dropped
-fn drop_location_span<'tcx>(tcx: TyCtxt<'tcx>, hir_id: hir::HirId) -> Span {
+fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span {
let owner_id = tcx.hir().get_enclosing_scope(hir_id).unwrap();
let owner_node = tcx.hir().get(owner_id);
@@ -1843,10 +1843,10 @@ fn restrict_precision_for_drop_types<'a, 'tcx>(
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
/// them completely.
/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
-fn restrict_precision_for_unsafe<'tcx>(
- mut place: Place<'tcx>,
+fn restrict_precision_for_unsafe(
+ mut place: Place<'_>,
mut curr_mode: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
if place.base_ty.is_unsafe_ptr() {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0);
}
@@ -1876,10 +1876,10 @@ fn restrict_precision_for_unsafe<'tcx>(
/// - No Index projections are captured, since arrays are captured completely.
/// - No unsafe block is required to capture `place`
/// Returns the truncated place and updated capture mode.
-fn restrict_capture_precision<'tcx>(
- place: Place<'tcx>,
+fn restrict_capture_precision(
+ place: Place<'_>,
curr_mode: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode);
if place.projections.is_empty() {
@@ -1904,10 +1904,10 @@ fn restrict_capture_precision<'tcx>(
}
/// Truncate deref of any reference.
-fn adjust_for_move_closure<'tcx>(
- mut place: Place<'tcx>,
+fn adjust_for_move_closure(
+ mut place: Place<'_>,
mut kind: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
if let Some(idx) = first_deref {
@@ -1919,10 +1919,10 @@ fn adjust_for_move_closure<'tcx>(
/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
-fn adjust_for_non_move_closure<'tcx>(
- mut place: Place<'tcx>,
+fn adjust_for_non_move_closure(
+ mut place: Place<'_>,
mut kind: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let contains_deref =
place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
@@ -2225,10 +2225,10 @@ fn determine_place_ancestry_relation<'tcx>(
/// // it is constrained to `'a`
/// }
/// ```
-fn truncate_capture_for_optimization<'tcx>(
- mut place: Place<'tcx>,
+fn truncate_capture_for_optimization(
+ mut place: Place<'_>,
mut curr_mode: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not));
// Find the right-most deref (if any). All the projections that come after this
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 52ffd286e..250f4cd3f 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -448,8 +448,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() {
- let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id };
+ let fcx_closure_kind_origins =
+ fcx_typeck_results.closure_kind_origins().items_in_stable_order();
+
+ for (local_id, origin) in fcx_closure_kind_origins {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id };
let place_span = origin.0;
let place = self.resolve(origin.1.clone(), &place_span);
self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place));
@@ -458,11 +461,12 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
fn visit_coercion_casts(&mut self) {
let fcx_typeck_results = self.fcx.typeck_results.borrow();
- let fcx_coercion_casts = fcx_typeck_results.coercion_casts();
+
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
+ let fcx_coercion_casts = fcx_typeck_results.coercion_casts().to_sorted_stable_ord();
for local_id in fcx_coercion_casts {
- self.typeck_results.set_coercion_cast(*local_id);
+ self.typeck_results.set_coercion_cast(local_id);
}
}
@@ -471,22 +475,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- let mut errors_buffer = Vec::new();
- for (&local_id, c_ty) in fcx_typeck_results.user_provided_types().iter() {
- let hir_id = hir::HirId { owner: common_hir_owner, local_id };
-
- if cfg!(debug_assertions) && c_ty.needs_infer() {
- span_bug!(
- hir_id.to_span(self.fcx.tcx),
- "writeback: `{:?}` has inference variables",
- c_ty
- );
- };
+ if self.rustc_dump_user_substs {
+ let sorted_user_provided_types =
+ fcx_typeck_results.user_provided_types().items_in_stable_order();
- self.typeck_results.user_provided_types_mut().insert(hir_id, *c_ty);
+ let mut errors_buffer = Vec::new();
+ for (local_id, c_ty) in sorted_user_provided_types {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id };
- if let ty::UserType::TypeOf(_, user_substs) = c_ty.value {
- if self.rustc_dump_user_substs {
+ if let ty::UserType::TypeOf(_, user_substs) = c_ty.value {
// This is a unit-testing mechanism.
let span = self.tcx().hir().span(hir_id);
// We need to buffer the errors in order to guarantee a consistent
@@ -498,31 +495,49 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
err.buffer(&mut errors_buffer);
}
}
- }
- if !errors_buffer.is_empty() {
- errors_buffer.sort_by_key(|diag| diag.span.primary_span());
- for mut diag in errors_buffer {
- self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
+ if !errors_buffer.is_empty() {
+ errors_buffer.sort_by_key(|diag| diag.span.primary_span());
+ for mut diag in errors_buffer {
+ self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
+ }
}
}
+
+ self.typeck_results.user_provided_types_mut().extend(
+ fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id };
+
+ if cfg!(debug_assertions) && c_ty.needs_infer() {
+ span_bug!(
+ hir_id.to_span(self.fcx.tcx),
+ "writeback: `{:?}` has inference variables",
+ c_ty
+ );
+ };
+
+ (hir_id, *c_ty)
+ }),
+ );
}
fn visit_user_provided_sigs(&mut self) {
let fcx_typeck_results = self.fcx.typeck_results.borrow();
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
- for (&def_id, c_sig) in fcx_typeck_results.user_provided_sigs.iter() {
- if cfg!(debug_assertions) && c_sig.needs_infer() {
- span_bug!(
- self.fcx.tcx.def_span(def_id),
- "writeback: `{:?}` has inference variables",
- c_sig
- );
- };
-
- self.typeck_results.user_provided_sigs.insert(def_id, *c_sig);
- }
+ self.typeck_results.user_provided_sigs.extend(
+ fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| {
+ if cfg!(debug_assertions) && c_sig.needs_infer() {
+ span_bug!(
+ self.fcx.tcx.def_span(def_id),
+ "writeback: `{:?}` has inference variables",
+ c_sig
+ );
+ };
+
+ (def_id, *c_sig)
+ }),
+ );
}
fn visit_generator_interior_types(&mut self) {
@@ -534,8 +549,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn visit_opaque_types(&mut self) {
- let opaque_types =
- self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+ let opaque_types = self.fcx.infcx.take_opaque_types();
for (opaque_type_key, decl) in opaque_types {
let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
@@ -546,7 +560,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker {
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::Opaque(def_id, _) = *t.kind() {
+ if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *t.kind() {
if def_id == self.def_id.to_def_id() {
return ControlFlow::Break(());
}
@@ -642,7 +656,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (&local_id, &fn_sig) in fcx_typeck_results.liberated_fn_sigs().iter() {
+ let fcx_liberated_fn_sigs = fcx_typeck_results.liberated_fn_sigs().items_in_stable_order();
+
+ for (local_id, &fn_sig) in fcx_liberated_fn_sigs {
let hir_id = hir::HirId { owner: common_hir_owner, local_id };
let fn_sig = self.resolve(fn_sig, &hir_id);
self.typeck_results.liberated_fn_sigs_mut().insert(hir_id, fn_sig);
@@ -654,7 +670,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (&local_id, ftys) in fcx_typeck_results.fru_field_types().iter() {
+ let fcx_fru_field_types = fcx_typeck_results.fru_field_types().items_in_stable_order();
+
+ for (local_id, ftys) in fcx_fru_field_types {
let hir_id = hir::HirId { owner: common_hir_owner, local_id };
let ftys = self.resolve(ftys.clone(), &hir_id);
self.typeck_results.fru_field_types_mut().insert(hir_id, ftys);