Skip to content

Commit

Permalink
Make ProjectionTy::trait_ref truncate substs again
Browse files Browse the repository at this point in the history
Also make sure that type arguments of associated types are printed in
some error messages.
  • Loading branch information
matthewjasper committed Feb 13, 2021
1 parent 79f6f11 commit dfee89f
Show file tree
Hide file tree
Showing 12 changed files with 216 additions and 49 deletions.
40 changes: 34 additions & 6 deletions compiler/rustc_middle/src/ty/error.rs
@@ -1,5 +1,6 @@
use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::diagnostics::suggest_constraining_type_param;
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, DiagnosticBuilder};
Expand Down Expand Up @@ -400,14 +401,22 @@ impl<'tcx> TyCtxt<'tcx> {
{
// Synthesize the associated type restriction `Add<Output = Expected>`.
// 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 =
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_args = self.format_generic_args(assoc_substs);

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 {
format!("{}<{} = {}>", path, item_name, p)
format!("{}<{}{} = {}>", path, item_name, item_args, p)
};
note = !suggest_constraining_type_param(
self,
Expand Down Expand Up @@ -556,7 +565,7 @@ impl<T> Trait<T> for X {
ty: Ty<'tcx>,
) -> bool {
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(hir_generics) = item.generics() {
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
Expand Down Expand Up @@ -590,6 +599,7 @@ impl<T> Trait<T> for X {
&trait_ref,
pred.bounds,
&assoc,
assoc_substs,
ty,
msg,
) {
Expand All @@ -607,6 +617,7 @@ impl<T> Trait<T> for X {
&trait_ref,
param.bounds,
&assoc,
assoc_substs,
ty,
msg,
);
Expand Down Expand Up @@ -692,6 +703,7 @@ impl<T> Trait<T> for X {
db,
self.def_span(def_id),
&assoc,
proj_ty.trait_ref_and_own_substs(self).1,
values.found,
&msg,
) {
Expand Down Expand Up @@ -856,6 +868,7 @@ fn foo(&self) -> Self::T { String::new() }
trait_ref: &ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: &str,
) -> bool {
Expand All @@ -865,7 +878,12 @@ fn foo(&self) -> Self::T { String::new() }
// Relate the type param against `T` in `<A as T>::Foo`.
ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)
&& self.constrain_associated_type_structured_suggestion(
db, ptr.span, assoc, ty, msg,
db,
ptr.span,
assoc,
assoc_substs,
ty,
msg,
)
}
_ => false,
Expand All @@ -879,6 +897,7 @@ fn foo(&self) -> Self::T { String::new() }
db: &mut DiagnosticBuilder<'_>,
span: Span,
assoc: &ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
msg: &str,
) -> bool {
Expand All @@ -890,11 +909,20 @@ fn foo(&self) -> Self::T { String::new() }
let span = Span::new(pos, pos, span.ctxt());
(span, format!(", {} = {}", assoc.ident, ty))
} 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);
return true;
}
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
}
}
16 changes: 15 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Expand Up @@ -1289,8 +1289,22 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
self.skip_binder().projection_ty.item_def_id
}

/// Returns the `DefId` of the trait of the associated item being projected.
#[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()`,
// `self.0.trait_ref` is permitted to have escaping regions.
// This is because here `self` has a `Binder` and so does our
Expand Down
37 changes: 26 additions & 11 deletions compiler/rustc_middle/src/ty/sty.rs
Expand Up @@ -17,7 +17,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_index::vec::Idx;
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::spec::abi;
use std::borrow::Cow;
Expand Down Expand Up @@ -1112,20 +1112,35 @@ pub struct 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.
/// For example, if this is a projection of `<T as Iterator>::Item`,
/// 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> {
// FIXME: This method probably shouldn't exist at all, since it's not
// clear what this method really intends to do. Be careful when
// 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 }
let def_id = self.trait_def_id(tcx);
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, tcx.generics_of(def_id)) }
}

pub fn self_ty(&self) -> Ty<'tcx> {
Expand Down
Expand Up @@ -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)
}
ty::PredicateKind::Projection(data) => {
let trait_ref = bound_predicate.rebind(data).to_poly_trait_ref(self.tcx);
let self_ty = trait_ref.skip_binder().self_ty();
let self_ty = data.projection_ty.self_ty();
let ty = data.ty;
if predicate.references_error() {
return;
Expand Down
7 changes: 2 additions & 5 deletions compiler/rustc_traits/src/chalk/lowering.rs
Expand Up @@ -779,14 +779,11 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>
self,
interner: &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 {
trait_bound: trait_ref.lower_into(interner),
associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.item_def_id),
parameters: self.projection_ty.substs[trait_ref.substs.len()..]
.iter()
.map(|arg| arg.lower_into(interner))
.collect(),
parameters: own_substs.iter().map(|arg| arg.lower_into(interner)).collect(),
value: self.ty.lower_into(interner),
}
}
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_typeck/src/check/closure.rs
Expand Up @@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});

// 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
// many viable options, so pick the most restrictive.
let expected_kind = self
Expand All @@ -234,11 +234,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

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 is_gen = gen_trait == trait_ref.def_id();
let is_gen = gen_trait == trait_def_id;
if !is_fn && !is_gen {
debug!("deduce_sig_from_projection: not fn or generator");
return None;
Expand All @@ -256,7 +256,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

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);
debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty);

Expand Down Expand Up @@ -662,9 +662,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};

// 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));
if trait_ref.def_id != future_trait {
if trait_def_id != future_trait {
debug!("deduce_future_output_from_projection: not a future");
return None;
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Expand Up @@ -769,9 +769,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.filter_map(move |obligation| {
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Projection(data) => {
Some((bound_predicate.rebind(data).to_poly_trait_ref(self.tcx), obligation))
}
ty::PredicateKind::Projection(data) => Some((
bound_predicate.rebind(data).required_poly_trait_ref(self.tcx),
obligation,
)),
ty::PredicateKind::Trait(data, _) => {
Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation))
}
Expand Down
33 changes: 19 additions & 14 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Expand Up @@ -24,6 +24,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::Obligation;

use std::cmp::Ordering;
use std::iter;

use super::probe::Mode;
use super::{CandidateSource, MethodError, NoMatchData};
Expand Down Expand Up @@ -649,21 +650,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::PredicateKind::Projection(pred) => {
let pred = bound_predicate.rebind(pred);
// `<Foo as Iterator>::Item = String`.
let trait_ref =
pred.skip_binder().projection_ty.trait_ref(self.tcx);
let assoc = self
.tcx
.associated_item(pred.skip_binder().projection_ty.item_def_id);
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
let projection_ty = pred.skip_binder().projection_ty;

let substs_with_infer_self = tcx.mk_substs(
iter::once(tcx.mk_ty_var(ty::TyVid { index: 0 }).into())
.chain(projection_ty.substs.iter().skip(1)),
);
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, _) => {
let p = poly_trait_ref.trait_ref;
Expand Down
@@ -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() {}
@@ -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`.

0 comments on commit dfee89f

Please sign in to comment.