Rollup merge of #107304 - Nilstrieb:ᐸTy as PartialEqᐳ::eq because what else are we gonna use in rustc_middle, r=compiler-errors

Use `can_eq` to compare types for default assoc type error

This correctly handles inference variables like `{integer}`. I had to move all of this `note_and_explain` code to `rustc_infer`, it made no sense for it to be in `rustc_middle` anyways.

The commits are reviewed separately.

Fixes #106968
This commit is contained in:
Matthias Krüger 2023-01-26 07:53:26 +01:00 committed by GitHub
commit 3aeafca070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 703 additions and 648 deletions

View File

@ -79,6 +79,7 @@ use std::path::PathBuf;
use std::{cmp, fmt, iter};
mod note;
mod note_and_explain;
mod suggest;
pub(crate) mod need_type_info;
@ -1846,7 +1847,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
self.check_and_note_conflicting_crates(diag, terr);
self.tcx.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values
&& let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind()

View File

@ -0,0 +1,654 @@
use super::TypeErrCtxt;
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, Diagnostic, MultiSpan};
use rustc_hir::{self as hir, def::DefKind};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::Printer;
use rustc_middle::{
traits::ObligationCause,
ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty},
};
use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub fn note_and_explain_type_err(
&self,
diag: &mut Diagnostic,
err: TypeError<'tcx>,
cause: &ObligationCause<'tcx>,
sp: Span,
body_owner_def_id: DefId,
) {
use ty::error::TypeError::*;
debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
let tcx = self.tcx;
match err {
ArgumentSorts(values, _) | Sorts(values) => {
match (values.expected.kind(), values.found.kind()) {
(ty::Closure(..), ty::Closure(..)) => {
diag.note("no two closures, even if identical, have the same type");
diag.help("consider boxing your closure and/or using it as a trait object");
}
(ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
// Issue #63167
diag.note("distinct uses of `impl Trait` result in different opaque types");
}
(ty::Float(_), ty::Infer(ty::IntVar(_)))
if let Ok(
// Issue #53280
snippet,
) = tcx.sess.source_map().span_to_snippet(sp) =>
{
if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
diag.span_suggestion(
sp,
"use a float literal",
format!("{}.0", snippet),
MachineApplicable,
);
}
}
(ty::Param(expected), ty::Param(found)) => {
let generics = tcx.generics_of(body_owner_def_id);
let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
if !sp.contains(e_span) {
diag.span_label(e_span, "expected type parameter");
}
let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
if !sp.contains(f_span) {
diag.span_label(f_span, "found type parameter");
}
diag.note(
"a type parameter was expected, but a different one was found; \
you might be missing a type parameter or trait bound",
);
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch10-02-traits.html\
#traits-as-parameters",
);
}
(ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
diag.note("an associated type was expected, but a different one was found");
}
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
{
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
let hir = tcx.hir();
let mut note = true;
if let Some(generics) = generics
.type_param(p, tcx)
.def_id
.as_local()
.map(|id| hir.local_def_id_to_hir_id(id))
.and_then(|id| tcx.hir().find_parent(id))
.as_ref()
.and_then(|node| node.generics())
{
// Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics.
let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
let path =
tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
let item_name = tcx.item_name(proj.def_id);
let item_args = self.format_generic_args(assoc_substs);
let path = if path.ends_with('>') {
format!(
"{}, {}{} = {}>",
&path[..path.len() - 1],
item_name,
item_args,
p
)
} else {
format!("{}<{}{} = {}>", path, item_name, item_args, p)
};
note = !suggest_constraining_type_param(
tcx,
generics,
diag,
&format!("{}", proj.self_ty()),
&path,
None,
);
}
if note {
diag.note("you might be missing a type parameter or trait bound");
}
}
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
diag.help("type parameters must be constrained to match other types");
if tcx.sess.teach(&diag.get_code().unwrap()) {
diag.help(
"given a type parameter `T` and a method `foo`:
```
trait Trait<T> { fn foo(&tcx) -> T; }
```
the only ways to implement method `foo` are:
- constrain `T` with an explicit type:
```
impl Trait<String> for X {
fn foo(&tcx) -> String { String::new() }
}
```
- add a trait bound to `T` and call a method on that trait that returns `Self`:
```
impl<T: std::default::Default> Trait<T> for X {
fn foo(&tcx) -> T { <T as std::default::Default>::default() }
}
```
- change `foo` to return an argument of type `T`:
```
impl<T> Trait<T> for X {
fn foo(&tcx, x: T) -> T { x }
}
```",
);
}
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch10-02-traits.html\
#traits-as-parameters",
);
}
(ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
diag.help(&format!(
"every closure has a distinct type and so could not always match the \
caller-chosen type of parameter `{}`",
p
));
}
(ty::Param(p), _) | (_, ty::Param(p)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
}
(ty::Alias(ty::Projection, proj_ty), _) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
self.expected_projection(
diag,
proj_ty,
values,
body_owner_def_id,
cause.code(),
);
}
(_, ty::Alias(ty::Projection, proj_ty)) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
let msg = format!(
"consider constraining the associated type `{}` to `{}`",
values.found, values.expected,
);
if !(self.suggest_constraining_opaque_associated_type(
diag,
&msg,
proj_ty,
values.expected,
) || self.suggest_constraint(
diag,
&msg,
body_owner_def_id,
proj_ty,
values.expected,
)) {
diag.help(&msg);
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
}
_ => {}
}
debug!(
"note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
values.expected,
values.expected.kind(),
values.found,
values.found.kind(),
);
}
CyclicTy(ty) => {
// Watch out for various cases of cyclic types and try to explain.
if ty.is_closure() || ty.is_generator() {
diag.note(
"closures cannot capture themselves or take themselves as argument;\n\
this error may be the result of a recent compiler bug-fix,\n\
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
for more information",
);
}
}
TargetFeatureCast(def_id) => {
let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
diag.note(
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
);
diag.span_labels(target_spans, "`#[target_feature]` added here");
}
_ => {}
}
}
fn suggest_constraint(
&self,
diag: &mut Diagnostic,
msg: &str,
body_owner_def_id: DefId,
proj_ty: &ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let assoc = tcx.associated_item(proj_ty.def_id);
let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx);
if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) {
if let Some(hir_generics) = item.generics() {
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
// This will also work for `impl Trait`.
let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
let generics = tcx.generics_of(body_owner_def_id);
generics.type_param(param_ty, tcx).def_id
} else {
return false;
};
let Some(def_id) = def_id.as_local() else {
return false;
};
// First look in the `where` clause, as this might be
// `fn foo<T>(x: T) where T: Trait`.
for pred in hir_generics.bounds_for_param(def_id) {
if self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
&trait_ref,
pred.bounds,
&assoc,
assoc_substs,
ty,
msg,
false,
) {
return true;
}
}
}
}
false
}
/// An associated type was expected and a different type was found.
///
/// We perform a few different checks to see what we can suggest:
///
/// - In the current item, look for associated functions that return the expected type and
/// suggest calling them. (Not a structured suggestion.)
/// - If any of the item's generic bounds can be constrained, we suggest constraining the
/// associated type to the found type.
/// - If the associated type has a default type and was expected inside of a `trait`, we
/// mention that this is disallowed.
/// - If all other things fail, and the error is not because of a mismatch between the `trait`
/// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
/// fn that returns the type.
fn expected_projection(
&self,
diag: &mut Diagnostic,
proj_ty: &ty::AliasTy<'tcx>,
values: ExpectedFound<Ty<'tcx>>,
body_owner_def_id: DefId,
cause_code: &ObligationCauseCode<'_>,
) {
let tcx = self.tcx;
let msg = format!(
"consider constraining the associated type `{}` to `{}`",
values.expected, values.found
);
let body_owner = tcx.hir().get_if_local(body_owner_def_id);
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
// We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
let callable_scope = matches!(
body_owner,
Some(
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
| hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
)
);
let impl_comparison =
matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
let assoc = tcx.associated_item(proj_ty.def_id);
if !callable_scope || impl_comparison {
// We do not want to suggest calling functions when the reason of the
// type error is a comparison of an `impl` with its `trait` or when the
// scope is outside of a `Body`.
} else {
// If we find a suitable associated function that returns the expected type, we don't
// want the more general suggestion later in this method about "consider constraining
// the associated type or calling a method that returns the associated type".
let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type(
diag,
assoc.container_id(tcx),
current_method_ident,
proj_ty.def_id,
values.expected,
);
// Possibly suggest constraining the associated type to conform to the
// found type.
if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
|| point_at_assoc_fn
{
return;
}
}
self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
return;
}
if !impl_comparison {
// Generic suggestion when we can't be more specific.
if callable_scope {
diag.help(&format!(
"{} or calling a method that returns `{}`",
msg, values.expected
));
} else {
diag.help(&msg);
}
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
if tcx.sess.teach(&diag.get_code().unwrap()) {
diag.help(
"given an associated type `T` and a method `foo`:
```
trait Trait {
type T;
fn foo(&tcx) -> Self::T;
}
```
the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
```
impl Trait for X {
type T = String;
fn foo(&tcx) -> Self::T { String::new() }
}
```",
);
}
}
/// When the expected `impl Trait` is not defined in the current item, it will come from
/// a return type. This can occur when dealing with `TryStream` (#71035).
fn suggest_constraining_opaque_associated_type(
&self,
diag: &mut Diagnostic,
msg: &str,
proj_ty: &ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let assoc = tcx.associated_item(proj_ty.def_id);
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
let opaque_local_def_id = def_id.as_local();
let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
match &tcx.hir().expect_item(opaque_local_def_id).kind {
hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty,
_ => bug!("The HirId comes from a `ty::Opaque`"),
}
} else {
return false;
};
let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx);
self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
&trait_ref,
opaque_hir_ty.bounds,
assoc,
assoc_substs,
ty,
msg,
true,
)
} else {
false
}
}
fn point_at_methods_that_satisfy_associated_type(
&self,
diag: &mut Diagnostic,
assoc_container_id: DefId,
current_method_ident: Option<Symbol>,
proj_ty_item_def_id: DefId,
expected: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let items = tcx.associated_items(assoc_container_id);
// Find all the methods in the trait that could be called to construct the
// expected associated type.
// FIXME: consider suggesting the use of associated `const`s.
let methods: Vec<(Span, String)> = items
.in_definition_order()
.filter(|item| {
ty::AssocKind::Fn == item.kind && Some(item.name) != current_method_ident
})
.filter_map(|item| {
let method = tcx.fn_sig(item.def_id);
match *method.output().skip_binder().kind() {
ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
if item_def_id == proj_ty_item_def_id =>
{
Some((
tcx.def_span(item.def_id),
format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
))
}
_ => None,
}
})
.collect();
if !methods.is_empty() {
// Use a single `help:` to show all the methods in the trait that can
// be used to construct the expected associated type.
let mut span: MultiSpan =
methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
let msg = format!(
"{some} method{s} {are} available that return{r} `{ty}`",
some = if methods.len() == 1 { "a" } else { "some" },
s = pluralize!(methods.len()),
are = pluralize!("is", methods.len()),
r = if methods.len() == 1 { "s" } else { "" },
ty = expected
);
for (sp, label) in methods.into_iter() {
span.push_span_label(sp, label);
}
diag.span_help(span, &msg);
return true;
}
false
}
fn point_at_associated_type(
&self,
diag: &mut Diagnostic,
body_owner_def_id: DefId,
found: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let Some(hir_id) = body_owner_def_id.as_local() else {
return false;
};
let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id);
// When `body_owner` is an `impl` or `trait` item, look in its associated types for
// `expected` and point at it.
let parent_id = tcx.hir().get_parent_item(hir_id);
let item = tcx.hir().find_by_def_id(parent_id.def_id);
debug!("expected_projection parent item {:?}", item);
let param_env = tcx.param_env(body_owner_def_id);
match item {
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => {
// FIXME: account for `#![feature(specialization)]`
for item in &items[..] {
match item.kind {
hir::AssocItemKind::Type => {
// FIXME: account for returning some type in a trait fn impl that has
// an assoc type as a return type (#72076).
if let hir::Defaultness::Default { has_value: true } =
tcx.impl_defaultness(item.id.owner_id)
{
let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity();
if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() {
diag.span_label(
item.span,
"associated type defaults can't be assumed inside the \
trait defining them",
);
return true;
}
}
}
_ => {}
}
}
}
Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
..
})) => {
for item in &items[..] {
if let hir::AssocItemKind::Type = item.kind {
let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity();
if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() {
diag.span_label(item.span, "expected this associated type");
return true;
}
}
}
}
_ => {}
}
false
}
/// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
/// requirement, provide a structured suggestion to constrain it to a given type `ty`.
///
/// `is_bound_surely_present` indicates whether we know the bound we're looking for is
/// inside `bounds`. If that's the case then we can consider `bounds` containing only one
/// trait bound as the one we're looking for. This can help in cases where the associated
/// type is defined on a supertrait of the one present in the bounds.
fn constrain_generic_bound_associated_type_structured_suggestion(
&self,
diag: &mut Diagnostic,
trait_ref: &ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: &str,
is_bound_surely_present: bool,
) -> bool {
// FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
let trait_bounds = bounds.iter().filter_map(|bound| match bound {
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
_ => None,
});
let matching_trait_bounds = trait_bounds
.clone()
.filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
.collect::<Vec<_>>();
let span = match &matching_trait_bounds[..] {
&[ptr] => ptr.span,
&[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
&[ptr] => ptr.span,
_ => return false,
},
_ => return false,
};
self.constrain_associated_type_structured_suggestion(
diag,
span,
assoc,
assoc_substs,
ty,
msg,
)
}
/// Given a span corresponding to a bound, provide a structured suggestion to set an
/// associated type to a given type `ty`.
fn constrain_associated_type_structured_suggestion(
&self,
diag: &mut Diagnostic,
span: Span,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: &str,
) -> bool {
let tcx = self.tcx;
if let Ok(has_params) =
tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
{
let (span, sugg) = if has_params {
let pos = span.hi() - BytePos(1);
let span = Span::new(pos, pos, span.ctxt(), span.parent());
(span, format!(", {} = {}", assoc.ident(tcx), ty))
} else {
let item_args = self.format_generic_args(assoc_substs);
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
};
diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
return true;
}
false
}
pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS)
.path_generic_args(Ok, args)
.expect("could not write to `String`.")
.into_buffer()
}
}

View File

@ -130,7 +130,7 @@ impl std::fmt::Display for AssocKind {
/// done only on items with the same name.
#[derive(Debug, Clone, PartialEq, HashStable)]
pub struct AssocItems<'tcx> {
pub(super) items: SortedIndexMultiMap<u32, Symbol, &'tcx ty::AssocItem>,
items: SortedIndexMultiMap<u32, Symbol, &'tcx ty::AssocItem>,
}
impl<'tcx> AssocItems<'tcx> {

View File

@ -1,24 +1,18 @@
use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::diagnostics::suggest_constraining_type_param;
use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, Printer};
use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter};
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, Diagnostic, MultiSpan};
use rustc_errors::pluralize;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{BytePos, Span};
use rustc_span::symbol::Symbol;
use rustc_target::spec::abi;
use std::borrow::Cow;
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::hash::Hash;
use std::hash::Hasher;
use std::path::PathBuf;
use super::print::PrettyPrinter;
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)]
pub struct ExpectedFound<T> {
pub expected: T,
@ -391,620 +385,6 @@ impl<'tcx> Ty<'tcx> {
}
impl<'tcx> TyCtxt<'tcx> {
pub fn note_and_explain_type_err(
self,
diag: &mut Diagnostic,
err: TypeError<'tcx>,
cause: &ObligationCause<'tcx>,
sp: Span,
body_owner_def_id: DefId,
) {
use self::TypeError::*;
debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
match err {
ArgumentSorts(values, _) | Sorts(values) => {
match (values.expected.kind(), values.found.kind()) {
(ty::Closure(..), ty::Closure(..)) => {
diag.note("no two closures, even if identical, have the same type");
diag.help("consider boxing your closure and/or using it as a trait object");
}
(ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
// Issue #63167
diag.note("distinct uses of `impl Trait` result in different opaque types");
}
(ty::Float(_), ty::Infer(ty::IntVar(_)))
if let Ok(
// Issue #53280
snippet,
) = self.sess.source_map().span_to_snippet(sp) =>
{
if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
diag.span_suggestion(
sp,
"use a float literal",
format!("{}.0", snippet),
MachineApplicable,
);
}
}
(ty::Param(expected), ty::Param(found)) => {
let generics = self.generics_of(body_owner_def_id);
let e_span = self.def_span(generics.type_param(expected, self).def_id);
if !sp.contains(e_span) {
diag.span_label(e_span, "expected type parameter");
}
let f_span = self.def_span(generics.type_param(found, self).def_id);
if !sp.contains(f_span) {
diag.span_label(f_span, "found type parameter");
}
diag.note(
"a type parameter was expected, but a different one was found; \
you might be missing a type parameter or trait bound",
);
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch10-02-traits.html\
#traits-as-parameters",
);
}
(ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
diag.note("an associated type was expected, but a different one was found");
}
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
if self.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
{
let generics = self.generics_of(body_owner_def_id);
let p_span = self.def_span(generics.type_param(p, self).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
let hir = self.hir();
let mut note = true;
if let Some(generics) = generics
.type_param(p, self)
.def_id
.as_local()
.map(|id| hir.local_def_id_to_hir_id(id))
.and_then(|id| self.hir().find_parent(id))
.as_ref()
.and_then(|node| node.generics())
{
// Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics.
let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(self);
let path =
self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
let item_name = self.item_name(proj.def_id);
let item_args = self.format_generic_args(assoc_substs);
let path = if path.ends_with('>') {
format!(
"{}, {}{} = {}>",
&path[..path.len() - 1],
item_name,
item_args,
p
)
} else {
format!("{}<{}{} = {}>", path, item_name, item_args, p)
};
note = !suggest_constraining_type_param(
self,
generics,
diag,
&format!("{}", proj.self_ty()),
&path,
None,
);
}
if note {
diag.note("you might be missing a type parameter or trait bound");
}
}
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
let generics = self.generics_of(body_owner_def_id);
let p_span = self.def_span(generics.type_param(p, self).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
diag.help("type parameters must be constrained to match other types");
if self.sess.teach(&diag.get_code().unwrap()) {
diag.help(
"given a type parameter `T` and a method `foo`:
```
trait Trait<T> { fn foo(&self) -> T; }
```
the only ways to implement method `foo` are:
- constrain `T` with an explicit type:
```
impl Trait<String> for X {
fn foo(&self) -> String { String::new() }
}
```
- add a trait bound to `T` and call a method on that trait that returns `Self`:
```
impl<T: std::default::Default> Trait<T> for X {
fn foo(&self) -> T { <T as std::default::Default>::default() }
}
```
- change `foo` to return an argument of type `T`:
```
impl<T> Trait<T> for X {
fn foo(&self, x: T) -> T { x }
}
```",
);
}
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch10-02-traits.html\
#traits-as-parameters",
);
}
(ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {
let generics = self.generics_of(body_owner_def_id);
let p_span = self.def_span(generics.type_param(p, self).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
diag.help(&format!(
"every closure has a distinct type and so could not always match the \
caller-chosen type of parameter `{}`",
p
));
}
(ty::Param(p), _) | (_, ty::Param(p)) => {
let generics = self.generics_of(body_owner_def_id);
let p_span = self.def_span(generics.type_param(p, self).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
}
(ty::Alias(ty::Projection, proj_ty), _) if self.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
self.expected_projection(
diag,
proj_ty,
values,
body_owner_def_id,
cause.code(),
);
}
(_, ty::Alias(ty::Projection, proj_ty)) if self.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
let msg = format!(
"consider constraining the associated type `{}` to `{}`",
values.found, values.expected,
);
if !(self.suggest_constraining_opaque_associated_type(
diag,
&msg,
proj_ty,
values.expected,
) || self.suggest_constraint(
diag,
&msg,
body_owner_def_id,
proj_ty,
values.expected,
)) {
diag.help(&msg);
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
}
_ => {}
}
debug!(
"note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
values.expected,
values.expected.kind(),
values.found,
values.found.kind(),
);
}
CyclicTy(ty) => {
// Watch out for various cases of cyclic types and try to explain.
if ty.is_closure() || ty.is_generator() {
diag.note(
"closures cannot capture themselves or take themselves as argument;\n\
this error may be the result of a recent compiler bug-fix,\n\
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
for more information",
);
}
}
TargetFeatureCast(def_id) => {
let target_spans =
self.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
diag.note(
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
);
diag.span_labels(target_spans, "`#[target_feature]` added here");
}
_ => {}
}
}
fn suggest_constraint(
self,
diag: &mut Diagnostic,
msg: &str,
body_owner_def_id: DefId,
proj_ty: &ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let assoc = self.associated_item(proj_ty.def_id);
let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self);
if let Some(item) = self.hir().get_if_local(body_owner_def_id) {
if let Some(hir_generics) = item.generics() {
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
// This will also work for `impl Trait`.
let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
let generics = self.generics_of(body_owner_def_id);
generics.type_param(param_ty, self).def_id
} else {
return false;
};
let Some(def_id) = def_id.as_local() else {
return false;
};
// First look in the `where` clause, as this might be
// `fn foo<T>(x: T) where T: Trait`.
for pred in hir_generics.bounds_for_param(def_id) {
if self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
&trait_ref,
pred.bounds,
&assoc,
assoc_substs,
ty,
msg,
false,
) {
return true;
}
}
}
}
false
}
/// An associated type was expected and a different type was found.
///
/// We perform a few different checks to see what we can suggest:
///
/// - In the current item, look for associated functions that return the expected type and
/// suggest calling them. (Not a structured suggestion.)
/// - If any of the item's generic bounds can be constrained, we suggest constraining the
/// associated type to the found type.
/// - If the associated type has a default type and was expected inside of a `trait`, we
/// mention that this is disallowed.
/// - If all other things fail, and the error is not because of a mismatch between the `trait`
/// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
/// fn that returns the type.
fn expected_projection(
self,
diag: &mut Diagnostic,
proj_ty: &ty::AliasTy<'tcx>,
values: ExpectedFound<Ty<'tcx>>,
body_owner_def_id: DefId,
cause_code: &ObligationCauseCode<'_>,
) {
let msg = format!(
"consider constraining the associated type `{}` to `{}`",
values.expected, values.found
);
let body_owner = self.hir().get_if_local(body_owner_def_id);
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
// We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
let callable_scope = matches!(
body_owner,
Some(
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
| hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
)
);
let impl_comparison =
matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
let assoc = self.associated_item(proj_ty.def_id);
if !callable_scope || impl_comparison {
// We do not want to suggest calling functions when the reason of the
// type error is a comparison of an `impl` with its `trait` or when the
// scope is outside of a `Body`.
} else {
// If we find a suitable associated function that returns the expected type, we don't
// want the more general suggestion later in this method about "consider constraining
// the associated type or calling a method that returns the associated type".
let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type(
diag,
assoc.container_id(self),
current_method_ident,
proj_ty.def_id,
values.expected,
);
// Possibly suggest constraining the associated type to conform to the
// found type.
if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
|| point_at_assoc_fn
{
return;
}
}
self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
return;
}
if !impl_comparison {
// Generic suggestion when we can't be more specific.
if callable_scope {
diag.help(&format!(
"{} or calling a method that returns `{}`",
msg, values.expected
));
} else {
diag.help(&msg);
}
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
if self.sess.teach(&diag.get_code().unwrap()) {
diag.help(
"given an associated type `T` and a method `foo`:
```
trait Trait {
type T;
fn foo(&self) -> Self::T;
}
```
the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
```
impl Trait for X {
type T = String;
fn foo(&self) -> Self::T { String::new() }
}
```",
);
}
}
/// When the expected `impl Trait` is not defined in the current item, it will come from
/// a return type. This can occur when dealing with `TryStream` (#71035).
fn suggest_constraining_opaque_associated_type(
self,
diag: &mut Diagnostic,
msg: &str,
proj_ty: &ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
let assoc = self.associated_item(proj_ty.def_id);
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
let opaque_local_def_id = def_id.as_local();
let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
match &self.hir().expect_item(opaque_local_def_id).kind {
hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty,
_ => bug!("The HirId comes from a `ty::Opaque`"),
}
} else {
return false;
};
let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self);
self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
&trait_ref,
opaque_hir_ty.bounds,
assoc,
assoc_substs,
ty,
msg,
true,
)
} else {
false
}
}
fn point_at_methods_that_satisfy_associated_type(
self,
diag: &mut Diagnostic,
assoc_container_id: DefId,
current_method_ident: Option<Symbol>,
proj_ty_item_def_id: DefId,
expected: Ty<'tcx>,
) -> bool {
let items = self.associated_items(assoc_container_id);
// Find all the methods in the trait that could be called to construct the
// expected associated type.
// FIXME: consider suggesting the use of associated `const`s.
let methods: Vec<(Span, String)> = items
.items
.iter()
.filter(|(name, item)| {
ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident
})
.filter_map(|(_, item)| {
let method = self.fn_sig(item.def_id);
match *method.output().skip_binder().kind() {
ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
if item_def_id == proj_ty_item_def_id =>
{
Some((
self.def_span(item.def_id),
format!("consider calling `{}`", self.def_path_str(item.def_id)),
))
}
_ => None,
}
})
.collect();
if !methods.is_empty() {
// Use a single `help:` to show all the methods in the trait that can
// be used to construct the expected associated type.
let mut span: MultiSpan =
methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
let msg = format!(
"{some} method{s} {are} available that return{r} `{ty}`",
some = if methods.len() == 1 { "a" } else { "some" },
s = pluralize!(methods.len()),
are = pluralize!("is", methods.len()),
r = if methods.len() == 1 { "s" } else { "" },
ty = expected
);
for (sp, label) in methods.into_iter() {
span.push_span_label(sp, label);
}
diag.span_help(span, &msg);
return true;
}
false
}
fn point_at_associated_type(
self,
diag: &mut Diagnostic,
body_owner_def_id: DefId,
found: Ty<'tcx>,
) -> bool {
let Some(hir_id) = body_owner_def_id.as_local() else {
return false;
};
let hir_id = self.hir().local_def_id_to_hir_id(hir_id);
// When `body_owner` is an `impl` or `trait` item, look in its associated types for
// `expected` and point at it.
let parent_id = self.hir().get_parent_item(hir_id);
let item = self.hir().find_by_def_id(parent_id.def_id);
debug!("expected_projection parent item {:?}", item);
match item {
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => {
// FIXME: account for `#![feature(specialization)]`
for item in &items[..] {
match item.kind {
hir::AssocItemKind::Type => {
// FIXME: account for returning some type in a trait fn impl that has
// an assoc type as a return type (#72076).
if let hir::Defaultness::Default { has_value: true } =
self.impl_defaultness(item.id.owner_id)
{
if self.type_of(item.id.owner_id) == found {
diag.span_label(
item.span,
"associated type defaults can't be assumed inside the \
trait defining them",
);
return true;
}
}
}
_ => {}
}
}
}
Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
..
})) => {
for item in &items[..] {
if let hir::AssocItemKind::Type = item.kind {
if self.type_of(item.id.owner_id) == found {
diag.span_label(item.span, "expected this associated type");
return true;
}
}
}
}
_ => {}
}
false
}
/// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
/// requirement, provide a structured suggestion to constrain it to a given type `ty`.
///
/// `is_bound_surely_present` indicates whether we know the bound we're looking for is
/// inside `bounds`. If that's the case then we can consider `bounds` containing only one
/// trait bound as the one we're looking for. This can help in cases where the associated
/// type is defined on a supertrait of the one present in the bounds.
fn constrain_generic_bound_associated_type_structured_suggestion(
self,
diag: &mut Diagnostic,
trait_ref: &ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: &str,
is_bound_surely_present: bool,
) -> bool {
// FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
let trait_bounds = bounds.iter().filter_map(|bound| match bound {
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
_ => None,
});
let matching_trait_bounds = trait_bounds
.clone()
.filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
.collect::<Vec<_>>();
let span = match &matching_trait_bounds[..] {
&[ptr] => ptr.span,
&[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
&[ptr] => ptr.span,
_ => return false,
},
_ => return false,
};
self.constrain_associated_type_structured_suggestion(
diag,
span,
assoc,
assoc_substs,
ty,
msg,
)
}
/// Given a span corresponding to a bound, provide a structured suggestion to set an
/// associated type to a given type `ty`.
fn constrain_associated_type_structured_suggestion(
self,
diag: &mut Diagnostic,
span: Span,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: &str,
) -> bool {
if let Ok(has_params) =
self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
{
let (span, sugg) = if has_params {
let pos = span.hi() - BytePos(1);
let span = Span::new(pos, pos, span.ctxt(), span.parent());
(span, format!(", {} = {}", assoc.ident(self), ty))
} else {
let item_args = self.format_generic_args(assoc_substs);
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(self), item_args, ty))
};
diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
return true;
}
false
}
pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
let width = self.sess.diagnostic_width();
let length_limit = width.saturating_sub(30);
@ -1047,11 +427,4 @@ fn foo(&self) -> Self::T { String::new() }
Err(_) => (regular, None),
}
}
fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String {
FmtPrinter::new(self, hir::def::Namespace::TypeNS)
.path_generic_args(Ok, args)
.expect("could not write to `String`.")
.into_buffer()
}
}

View File

@ -441,6 +441,10 @@ impl<'tcx> TyCtxt<'tcx> {
self.opt_def_kind(def_id)
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
}
pub fn bound_type_of(self, def_id: impl IntoQueryParam<DefId>) -> ty::EarlyBinder<Ty<'tcx>> {
ty::EarlyBinder(self.type_of(def_id))
}
}
impl<'tcx> TyCtxtAt<'tcx> {
@ -449,4 +453,8 @@ impl<'tcx> TyCtxtAt<'tcx> {
self.opt_def_kind(def_id)
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
}
pub fn bound_type_of(self, def_id: impl IntoQueryParam<DefId>) -> ty::EarlyBinder<Ty<'tcx>> {
ty::EarlyBinder(self.type_of(def_id))
}
}

View File

@ -3,7 +3,6 @@
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir;
use crate::ty::layout::IntegerExt;
use crate::ty::query::TyCtxtAt;
use crate::ty::{
self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitable,
@ -637,10 +636,6 @@ impl<'tcx> TyCtxt<'tcx> {
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
}
pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> {
ty::EarlyBinder(self.type_of(def_id))
}
pub fn bound_return_position_impl_trait_in_trait_tys(
self,
def_id: DefId,
@ -738,12 +733,6 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
impl<'tcx> TyCtxtAt<'tcx> {
pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> {
ty::EarlyBinder(self.type_of(def_id))
}
}
struct OpaqueTypeExpander<'tcx> {
// Contains the DefIds of the opaque types that are currently being
// expanded. When we expand an opaque type we insert the DefId of

View File

@ -44,4 +44,18 @@ impl AssocConst for () {
const C: Self::Ty = 0u8;
}
pub trait Trait {
type Res = isize; //~ NOTE associated type defaults can't be assumed inside the trait defining them
fn infer_me_correctly() -> Self::Res {
//~^ NOTE expected `<Self as Trait>::Res` because of return type
// {integer} == isize
2
//~^ ERROR mismatched types
//~| NOTE expected associated type, found integer
//~| NOTE expected associated type `<Self as Trait>::Res`
}
}
fn main() {}

View File

@ -24,6 +24,21 @@ LL | const C: Self::Ty = 0u8;
= note: expected associated type `<Self as AssocConst>::Ty`
found type `u8`
error: aborting due to 2 previous errors
error[E0308]: mismatched types
--> $DIR/defaults-in-other-trait-items.rs:54:9
|
LL | type Res = isize;
| ----------------- associated type defaults can't be assumed inside the trait defining them
LL |
LL | fn infer_me_correctly() -> Self::Res {
| --------- expected `<Self as Trait>::Res` because of return type
...
LL | 2
| ^ expected associated type, found integer
|
= note: expected associated type `<Self as Trait>::Res`
found type `{integer}`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -1,13 +1,13 @@
error[E0308]: mismatched types
--> $DIR/issue-26681.rs:17:39
|
LL | type Fv: Foo = u8;
| ------------------ associated type defaults can't be assumed inside the trait defining them
LL | const C: <Self::Fv as Foo>::Bar = 6665;
| ^^^^ expected associated type, found integer
|
= note: expected associated type `<<Self as Baz>::Fv as Foo>::Bar`
found type `{integer}`
= help: consider constraining the associated type `<<Self as Baz>::Fv as Foo>::Bar` to `{integer}`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error: aborting due to previous error