Make ProjectionTy::trait_ref truncate substs again

Also make sure that type arguments of associated types are printed in
some error messages.
This commit is contained in:
Matthew Jasper 2021-02-12 22:44:43 +00:00
parent 79f6f11816
commit dfee89f755
12 changed files with 216 additions and 49 deletions

View File

@ -1,5 +1,6 @@
use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::diagnostics::suggest_constraining_type_param; use crate::ty::diagnostics::suggest_constraining_type_param;
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, DiagnosticBuilder}; use rustc_errors::{pluralize, DiagnosticBuilder};
@ -400,14 +401,22 @@ impl<'tcx> TyCtxt<'tcx> {
{ {
// Synthesize the associated type restriction `Add<Output = Expected>`. // Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics. // FIXME: extract this logic for use in other diagnostics.
let trait_ref = proj.trait_ref(self); let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(self);
let path = let path =
self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
let item_name = self.item_name(proj.item_def_id); let item_name = self.item_name(proj.item_def_id);
let item_args = self.format_generic_args(assoc_substs);
let path = if path.ends_with('>') { let path = if path.ends_with('>') {
format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p) format!(
"{}, {}{} = {}>",
&path[..path.len() - 1],
item_name,
item_args,
p
)
} else { } else {
format!("{}<{} = {}>", path, item_name, p) format!("{}<{}{} = {}>", path, item_name, item_args, p)
}; };
note = !suggest_constraining_type_param( note = !suggest_constraining_type_param(
self, self,
@ -556,7 +565,7 @@ impl<T> Trait<T> for X {
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> bool { ) -> bool {
let assoc = self.associated_item(proj_ty.item_def_id); let assoc = self.associated_item(proj_ty.item_def_id);
let trait_ref = proj_ty.trait_ref(self); 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(item) = self.hir().get_if_local(body_owner_def_id) {
if let Some(hir_generics) = item.generics() { if let Some(hir_generics) = item.generics() {
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`. // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
@ -590,6 +599,7 @@ impl<T> Trait<T> for X {
&trait_ref, &trait_ref,
pred.bounds, pred.bounds,
&assoc, &assoc,
assoc_substs,
ty, ty,
msg, msg,
) { ) {
@ -607,6 +617,7 @@ impl<T> Trait<T> for X {
&trait_ref, &trait_ref,
param.bounds, param.bounds,
&assoc, &assoc,
assoc_substs,
ty, ty,
msg, msg,
); );
@ -692,6 +703,7 @@ impl<T> Trait<T> for X {
db, db,
self.def_span(def_id), self.def_span(def_id),
&assoc, &assoc,
proj_ty.trait_ref_and_own_substs(self).1,
values.found, values.found,
&msg, &msg,
) { ) {
@ -856,6 +868,7 @@ fn foo(&self) -> Self::T { String::new() }
trait_ref: &ty::TraitRef<'tcx>, trait_ref: &ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>, bounds: hir::GenericBounds<'_>,
assoc: &ty::AssocItem, assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>, ty: Ty<'tcx>,
msg: &str, msg: &str,
) -> bool { ) -> bool {
@ -865,7 +878,12 @@ fn foo(&self) -> Self::T { String::new() }
// Relate the type param against `T` in `<A as T>::Foo`. // Relate the type param against `T` in `<A as T>::Foo`.
ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)
&& self.constrain_associated_type_structured_suggestion( && self.constrain_associated_type_structured_suggestion(
db, ptr.span, assoc, ty, msg, db,
ptr.span,
assoc,
assoc_substs,
ty,
msg,
) )
} }
_ => false, _ => false,
@ -879,6 +897,7 @@ fn foo(&self) -> Self::T { String::new() }
db: &mut DiagnosticBuilder<'_>, db: &mut DiagnosticBuilder<'_>,
span: Span, span: Span,
assoc: &ty::AssocItem, assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>, ty: Ty<'tcx>,
msg: &str, msg: &str,
) -> bool { ) -> bool {
@ -890,11 +909,20 @@ fn foo(&self) -> Self::T { String::new() }
let span = Span::new(pos, pos, span.ctxt()); let span = Span::new(pos, pos, span.ctxt());
(span, format!(", {} = {}", assoc.ident, ty)) (span, format!(", {} = {}", assoc.ident, ty))
} else { } else {
(span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) let item_args = self.format_generic_args(assoc_substs);
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident, item_args, ty))
}; };
db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
return true; return true;
} }
false false
} }
fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String {
let mut item_args = String::new();
FmtPrinter::new(self, &mut item_args, hir::def::Namespace::TypeNS)
.path_generic_args(Ok, args)
.expect("could not write to `String`.");
item_args
}
} }

View File

@ -1289,8 +1289,22 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
self.skip_binder().projection_ty.item_def_id self.skip_binder().projection_ty.item_def_id
} }
/// Returns the `DefId` of the trait of the associated item being projected.
#[inline] #[inline]
pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
self.skip_binder().projection_ty.trait_def_id(tcx)
}
#[inline]
pub fn projection_self_ty(&self) -> Binder<Ty<'tcx>> {
self.map_bound(|predicate| predicate.projection_ty.self_ty())
}
/// Get the [PolyTraitRef] required for this projection to be well formed.
/// Note that for generic associated types the predicates of the associated
/// type also need to be checked.
#[inline]
pub fn required_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> {
// Note: unlike with `TraitRef::to_poly_trait_ref()`, // Note: unlike with `TraitRef::to_poly_trait_ref()`,
// `self.0.trait_ref` is permitted to have escaping regions. // `self.0.trait_ref` is permitted to have escaping regions.
// This is because here `self` has a `Binder` and so does our // This is because here `self` has a `Binder` and so does our

View File

@ -17,7 +17,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::symbol::{kw, Symbol};
use rustc_target::abi::VariantIdx; use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi; use rustc_target::spec::abi;
use std::borrow::Cow; use std::borrow::Cow;
@ -1112,20 +1112,35 @@ pub struct ProjectionTy<'tcx> {
} }
impl<'tcx> ProjectionTy<'tcx> { impl<'tcx> ProjectionTy<'tcx> {
pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
tcx.associated_item(self.item_def_id).container.id()
}
/// Extracts the underlying trait reference and own substs from this projection.
/// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
/// then this function would return a `T: Iterator` trait reference and `['a]` as the own substs
pub fn trait_ref_and_own_substs(
&self,
tcx: TyCtxt<'tcx>,
) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
let def_id = tcx.associated_item(self.item_def_id).container.id();
let trait_generics = tcx.generics_of(def_id);
(
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, trait_generics) },
&self.substs[trait_generics.count()..],
)
}
/// Extracts the underlying trait reference from this projection. /// Extracts the underlying trait reference from this projection.
/// For example, if this is a projection of `<T as Iterator>::Item`, /// For example, if this is a projection of `<T as Iterator>::Item`,
/// then this function would return a `T: Iterator` trait reference. /// then this function would return a `T: Iterator` trait reference.
///
/// WARNING: This will drop the substs for generic associated types
/// consider calling [Self::trait_ref_and_own_substs] to get those
/// as well.
pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
// FIXME: This method probably shouldn't exist at all, since it's not let def_id = self.trait_def_id(tcx);
// clear what this method really intends to do. Be careful when ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, tcx.generics_of(def_id)) }
// using this method since the resulting TraitRef additionally
// contains the substs for the assoc_item, which strictly speaking
// is not correct
let def_id = tcx.associated_item(self.item_def_id).container.id();
// Include substitutions for generic arguments of associated types
let assoc_item = tcx.associated_item(self.item_def_id);
let substs_assoc_item = self.substs.truncate_to(tcx, tcx.generics_of(assoc_item.def_id));
ty::TraitRef { def_id, substs: substs_assoc_item }
} }
pub fn self_ty(&self) -> Ty<'tcx> { pub fn self_ty(&self) -> Ty<'tcx> {

View File

@ -1589,8 +1589,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
self.emit_inference_failure_err(body_id, span, a.into(), vec![], ErrorCode::E0282) self.emit_inference_failure_err(body_id, span, a.into(), vec![], ErrorCode::E0282)
} }
ty::PredicateKind::Projection(data) => { ty::PredicateKind::Projection(data) => {
let trait_ref = bound_predicate.rebind(data).to_poly_trait_ref(self.tcx); let self_ty = data.projection_ty.self_ty();
let self_ty = trait_ref.skip_binder().self_ty();
let ty = data.ty; let ty = data.ty;
if predicate.references_error() { if predicate.references_error() {
return; return;

View File

@ -779,14 +779,11 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>
self, self,
interner: &RustInterner<'tcx>, interner: &RustInterner<'tcx>,
) -> chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>> { ) -> chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>> {
let trait_ref = self.projection_ty.trait_ref(interner.tcx); let (trait_ref, own_substs) = self.projection_ty.trait_ref_and_own_substs(interner.tcx);
chalk_solve::rust_ir::AliasEqBound { chalk_solve::rust_ir::AliasEqBound {
trait_bound: trait_ref.lower_into(interner), trait_bound: trait_ref.lower_into(interner),
associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.item_def_id), associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.item_def_id),
parameters: self.projection_ty.substs[trait_ref.substs.len()..] parameters: own_substs.iter().map(|arg| arg.lower_into(interner)).collect(),
.iter()
.map(|arg| arg.lower_into(interner))
.collect(),
value: self.ty.lower_into(interner), value: self.ty.lower_into(interner),
} }
} }

View File

@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}); });
// Even if we can't infer the full signature, we may be able to // Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur if there is a trait-reference // infer the kind. This can occur when we elaborate a predicate
// like `F : Fn<A>`. Note that due to subtyping we could encounter // like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive. // many viable options, so pick the most restrictive.
let expected_kind = self let expected_kind = self
@ -234,11 +234,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("deduce_sig_from_projection({:?})", projection); debug!("deduce_sig_from_projection({:?})", projection);
let trait_ref = projection.to_poly_trait_ref(tcx); let trait_def_id = projection.trait_def_id(tcx);
let is_fn = tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some(); let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some();
let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span);
let is_gen = gen_trait == trait_ref.def_id(); let is_gen = gen_trait == trait_def_id;
if !is_fn && !is_gen { if !is_fn && !is_gen {
debug!("deduce_sig_from_projection: not fn or generator"); debug!("deduce_sig_from_projection: not fn or generator");
return None; return None;
@ -256,7 +256,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
let input_tys = if is_fn { let input_tys = if is_fn {
let arg_param_ty = trait_ref.skip_binder().substs.type_at(1); let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1);
let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty);
debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty); debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty);
@ -662,9 +662,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}; };
// Check that this is a projection from the `Future` trait. // Check that this is a projection from the `Future` trait.
let trait_ref = predicate.projection_ty.trait_ref(self.tcx); let trait_def_id = predicate.projection_ty.trait_def_id(self.tcx);
let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span));
if trait_ref.def_id != future_trait { if trait_def_id != future_trait {
debug!("deduce_future_output_from_projection: not a future"); debug!("deduce_future_output_from_projection: not a future");
return None; return None;
} }

View File

@ -769,9 +769,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.filter_map(move |obligation| { .filter_map(move |obligation| {
let bound_predicate = obligation.predicate.kind(); let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() { match bound_predicate.skip_binder() {
ty::PredicateKind::Projection(data) => { ty::PredicateKind::Projection(data) => Some((
Some((bound_predicate.rebind(data).to_poly_trait_ref(self.tcx), obligation)) bound_predicate.rebind(data).required_poly_trait_ref(self.tcx),
} obligation,
)),
ty::PredicateKind::Trait(data, _) => { ty::PredicateKind::Trait(data, _) => {
Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation))
} }

View File

@ -24,6 +24,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::Obligation; use rustc_trait_selection::traits::Obligation;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::iter;
use super::probe::Mode; use super::probe::Mode;
use super::{CandidateSource, MethodError, NoMatchData}; use super::{CandidateSource, MethodError, NoMatchData};
@ -649,21 +650,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::PredicateKind::Projection(pred) => { ty::PredicateKind::Projection(pred) => {
let pred = bound_predicate.rebind(pred); let pred = bound_predicate.rebind(pred);
// `<Foo as Iterator>::Item = String`. // `<Foo as Iterator>::Item = String`.
let trait_ref = let projection_ty = pred.skip_binder().projection_ty;
pred.skip_binder().projection_ty.trait_ref(self.tcx);
let assoc = self let substs_with_infer_self = tcx.mk_substs(
.tcx iter::once(tcx.mk_ty_var(ty::TyVid { index: 0 }).into())
.associated_item(pred.skip_binder().projection_ty.item_def_id); .chain(projection_ty.substs.iter().skip(1)),
let ty = pred.skip_binder().ty;
let obligation = format!("{}::{} = {}", trait_ref, assoc.ident, ty);
let quiet = format!(
"<_ as {}>::{} = {}",
trait_ref.print_only_trait_path(),
assoc.ident,
ty
); );
bound_span_label(trait_ref.self_ty(), &obligation, &quiet);
Some((obligation, trait_ref.self_ty())) let quiet_projection_ty = ty::ProjectionTy {
substs: substs_with_infer_self,
item_def_id: projection_ty.item_def_id,
};
let ty = pred.skip_binder().ty;
let obligation = format!("{} = {}", projection_ty, ty);
let quiet = format!("{} = {}", quiet_projection_ty, ty);
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
Some((obligation, projection_ty.self_ty()))
} }
ty::PredicateKind::Trait(poly_trait_ref, _) => { ty::PredicateKind::Trait(poly_trait_ref, _) => {
let p = poly_trait_ref.trait_ref; let p = poly_trait_ref.trait_ref;

View File

@ -0,0 +1,17 @@
// Test that correct syntax is used in suggestion to constrain associated type
#![feature(generic_associated_types)]
//~^ WARNING the feature `generic_associated_types` is incomplete
trait X {
type Y<T>;
}
fn f<T: X>(a: T::Y<i32>) {
//~^ HELP consider constraining the associated type `<T as X>::Y<i32>` to `Vec<i32>`
//~| SUGGESTION Y<i32> = Vec<i32>>
let b: Vec<i32> = a;
//~^ ERROR mismatched types
}
fn main() {}

View File

@ -0,0 +1,27 @@
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/constraint-assoc-type-suggestion.rs:3:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
error[E0308]: mismatched types
--> $DIR/constraint-assoc-type-suggestion.rs:13:23
|
LL | let b: Vec<i32> = a;
| -------- ^ expected struct `Vec`, found associated type
| |
| expected due to this
|
= note: expected struct `Vec<i32>`
found associated type `<T as X>::Y<i32>`
help: consider constraining the associated type `<T as X>::Y<i32>` to `Vec<i32>`
|
LL | fn f<T: X<Y<i32> = Vec<i32>>>(a: T::Y<i32>) {
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,35 @@
// Test that the predicate printed in an unresolved method error prints the
// generics for a generic associated type.
#![feature(generic_associated_types)]
//~^ WARNING the feature `generic_associated_types` is incomplete
//~| NOTE `#[warn(incomplete_features)]` on by default
//~| NOTE see issue #44265
trait X {
type Y<T>;
}
trait M {
fn f(&self) {}
}
impl<T: X<Y<i32> = i32>> M for T {}
struct S;
//~^ NOTE method `f` not found for this
//~| NOTE doesn't satisfy `<S as X>::Y<i32> = i32`
//~| NOTE doesn't satisfy `S: M`
impl X for S {
type Y<T> = bool;
}
fn f(a: S) {
a.f();
//~^ ERROR the method `f` exists for struct `S`, but its trait bounds were not satisfied
//~| NOTE method cannot be called on `S` due to unsatisfied trait bounds
//~| NOTE the following trait bounds were not satisfied:
}
fn main() {}

View File

@ -0,0 +1,29 @@
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/method-unsatified-assoc-type-predicate.rs:4:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
error[E0599]: the method `f` exists for struct `S`, but its trait bounds were not satisfied
--> $DIR/method-unsatified-assoc-type-predicate.rs:29:7
|
LL | struct S;
| ---------
| |
| method `f` not found for this
| doesn't satisfy `<S as X>::Y<i32> = i32`
| doesn't satisfy `S: M`
...
LL | a.f();
| ^ method cannot be called on `S` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`<S as X>::Y<i32> = i32`
which is required by `S: M`
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0599`.