summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs')
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs878
1 files changed, 601 insertions, 277 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 15f2ba809..6b09bc898 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -22,12 +22,13 @@ use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::is_range_literal;
use rustc_hir::lang_items::LangItem;
-use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
+use rustc_hir::{CoroutineKind, CoroutineSource, Node};
use rustc_hir::{Expr, HirId};
use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime};
use rustc_middle::hir::map;
+use rustc_middle::traits::IsConstable;
use rustc_middle::ty::error::TypeError::{self, Sorts};
use rustc_middle::ty::{
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
@@ -41,43 +42,43 @@ use rustc_target::spec::abi;
use std::borrow::Cow;
use std::iter;
-use super::InferCtxtPrivExt;
use crate::infer::InferCtxtExt as _;
+use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
#[derive(Debug)]
-pub enum GeneratorInteriorOrUpvar {
+pub enum CoroutineInteriorOrUpvar {
// span of interior type
Interior(Span, Option<(Span, Option<Span>)>),
// span of upvar
Upvar(Span),
}
-// This type provides a uniform interface to retrieve data on generators, whether it originated from
+// This type provides a uniform interface to retrieve data on coroutines, whether it originated from
// the local crate being compiled or from a foreign crate.
#[derive(Debug)]
-struct GeneratorData<'tcx, 'a>(&'a TypeckResults<'tcx>);
+struct CoroutineData<'tcx, 'a>(&'a TypeckResults<'tcx>);
-impl<'tcx, 'a> GeneratorData<'tcx, 'a> {
- /// Try to get information about variables captured by the generator that matches a type we are
+impl<'tcx, 'a> CoroutineData<'tcx, 'a> {
+ /// Try to get information about variables captured by the coroutine that matches a type we are
/// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to
/// meet an obligation
fn try_get_upvar_span<F>(
&self,
infer_context: &InferCtxt<'tcx>,
- generator_did: DefId,
+ coroutine_did: DefId,
ty_matches: F,
- ) -> Option<GeneratorInteriorOrUpvar>
+ ) -> Option<CoroutineInteriorOrUpvar>
where
F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,
{
- infer_context.tcx.upvars_mentioned(generator_did).and_then(|upvars| {
+ infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| {
upvars.iter().find_map(|(upvar_id, upvar)| {
let upvar_ty = self.0.node_type(*upvar_id);
let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty);
ty_matches(ty::Binder::dummy(upvar_ty))
- .then(|| GeneratorInteriorOrUpvar::Upvar(upvar.span))
+ .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span))
})
})
}
@@ -244,9 +245,9 @@ pub trait TypeErrCtxtExt<'tcx> {
fn note_obligation_cause_for_async_await(
&self,
err: &mut Diagnostic,
- interior_or_upvar_span: GeneratorInteriorOrUpvar,
+ interior_or_upvar_span: CoroutineInteriorOrUpvar,
is_async: bool,
- outer_generator: Option<DefId>,
+ outer_coroutine: Option<DefId>,
trait_pred: ty::TraitPredicate<'tcx>,
target_ty: Ty<'tcx>,
obligation: &PredicateObligation<'tcx>,
@@ -313,6 +314,18 @@ pub trait TypeErrCtxtExt<'tcx> {
predicate: ty::Predicate<'tcx>,
call_hir_id: HirId,
);
+
+ fn look_for_iterator_item_mistakes(
+ &self,
+ assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
+ typeck_results: &TypeckResults<'tcx>,
+ type_diffs: &[TypeError<'tcx>],
+ param_env: ty::ParamEnv<'tcx>,
+ path_segment: &hir::PathSegment<'_>,
+ args: &[hir::Expr<'_>],
+ err: &mut Diagnostic,
+ );
+
fn point_at_chain(
&self,
expr: &hir::Expr<'_>,
@@ -321,6 +334,7 @@ pub trait TypeErrCtxtExt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
err: &mut Diagnostic,
);
+
fn probe_assoc_types_at_expr(
&self,
type_diffs: &[TypeError<'tcx>],
@@ -364,7 +378,7 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -
/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
/// it can also be an `impl Trait` param that needs to be decomposed to a type
/// param for cleaner code.
-fn suggest_restriction<'tcx>(
+pub fn suggest_restriction<'tcx>(
tcx: TyCtxt<'tcx>,
item_id: LocalDefId,
hir_generics: &hir::Generics<'tcx>,
@@ -884,7 +898,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return false;
}
- if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =
+ obligation.predicate.kind().skip_binder()
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
{
// Don't suggest calling to turn an unsized type into a sized type
@@ -1156,15 +1171,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be args[1]
&& let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
+ {
+ Some((
+ DefIdOrName::DefId(def_id),
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
},
)
}
@@ -1174,43 +1189,43 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
// for existential projection, args are shifted over by 1
&& let ty::Tuple(args) = proj.args.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
+ {
+ Some((
+ DefIdOrName::Name("trait object"),
+ pred.rebind(proj.term.ty().unwrap()),
+ pred.rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
})
}
ty::Param(param) => {
let generics = self.tcx.generics_of(body_id);
let name = if generics.count() > param.index as usize
- && let def = generics.param_at(param.index as usize, self.tcx)
- && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
- && def.name == param.name
- {
- DefIdOrName::DefId(def.def_id)
- } else {
- DefIdOrName::Name("type parameter")
- };
+ && let def = generics.param_at(param.index as usize, self.tcx)
+ && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
+ && def.name == param.name
+ {
+ DefIdOrName::DefId(def.def_id)
+ } else {
+ DefIdOrName::Name("type parameter")
+ };
param_env.caller_bounds().iter().find_map(|pred| {
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
// args tuple will always be args[1]
&& let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind()
- {
- Some((
- name,
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
+ {
+ Some((
+ name,
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
})
}
_ => None,
@@ -1316,7 +1331,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);
let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) =
- if let ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code()
+ if let ObligationCauseCode::ItemObligation(_)
+ | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code()
&& let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()
{
(
@@ -1618,7 +1634,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
let hir = self.tcx.hir();
- if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) = obligation.cause.code().peel_derives()
+ if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) =
+ obligation.cause.code().peel_derives()
&& let hir::Node::Expr(expr) = hir.get(*hir_id)
{
// FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()`
@@ -1628,9 +1645,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// use nth(1) to skip one layer of desugaring from `IntoIter::into_iter`
if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1)
- && let Some(expr_span) = expr.span.find_ancestor_inside(await_expr.span)
+ && let Some(expr_span) = expr.span.find_ancestor_inside_same_ctxt(await_expr.span)
{
- let removal_span = self.tcx
+ let removal_span = self
+ .tcx
.sess
.source_map()
.span_extend_while(expr_span, char::is_whitespace)
@@ -1654,30 +1672,28 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.span_label(*span, format!("this call returns `{}`", pred.self_ty()));
}
if let Some(typeck_results) = &self.typeck_results
- && let ty = typeck_results.expr_ty_adjusted(base)
- && let ty::FnDef(def_id, _args) = ty.kind()
- && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
- hir.get_if_local(*def_id)
- {
- let msg = format!(
- "alternatively, consider making `fn {ident}` asynchronous"
+ && let ty = typeck_results.expr_ty_adjusted(base)
+ && let ty::FnDef(def_id, _args) = ty.kind()
+ && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
+ hir.get_if_local(*def_id)
+ {
+ let msg = format!("alternatively, consider making `fn {ident}` asynchronous");
+ if vis_span.is_empty() {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ msg,
+ "async ",
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ vis_span.shrink_to_hi(),
+ msg,
+ " async",
+ Applicability::MaybeIncorrect,
);
- if vis_span.is_empty() {
- err.span_suggestion_verbose(
- span.shrink_to_lo(),
- msg,
- "async ",
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_suggestion_verbose(
- vis_span.shrink_to_hi(),
- msg,
- " async",
- Applicability::MaybeIncorrect,
- );
- }
}
+ }
}
}
}
@@ -1791,13 +1807,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"this expression has type `{}`, which implements `{}`",
ty,
trait_pred.print_modifiers_and_trait_path()
- )
+ ),
);
err.span_suggestion(
self.tcx.sess.source_map().end_point(stmt.span),
"remove this semicolon",
"",
- Applicability::MachineApplicable
+ Applicability::MachineApplicable,
);
return true;
}
@@ -1856,14 +1872,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut sugg =
vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())];
sugg.extend(visitor.returns.into_iter().flat_map(|expr| {
- let span = expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span);
+ let span =
+ expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span);
if !span.can_be_used_for_suggestions() {
vec![]
} else if let hir::ExprKind::Call(path, ..) = expr.kind
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind
&& method.ident.name == sym::new
&& let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind
- && box_path.res.opt_def_id().is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box())
+ && box_path
+ .res
+ .opt_def_id()
+ .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box())
{
// Don't box `Box::new`
vec![]
@@ -1963,7 +1983,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let argument_kind = match expected.skip_binder().self_ty().kind() {
ty::Closure(..) => "closure",
- ty::Generator(..) => "generator",
+ ty::Coroutine(..) => "coroutine",
_ => "function",
};
let mut err = struct_span_err!(
@@ -2013,15 +2033,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
let expected_self =
self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty()));
- let expected_args = self
- .tcx
- .anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args));
+ let expected_args =
+ self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args));
// Find another predicate whose self-type is equal to the expected self type,
// but whose args don't match.
- let other_pred = predicates.into_iter()
- .enumerate()
- .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
+ let other_pred = predicates.into_iter().enumerate().find(|(other_idx, (pred, _))| {
+ match pred.kind().skip_binder() {
ty::ClauseKind::Trait(trait_pred)
if self.tcx.is_fn_trait(trait_pred.def_id())
&& other_idx != idx
@@ -2040,7 +2058,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
true
}
_ => false,
- });
+ }
+ });
// If we found one, then it's very likely the cause of the error.
if let Some((_, (_, other_pred_span))) = other_pred {
err.span_note(
@@ -2126,33 +2145,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let hir = self.tcx.hir();
// Attempt to detect an async-await error by looking at the obligation causes, looking
- // for a generator to be present.
+ // for a coroutine to be present.
//
// When a future does not implement a trait because of a captured type in one of the
- // generators somewhere in the call stack, then the result is a chain of obligations.
+ // coroutines somewhere in the call stack, then the result is a chain of obligations.
//
// Given an `async fn` A that calls an `async fn` B which captures a non-send type and that
// future is passed as an argument to a function C which requires a `Send` type, then the
// chain looks something like this:
//
- // - `BuiltinDerivedObligation` with a generator witness (B)
- // - `BuiltinDerivedObligation` with a generator (B)
+ // - `BuiltinDerivedObligation` with a coroutine witness (B)
+ // - `BuiltinDerivedObligation` with a coroutine (B)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (B)
- // - `BuiltinDerivedObligation` with a generator witness (A)
- // - `BuiltinDerivedObligation` with a generator (A)
+ // - `BuiltinDerivedObligation` with a coroutine witness (A)
+ // - `BuiltinDerivedObligation` with a coroutine (A)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
// - `BindingObligation` with `impl_send` (Send requirement)
//
- // The first obligation in the chain is the most useful and has the generator that captured
- // the type. The last generator (`outer_generator` below) has information about where the
- // bound was introduced. At least one generator should be present for this diagnostic to be
+ // The first obligation in the chain is the most useful and has the coroutine that captured
+ // the type. The last coroutine (`outer_coroutine` below) has information about where the
+ // bound was introduced. At least one coroutine should be present for this diagnostic to be
// modified.
let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())),
_ => (None, None),
};
- let mut generator = None;
- let mut outer_generator = None;
+ let mut coroutine = None;
+ let mut outer_coroutine = None;
let mut next_code = Some(obligation.cause.code());
let mut seen_upvar_tys_infer_tuple = false;
@@ -2172,18 +2191,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
match *ty.kind() {
- ty::Generator(did, ..) | ty::GeneratorWitness(did, _) => {
- generator = generator.or(Some(did));
- outer_generator = Some(did);
+ ty::Coroutine(did, ..) | ty::CoroutineWitness(did, _) => {
+ coroutine = coroutine.or(Some(did));
+ outer_coroutine = Some(did);
}
ty::Tuple(_) if !seen_upvar_tys_infer_tuple => {
// By introducing a tuple of upvar types into the chain of obligations
- // of a generator, the first non-generator item is now the tuple itself,
+ // of a coroutine, the first non-coroutine item is now the tuple itself,
// we shall ignore this.
seen_upvar_tys_infer_tuple = true;
}
- _ if generator.is_none() => {
+ _ if coroutine.is_none() => {
trait_ref = Some(cause.derived.parent_trait_pred.skip_binder());
target_ty = Some(ty);
}
@@ -2201,18 +2220,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
match *ty.kind() {
- ty::Generator(did, ..) | ty::GeneratorWitness(did, ..) => {
- generator = generator.or(Some(did));
- outer_generator = Some(did);
+ ty::Coroutine(did, ..) | ty::CoroutineWitness(did, ..) => {
+ coroutine = coroutine.or(Some(did));
+ outer_coroutine = Some(did);
}
ty::Tuple(_) if !seen_upvar_tys_infer_tuple => {
// By introducing a tuple of upvar types into the chain of obligations
- // of a generator, the first non-generator item is now the tuple itself,
+ // of a coroutine, the first non-coroutine item is now the tuple itself,
// we shall ignore this.
seen_upvar_tys_infer_tuple = true;
}
- _ if generator.is_none() => {
+ _ if coroutine.is_none() => {
trait_ref = Some(derived_obligation.parent_trait_pred.skip_binder());
target_ty = Some(ty);
}
@@ -2225,48 +2244,48 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- // Only continue if a generator was found.
- debug!(?generator, ?trait_ref, ?target_ty);
- let (Some(generator_did), Some(trait_ref), Some(target_ty)) =
- (generator, trait_ref, target_ty)
+ // Only continue if a coroutine was found.
+ debug!(?coroutine, ?trait_ref, ?target_ty);
+ let (Some(coroutine_did), Some(trait_ref), Some(target_ty)) =
+ (coroutine, trait_ref, target_ty)
else {
return false;
};
- let span = self.tcx.def_span(generator_did);
+ let span = self.tcx.def_span(coroutine_did);
- let generator_did_root = self.tcx.typeck_root_def_id(generator_did);
+ let coroutine_did_root = self.tcx.typeck_root_def_id(coroutine_did);
debug!(
- ?generator_did,
- ?generator_did_root,
+ ?coroutine_did,
+ ?coroutine_did_root,
typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner),
?span,
);
- let generator_body = generator_did
+ let coroutine_body = coroutine_did
.as_local()
.and_then(|def_id| hir.maybe_body_owned_by(def_id))
.map(|body_id| hir.body(body_id));
let mut visitor = AwaitsVisitor::default();
- if let Some(body) = generator_body {
+ if let Some(body) = coroutine_body {
visitor.visit_body(body);
}
debug!(awaits = ?visitor.awaits);
- // Look for a type inside the generator interior that matches the target type to get
+ // Look for a type inside the coroutine interior that matches the target type to get
// a span.
let target_ty_erased = self.tcx.erase_regions(target_ty);
let ty_matches = |ty| -> bool {
// Careful: the regions for types that appear in the
- // generator interior are not generally known, so we
+ // coroutine interior are not generally known, so we
// want to erase them when comparing (and anyway,
// `Send` and other bounds are generally unaffected by
// the choice of region). When erasing regions, we
// also have to erase late-bound regions. This is
- // because the types that appear in the generator
+ // because the types that appear in the coroutine
// interior generally contain "bound regions" to
// represent regions that are part of the suspended
- // generator frame. Bound regions are preserved by
+ // coroutine frame. Bound regions are preserved by
// `erase_regions` and so we must also call
// `erase_late_bound_regions`.
let ty_erased = self.tcx.erase_late_bound_regions(ty);
@@ -2276,44 +2295,44 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
eq
};
- // Get the typeck results from the infcx if the generator is the function we are currently
+ // Get the typeck results from the infcx if the coroutine is the function we are currently
// type-checking; otherwise, get them by performing a query. This is needed to avoid
- // cycles. If we can't use resolved types because the generator comes from another crate,
+ // cycles. If we can't use resolved types because the coroutine comes from another crate,
// we still provide a targeted error but without all the relevant spans.
- let generator_data = match &self.typeck_results {
- Some(t) if t.hir_owner.to_def_id() == generator_did_root => GeneratorData(&t),
- _ if generator_did.is_local() => {
- GeneratorData(self.tcx.typeck(generator_did.expect_local()))
+ let coroutine_data = match &self.typeck_results {
+ Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(&t),
+ _ if coroutine_did.is_local() => {
+ CoroutineData(self.tcx.typeck(coroutine_did.expect_local()))
}
_ => return false,
};
- let generator_within_in_progress_typeck = match &self.typeck_results {
- Some(t) => t.hir_owner.to_def_id() == generator_did_root,
+ let coroutine_within_in_progress_typeck = match &self.typeck_results {
+ Some(t) => t.hir_owner.to_def_id() == coroutine_did_root,
_ => false,
};
let mut interior_or_upvar_span = None;
- let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches);
+ let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, hir, ty_matches);
debug!(?from_awaited_ty);
// Avoid disclosing internal information to downstream crates.
- if generator_did.is_local()
+ if coroutine_did.is_local()
// Try to avoid cycles.
- && !generator_within_in_progress_typeck
- && let Some(generator_info) = self.tcx.mir_generator_witnesses(generator_did)
+ && !coroutine_within_in_progress_typeck
+ && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did)
{
- debug!(?generator_info);
+ debug!(?coroutine_info);
'find_source: for (variant, source_info) in
- generator_info.variant_fields.iter().zip(&generator_info.variant_source_info)
+ coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info)
{
debug!(?variant);
for &local in variant {
- let decl = &generator_info.field_tys[local];
+ let decl = &coroutine_info.field_tys[local];
debug!(?decl);
if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits {
- interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(
+ interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(
decl.source_info.span,
Some((source_info.span, from_awaited_ty)),
));
@@ -2325,21 +2344,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if interior_or_upvar_span.is_none() {
interior_or_upvar_span =
- generator_data.try_get_upvar_span(&self, generator_did, ty_matches);
+ coroutine_data.try_get_upvar_span(&self, coroutine_did, ty_matches);
}
- if interior_or_upvar_span.is_none() && !generator_did.is_local() {
- interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span, None));
+ if interior_or_upvar_span.is_none() && !coroutine_did.is_local() {
+ interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(span, None));
}
debug!(?interior_or_upvar_span);
if let Some(interior_or_upvar_span) = interior_or_upvar_span {
- let is_async = self.tcx.generator_is_async(generator_did);
+ let is_async = self.tcx.coroutine_is_async(coroutine_did);
self.note_obligation_cause_for_async_await(
err,
interior_or_upvar_span,
is_async,
- outer_generator,
+ outer_coroutine,
trait_ref,
target_ty,
obligation,
@@ -2357,9 +2376,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn note_obligation_cause_for_async_await(
&self,
err: &mut Diagnostic,
- interior_or_upvar_span: GeneratorInteriorOrUpvar,
+ interior_or_upvar_span: CoroutineInteriorOrUpvar,
is_async: bool,
- outer_generator: Option<DefId>,
+ outer_coroutine: Option<DefId>,
trait_pred: ty::TraitPredicate<'tcx>,
target_ty: Ty<'tcx>,
obligation: &PredicateObligation<'tcx>,
@@ -2369,7 +2388,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let (await_or_yield, an_await_or_yield) =
if is_async { ("await", "an await") } else { ("yield", "a yield") };
- let future_or_generator = if is_async { "future" } else { "generator" };
+ let future_or_coroutine = if is_async { "future" } else { "coroutine" };
// Special case the primary error message when send or sync is the trait that was
// not implemented.
@@ -2382,34 +2401,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.clear_code();
err.set_primary_message(format!(
- "{future_or_generator} cannot be {trait_verb} between threads safely"
+ "{future_or_coroutine} cannot be {trait_verb} between threads safely"
));
let original_span = err.span.primary_span().unwrap();
let mut span = MultiSpan::from_span(original_span);
- let message = outer_generator
- .and_then(|generator_did| {
- Some(match self.tcx.generator_kind(generator_did).unwrap() {
- GeneratorKind::Gen => format!("generator is not {trait_name}"),
- GeneratorKind::Async(AsyncGeneratorKind::Fn) => self
+ let message = outer_coroutine
+ .and_then(|coroutine_did| {
+ Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() {
+ CoroutineKind::Coroutine => format!("coroutine is not {trait_name}"),
+ CoroutineKind::Async(CoroutineSource::Fn) => self
.tcx
- .parent(generator_did)
+ .parent(coroutine_did)
.as_local()
.map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
.map(|name| {
format!("future returned by `{name}` is not {trait_name}")
})?,
- GeneratorKind::Async(AsyncGeneratorKind::Block) => {
+ CoroutineKind::Async(CoroutineSource::Block) => {
format!("future created by async block is not {trait_name}")
}
- GeneratorKind::Async(AsyncGeneratorKind::Closure) => {
+ CoroutineKind::Async(CoroutineSource::Closure) => {
format!("future created by async closure is not {trait_name}")
}
+ CoroutineKind::Gen(CoroutineSource::Fn) => self
+ .tcx
+ .parent(coroutine_did)
+ .as_local()
+ .map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
+ .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
+ .map(|name| {
+ format!("iterator returned by `{name}` is not {trait_name}")
+ })?,
+ CoroutineKind::Gen(CoroutineSource::Block) => {
+ format!("iterator created by gen block is not {trait_name}")
+ }
+ CoroutineKind::Gen(CoroutineSource::Closure) => {
+ format!("iterator created by gen closure is not {trait_name}")
+ }
})
})
- .unwrap_or_else(|| format!("{future_or_generator} is not {trait_name}"));
+ .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}"));
span.push_span_label(original_span, message);
err.set_span(span);
@@ -2453,11 +2487,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
err.span_note(
span,
- format!("{future_or_generator} {trait_explanation} as this value is used across {an_await_or_yield}"),
+ format!("{future_or_coroutine} {trait_explanation} as this value is used across {an_await_or_yield}"),
);
};
match interior_or_upvar_span {
- GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => {
+ CoroutineInteriorOrUpvar::Interior(interior_span, interior_extra_info) => {
if let Some((yield_span, from_awaited_ty)) = interior_extra_info {
if let Some(await_span) = from_awaited_ty {
// The type causing this obligation is one being awaited at await_span.
@@ -2480,7 +2514,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
}
- GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
+ CoroutineInteriorOrUpvar::Upvar(upvar_span) => {
// `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send`
let non_send = match target_ty.kind() {
ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) {
@@ -2647,9 +2681,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Check for foreign traits being reachable.
self.tcx.visible_parent_map(()).get(&def_id).is_some()
};
- if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item {
- // FIXME(estebank): extend this to search for all the types that do
- // implement this trait and list them.
+ if Some(def_id) == self.tcx.lang_items().sized_trait()
+ && let Some(hir::Node::TraitItem(hir::TraitItem {
+ ident,
+ kind: hir::TraitItemKind::Type(bounds, None),
+ ..
+ })) = tcx.hir().get_if_local(item_def_id)
+ // Do not suggest relaxing if there is an explicit `Sized` obligation.
+ && !bounds.iter()
+ .filter_map(|bound| bound.trait_ref())
+ .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait())
+ {
+ let (span, separator) = if let [.., last] = bounds {
+ (last.span().shrink_to_hi(), " +")
+ } else {
+ (ident.span.shrink_to_hi(), ":")
+ };
+ err.span_suggestion_verbose(
+ span,
+ "consider relaxing the implicit `Sized` restriction",
+ format!("{separator} ?Sized"),
+ Applicability::MachineApplicable,
+ );
+ }
+ if let DefKind::Trait = tcx.def_kind(item_def_id)
+ && !visible_item
+ {
err.note(format!(
"`{short_item_name}` is a \"sealed trait\", because to implement \
it you also need to implement `{}`, which is not accessible; \
@@ -2657,6 +2714,34 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
types that already implement it",
with_no_trimmed_paths!(tcx.def_path_str(def_id)),
));
+ let impls_of = tcx.trait_impls_of(def_id);
+ let impls = impls_of
+ .non_blanket_impls()
+ .values()
+ .flatten()
+ .chain(impls_of.blanket_impls().iter())
+ .collect::<Vec<_>>();
+ if !impls.is_empty() {
+ let len = impls.len();
+ let mut types = impls.iter()
+ .map(|t| with_no_trimmed_paths!(format!(
+ " {}",
+ tcx.type_of(*t).instantiate_identity(),
+ )))
+ .collect::<Vec<_>>();
+ let post = if types.len() > 9 {
+ types.truncate(8);
+ format!("\nand {} others", len - 8)
+ } else {
+ String::new()
+ };
+ err.help(format!(
+ "the following type{} implement{} the trait:\n{}{post}",
+ pluralize!(len),
+ if len == 1 { "s" } else { "" },
+ types.join("\n"),
+ ));
+ }
}
}
} else {
@@ -2684,20 +2769,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
));
}
}
- ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
+ ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => {
err.note(
"the `Copy` trait is required because this value will be copied for each element of the array",
);
-
- if is_const_fn {
- err.help(
- "consider creating a new `const` item and initializing it with the result \
- of the function call to be used in the repeat position, like \
- `const VAL: Type = const_fn();` and `let x = [VAL; 42];`",
- );
+ let value_kind = match is_constable {
+ IsConstable::Fn => Some("the result of the function call"),
+ IsConstable::Ctor => Some("the result of the constructor"),
+ _ => None
+ };
+ let sm = tcx.sess.source_map();
+ if let Some(value_kind) = value_kind &&
+ let Ok(snip) = sm.span_to_snippet(elt_span)
+ {
+ let help_msg = format!(
+ "consider creating a new `const` item and initializing it with {value_kind} \
+ to be used in the repeat position");
+ let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default();
+ err.multipart_suggestion(help_msg, vec![
+ (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")),
+ (elt_span, "ARRAY_REPEAT_VALUE".to_string())
+ ], Applicability::MachineApplicable);
}
- if self.tcx.sess.is_nightly_build() && is_const_fn {
+ if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) {
err.help(
"create an inline `const` block, see RFC #2920 \
<https://github.com/rust-lang/rfcs/pull/2920> for more information",
@@ -2754,7 +2849,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& let ty::ClauseKind::Trait(trait_pred) = clause
&& let ty::Dynamic(..) = trait_pred.self_ty().kind()
{
- let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+ let span = if let Ok(snippet) =
+ self.tcx.sess.source_map().span_to_snippet(span)
&& snippet.starts_with("dyn ")
{
let pos = snippet.len() - snippet[3..].trim_start().len();
@@ -2789,7 +2885,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note("the return type of a function must have a statically known size");
}
ObligationCauseCode::SizedYieldType => {
- err.note("the yield type of a generator must have a statically known size");
+ err.note("the yield type of a coroutine must have a statically known size");
}
ObligationCauseCode::AssignmentLhsSized => {
err.note("the left-hand-side of an assignment must have a statically known size");
@@ -2845,22 +2941,28 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note("all inline asm arguments must have a statically known size");
}
ObligationCauseCode::SizedClosureCapture(closure_def_id) => {
- err.note("all values captured by value by a closure must have a statically known size");
- let hir::ExprKind::Closure(closure) = self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind else {
+ err.note(
+ "all values captured by value by a closure must have a statically known size",
+ );
+ let hir::ExprKind::Closure(closure) =
+ self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind
+ else {
bug!("expected closure in SizedClosureCapture obligation");
};
- if let hir::CaptureBy::Value = closure.capture_clause
+ if let hir::CaptureBy::Value { .. } = closure.capture_clause
&& let Some(span) = closure.fn_arg_span
{
err.span_label(span, "this closure captures all values by move");
}
}
- ObligationCauseCode::SizedGeneratorInterior(generator_def_id) => {
- let what = match self.tcx.generator_kind(generator_def_id) {
- None | Some(hir::GeneratorKind::Gen) => "yield",
- Some(hir::GeneratorKind::Async(..)) => "await",
+ ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => {
+ let what = match self.tcx.coroutine_kind(coroutine_def_id) {
+ None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield",
+ Some(hir::CoroutineKind::Async(..)) => "await",
};
- err.note(format!("all values live across `{what}` must have a statically known size"));
+ err.note(format!(
+ "all values live across `{what}` must have a statically known size"
+ ));
}
ObligationCauseCode::ConstPatternStructural => {
err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`");
@@ -2878,7 +2980,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}
- // If the obligation for a tuple is set directly by a Generator or Closure,
+ // If the obligation for a tuple is set directly by a Coroutine or Closure,
// then the tuple must be the one containing capture types.
let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) {
false
@@ -2888,7 +2990,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let parent_trait_ref =
self.resolve_vars_if_possible(data.parent_trait_pred);
let nested_ty = parent_trait_ref.skip_binder().self_ty();
- matches!(nested_ty.kind(), ty::Generator(..))
+ matches!(nested_ty.kind(), ty::Coroutine(..))
|| matches!(nested_ty.kind(), ty::Closure(..))
} else {
false
@@ -2908,7 +3010,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
},
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
// If the previous type is async fn, this is the future generated by the body of an async function.
- // Avoid printing it twice (it was already printed in the `ty::Generator` arm below).
+ // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below).
let is_future = tcx.ty_is_opaque_future(ty);
debug!(
?obligated_types,
@@ -2917,8 +3019,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
if is_future
&& obligated_types.last().is_some_and(|ty| match ty.kind() {
- ty::Generator(last_def_id, ..) => {
- tcx.generator_is_async(*last_def_id)
+ ty::Coroutine(last_def_id, ..) => {
+ tcx.coroutine_is_async(*last_def_id)
}
_ => false,
})
@@ -2927,7 +3029,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
err.span_note(self.tcx.def_span(def_id), msg)
}
- ty::GeneratorWitness(def_id, args) => {
+ ty::CoroutineWitness(def_id, args) => {
use std::fmt::Write;
// FIXME: this is kind of an unusual format for rustc, can we make it more clear?
@@ -2935,21 +3037,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// FIXME: only print types which don't meet the trait requirement
let mut msg =
"required because it captures the following types: ".to_owned();
- for bty in tcx.generator_hidden_types(*def_id) {
+ for bty in tcx.coroutine_hidden_types(*def_id) {
let ty = bty.instantiate(tcx, args);
write!(msg, "`{ty}`, ").unwrap();
}
err.note(msg.trim_end_matches(", ").to_string())
}
- ty::Generator(def_id, _, _) => {
+ ty::Coroutine(def_id, _, _) => {
let sp = self.tcx.def_span(def_id);
- // Special-case this to say "async block" instead of `[static generator]`.
- let kind = tcx.generator_kind(def_id).unwrap().descr();
+ // Special-case this to say "async block" instead of `[static coroutine]`.
+ let kind = tcx.coroutine_kind(def_id).unwrap();
err.span_note(
sp,
with_forced_trimmed_paths!(format!(
- "required because it's used within this {kind}",
+ "required because it's used within this {kind:#}",
)),
)
}
@@ -3244,7 +3346,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) {
if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) {
let body = self.tcx.hir().body(body_id);
- if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
+ if let Some(hir::CoroutineKind::Async(_)) = body.coroutine_kind {
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
@@ -3385,15 +3487,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code()
- && self.tcx.is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id)
+ && self
+ .tcx
+ .is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id)
&& let ty::Slice(_) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
&& let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind()
&& let ty::Uint(ty::UintTy::Usize) = inner_ty.kind()
{
err.span_suggestion_verbose(
obligation.cause.span.shrink_to_lo(),
- "dereference this index",
- '*',
+ "dereference this index",
+ '*',
Applicability::MachineApplicable,
);
}
@@ -3413,10 +3517,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
&& let Some(typeck_results) = &self.typeck_results
{
- if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
- let expr = expr.peel_blocks();
- let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx,));
- let span = expr.span;
+ if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
+ let inner_expr = expr.peel_blocks();
+ let ty = typeck_results.expr_ty_adjusted_opt(inner_expr)
+ .unwrap_or(Ty::new_misc_error(tcx));
+ let span = inner_expr.span;
if Some(span) != err.span.primary_span() {
err.span_label(
span,
@@ -3427,6 +3532,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
format!("this tail expression is of type `{ty}`")
},
);
+ if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
+ && let ty::ClauseKind::Trait(pred) = clause
+ && [
+ tcx.lang_items().fn_once_trait(),
+ tcx.lang_items().fn_mut_trait(),
+ tcx.lang_items().fn_trait(),
+ ].contains(&Some(pred.def_id()))
+ {
+ if let [stmt, ..] = block.stmts
+ && let hir::StmtKind::Semi(value) = stmt.kind
+ && let hir::ExprKind::Closure(hir::Closure {
+ body,
+ fn_decl_span,
+ ..
+ }) = value.kind
+ && let body = hir.body(*body)
+ && !matches!(body.value.kind, hir::ExprKind::Block(..))
+ {
+ // Check if the failed predicate was an expectation of a closure type
+ // and if there might have been a `{ |args|` typo instead of `|args| {`.
+ err.multipart_suggestion(
+ "you might have meant to open the closure body instead of placing \
+ a closure within a block",
+ vec![
+ (expr.span.with_hi(value.span.lo()), String::new()),
+ (fn_decl_span.shrink_to_hi(), " {".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ // Maybe the bare block was meant to be a closure.
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_lo(),
+ "you might have meant to create the closure instead of a block",
+ format!(
+ "|{}| ",
+ (0..pred.trait_ref.args.len() - 1).map(|_| "_")
+ .collect::<Vec<_>>()
+ .join(", ")),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
}
}
@@ -3437,7 +3585,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut type_diffs = vec![];
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code
&& let Some(node_args) = typeck_results.node_args_opt(call_hir_id)
- && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args)
+ && let where_clauses =
+ self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args)
&& let Some(where_pred) = where_clauses.predicates.get(*idx)
{
if let Some(where_pred) = where_pred.as_trait_clause()
@@ -3447,32 +3596,34 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let failed_pred = self.instantiate_binder_with_fresh_vars(
expr.span,
LateBoundRegionConversionTime::FnCall,
- failed_pred
+ failed_pred,
);
- let zipped =
- iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
+ let zipped = iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
for (expected, actual) in zipped {
self.probe(|_| {
- match self
- .at(&ObligationCause::misc(expr.span, body_id), param_env)
- .eq(DefineOpaqueTypes::No, expected, actual)
- {
+ match self.at(&ObligationCause::misc(expr.span, body_id), param_env).eq(
+ DefineOpaqueTypes::No,
+ expected,
+ actual,
+ ) {
Ok(_) => (), // We ignore nested obligations here for now.
Err(err) => type_diffs.push(err),
}
})
- };
+ }
} else if let Some(where_pred) = where_pred.as_projection_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
&& let Some(found) = failed_pred.skip_binder().term.ty()
{
- type_diffs = vec![
- Sorts(ty::error::ExpectedFound {
- expected: Ty::new_alias(self.tcx,ty::Projection, where_pred.skip_binder().projection_ty),
- found,
- }),
- ];
+ type_diffs = vec![Sorts(ty::error::ExpectedFound {
+ expected: Ty::new_alias(
+ self.tcx,
+ ty::Projection,
+ where_pred.skip_binder().projection_ty,
+ ),
+ found,
+ })];
}
}
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
@@ -3586,12 +3737,115 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
msg,
sugg,
Applicability::MaybeIncorrect,
- SuggestionStyle::ShowAlways
+ SuggestionStyle::ShowAlways,
);
}
}
}
+ fn look_for_iterator_item_mistakes(
+ &self,
+ assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
+ typeck_results: &TypeckResults<'tcx>,
+ type_diffs: &[TypeError<'tcx>],
+ param_env: ty::ParamEnv<'tcx>,
+ path_segment: &hir::PathSegment<'_>,
+ args: &[hir::Expr<'_>],
+ err: &mut Diagnostic,
+ ) {
+ let tcx = self.tcx;
+ // Special case for iterator chains, we look at potential failures of `Iterator::Item`
+ // not being `: Clone` and `Iterator::map` calls with spurious trailing `;`.
+ for entry in assocs_in_this_method {
+ let Some((_span, (def_id, ty))) = entry else {
+ continue;
+ };
+ for diff in type_diffs {
+ let Sorts(expected_found) = diff else {
+ continue;
+ };
+ if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
+ && path_segment.ident.name == sym::map
+ && self.can_eq(param_env, expected_found.found, *ty)
+ && let [arg] = args
+ && let hir::ExprKind::Closure(closure) = arg.kind
+ {
+ let body = tcx.hir().body(closure.body);
+ if let hir::ExprKind::Block(block, None) = body.value.kind
+ && let None = block.expr
+ && let [.., stmt] = block.stmts
+ && let hir::StmtKind::Semi(expr) = stmt.kind
+ // FIXME: actually check the expected vs found types, but right now
+ // the expected is a projection that we need to resolve.
+ // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr)
+ && expected_found.found.is_unit()
+ {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi().with_hi(stmt.span.hi()),
+ "consider removing this semicolon",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ let expr = if let hir::ExprKind::Block(block, None) = body.value.kind
+ && let Some(expr) = block.expr
+ {
+ expr
+ } else {
+ body.value
+ };
+ if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind
+ && path_segment.ident.name == sym::clone
+ && let Some(expr_ty) = typeck_results.expr_ty_opt(expr)
+ && let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr)
+ && self.can_eq(param_env, expr_ty, rcvr_ty)
+ && let ty::Ref(_, ty, _) = expr_ty.kind()
+ {
+ err.span_label(
+ span,
+ format!(
+ "this method call is cloning the reference `{expr_ty}`, not \
+ `{ty}` which doesn't implement `Clone`",
+ ),
+ );
+ let ty::Param(..) = ty.kind() else {
+ continue;
+ };
+ let hir = tcx.hir();
+ let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id);
+
+ let pred = ty::Binder::dummy(ty::TraitPredicate {
+ trait_ref: ty::TraitRef::from_lang_item(
+ tcx,
+ LangItem::Clone,
+ span,
+ [*ty],
+ ),
+ polarity: ty::ImplPolarity::Positive,
+ });
+ let Some(generics) = node.generics() else {
+ continue;
+ };
+ let Some(body_id) = node.body_id() else {
+ continue;
+ };
+ suggest_restriction(
+ tcx,
+ hir.body_owner_def_id(body_id),
+ &generics,
+ &format!("type parameter `{ty}`"),
+ err,
+ node.fn_sig(),
+ None,
+ pred,
+ None,
+ );
+ }
+ }
+ }
+ }
+ }
+
fn point_at_chain(
&self,
expr: &hir::Expr<'_>,
@@ -3611,13 +3865,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut prev_ty = self.resolve_vars_if_possible(
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
- while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
+ while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
// Point at every method call in the chain with the resulting type.
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
// ^^^^^^ ^^^^^^^^^^^
expr = rcvr_expr;
let assocs_in_this_method =
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
+ self.look_for_iterator_item_mistakes(
+ &assocs_in_this_method,
+ typeck_results,
+ &type_diffs,
+ param_env,
+ path_segment,
+ args,
+ err,
+ );
assocs.push(assocs_in_this_method);
prev_ty = self.resolve_vars_if_possible(
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
@@ -3638,9 +3901,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let hir::Node::Param(param) = parent {
// ...and it is a an fn argument.
let prev_ty = self.resolve_vars_if_possible(
- typeck_results.node_type_opt(param.hir_id).unwrap_or(Ty::new_misc_error(tcx,)),
+ typeck_results
+ .node_type_opt(param.hir_id)
+ .unwrap_or(Ty::new_misc_error(tcx)),
+ );
+ let assocs_in_this_method = self.probe_assoc_types_at_expr(
+ &type_diffs,
+ param.ty_span,
+ prev_ty,
+ param.hir_id,
+ param_env,
);
- let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
if assocs_in_this_method.iter().any(|a| a.is_some()) {
assocs.push(assocs_in_this_method);
print_root_expr = false;
@@ -3651,7 +3922,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
// We want the type before deref coercions, otherwise we talk about `&[_]`
// instead of `Vec<_>`.
- if let Some(ty) = typeck_results.expr_ty_opt(expr) && print_root_expr {
+ if let Some(ty) = typeck_results.expr_ty_opt(expr)
+ && print_root_expr
+ {
let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
// Point at the root expression
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
@@ -3782,7 +4055,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// This corresponds to `<ExprTy as Iterator>::Item = _`.
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::ClauseKind::Projection(ty::ProjectionPredicate {
- projection_ty: self.tcx.mk_alias_ty(proj.def_id, args),
+ projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args),
term: ty_var.into(),
}),
));
@@ -4000,14 +4273,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// ... whose signature is `async` (i.e. this is an AFIT)
let (sig, body) = item.expect_fn();
- let hir::IsAsync::Async(async_span) = sig.header.asyncness else {
- return;
- };
- let Ok(async_span) =
- self.tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace())
- else {
- return;
- };
let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) =
sig.decl.output
else {
@@ -4021,55 +4286,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}
- let future = self.tcx.hir().item(*def).expect_opaque_ty();
- let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else {
- // `async fn` should always lower to a lang item bound... but don't ICE.
- return;
- };
- let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) =
- generics.bindings.get(0).map(|binding| binding.kind)
- else {
- // Also should never happen.
+ let Some(sugg) = suggest_desugaring_async_fn_to_impl_future_in_trait(
+ self.tcx,
+ *sig,
+ *body,
+ opaque_def_id.expect_local(),
+ &format!(" + {auto_trait}"),
+ ) else {
return;
};
let function_name = self.tcx.def_path_str(fn_def_id);
-
- let mut sugg = if future_output_ty.span.is_empty() {
- vec![
- (async_span, String::new()),
- (
- future_output_ty.span,
- format!(" -> impl std::future::Future<Output = ()> + {auto_trait}"),
- ),
- ]
- } else {
- vec![
- (
- future_output_ty.span.shrink_to_lo(),
- "impl std::future::Future<Output = ".to_owned(),
- ),
- (future_output_ty.span.shrink_to_hi(), format!("> + {auto_trait}")),
- (async_span, String::new()),
- ]
- };
-
- // If there's a body, we also need to wrap it in `async {}`
- if let hir::TraitFn::Provided(body) = body {
- let body = self.tcx.hir().body(*body);
- let body_span = body.value.span;
- let body_span_without_braces =
- body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1));
- if body_span_without_braces.is_empty() {
- sugg.push((body_span_without_braces, " async {} ".to_owned()));
- } else {
- sugg.extend([
- (body_span_without_braces.shrink_to_lo(), "async {".to_owned()),
- (body_span_without_braces.shrink_to_hi(), "} ".to_owned()),
- ]);
- }
- }
-
err.multipart_suggestion(
format!(
"`{auto_trait}` can be made part of the associated future's \
@@ -4150,7 +4377,9 @@ fn hint_missing_borrow<'tcx>(
let mut span = arg.span.shrink_to_lo();
let mut left = found_refs.len() - expected_refs.len();
let mut ty = arg;
- while let hir::TyKind::Ref(_, mut_ty) = &ty.kind && left > 0 {
+ while let hir::TyKind::Ref(_, mut_ty) = &ty.kind
+ && left > 0
+ {
span = span.with_hi(mut_ty.ty.span.lo());
ty = mut_ty.ty;
left -= 1;
@@ -4221,7 +4450,7 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
assert!(!self.in_block_tail);
- if body.generator_kind().is_none() {
+ if body.coroutine_kind().is_none() {
if let hir::ExprKind::Block(block, None) = body.value.kind {
if block.expr.is_some() {
self.in_block_tail = true;
@@ -4300,6 +4529,39 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> {
}
}
+pub(super) fn get_explanation_based_on_obligation<'tcx>(
+ obligation: &PredicateObligation<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+ pre_message: String,
+) -> String {
+ if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
+ "consider using `()`, or a `Result`".to_owned()
+ } else {
+ let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
+ ty::FnDef(_, _) => Some("fn item"),
+ ty::Closure(_, _) => Some("closure"),
+ _ => None,
+ };
+
+ match ty_desc {
+ Some(desc) => format!(
+ "{}the trait `{}` is not implemented for {} `{}`",
+ pre_message,
+ trait_predicate.print_modifiers_and_trait_path(),
+ desc,
+ trait_ref.skip_binder().self_ty(),
+ ),
+ None => format!(
+ "{}the trait `{}` is not implemented for `{}`",
+ pre_message,
+ trait_predicate.print_modifiers_and_trait_path(),
+ trait_ref.skip_binder().self_ty(),
+ ),
+ }
+ }
+}
+
// Replace `param` with `replace_ty`
struct ReplaceImplTraitFolder<'tcx> {
tcx: TyCtxt<'tcx>,
@@ -4321,3 +4583,65 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> {
self.tcx
}
}
+
+pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sig: hir::FnSig<'tcx>,
+ body: hir::TraitFn<'tcx>,
+ opaque_def_id: LocalDefId,
+ add_bounds: &str,
+) -> Option<Vec<(Span, String)>> {
+ let hir::IsAsync::Async(async_span) = sig.header.asyncness else {
+ return None;
+ };
+ let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace())
+ else {
+ return None;
+ };
+
+ let future = tcx.hir().get_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
+ let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else {
+ // `async fn` should always lower to a lang item bound... but don't ICE.
+ return None;
+ };
+ let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) =
+ generics.bindings.get(0).map(|binding| binding.kind)
+ else {
+ // Also should never happen.
+ return None;
+ };
+
+ let mut sugg = if future_output_ty.span.is_empty() {
+ vec![
+ (async_span, String::new()),
+ (
+ future_output_ty.span,
+ format!(" -> impl std::future::Future<Output = ()>{add_bounds}"),
+ ),
+ ]
+ } else {
+ vec![
+ (future_output_ty.span.shrink_to_lo(), "impl std::future::Future<Output = ".to_owned()),
+ (future_output_ty.span.shrink_to_hi(), format!(">{add_bounds}")),
+ (async_span, String::new()),
+ ]
+ };
+
+ // If there's a body, we also need to wrap it in `async {}`
+ if let hir::TraitFn::Provided(body) = body {
+ let body = tcx.hir().body(body);
+ let body_span = body.value.span;
+ let body_span_without_braces =
+ body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1));
+ if body_span_without_braces.is_empty() {
+ sugg.push((body_span_without_braces, " async {} ".to_owned()));
+ } else {
+ sugg.extend([
+ (body_span_without_braces.shrink_to_lo(), "async {".to_owned()),
+ (body_span_without_braces.shrink_to_hi(), "} ".to_owned()),
+ ]);
+ }
+ }
+
+ Some(sugg)
+}