Skip to content

Commit dfee89f

Browse files
committed
Make ProjectionTy::trait_ref truncate substs again
Also make sure that type arguments of associated types are printed in some error messages.
1 parent 79f6f11 commit dfee89f

File tree

12 files changed

+216
-49
lines changed

12 files changed

+216
-49
lines changed

compiler/rustc_middle/src/ty/error.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::traits::{ObligationCause, ObligationCauseCode};
22
use crate::ty::diagnostics::suggest_constraining_type_param;
3+
use crate::ty::print::{FmtPrinter, Printer};
34
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
45
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
56
use rustc_errors::{pluralize, DiagnosticBuilder};
@@ -400,14 +401,22 @@ impl<'tcx> TyCtxt<'tcx> {
400401
{
401402
// Synthesize the associated type restriction `Add<Output = Expected>`.
402403
// FIXME: extract this logic for use in other diagnostics.
403-
let trait_ref = proj.trait_ref(self);
404+
let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(self);
404405
let path =
405406
self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
406407
let item_name = self.item_name(proj.item_def_id);
408+
let item_args = self.format_generic_args(assoc_substs);
409+
407410
let path = if path.ends_with('>') {
408-
format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p)
411+
format!(
412+
"{}, {}{} = {}>",
413+
&path[..path.len() - 1],
414+
item_name,
415+
item_args,
416+
p
417+
)
409418
} else {
410-
format!("{}<{} = {}>", path, item_name, p)
419+
format!("{}<{}{} = {}>", path, item_name, item_args, p)
411420
};
412421
note = !suggest_constraining_type_param(
413422
self,
@@ -556,7 +565,7 @@ impl<T> Trait<T> for X {
556565
ty: Ty<'tcx>,
557566
) -> bool {
558567
let assoc = self.associated_item(proj_ty.item_def_id);
559-
let trait_ref = proj_ty.trait_ref(self);
568+
let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self);
560569
if let Some(item) = self.hir().get_if_local(body_owner_def_id) {
561570
if let Some(hir_generics) = item.generics() {
562571
// 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 {
590599
&trait_ref,
591600
pred.bounds,
592601
&assoc,
602+
assoc_substs,
593603
ty,
594604
msg,
595605
) {
@@ -607,6 +617,7 @@ impl<T> Trait<T> for X {
607617
&trait_ref,
608618
param.bounds,
609619
&assoc,
620+
assoc_substs,
610621
ty,
611622
msg,
612623
);
@@ -692,6 +703,7 @@ impl<T> Trait<T> for X {
692703
db,
693704
self.def_span(def_id),
694705
&assoc,
706+
proj_ty.trait_ref_and_own_substs(self).1,
695707
values.found,
696708
&msg,
697709
) {
@@ -856,6 +868,7 @@ fn foo(&self) -> Self::T { String::new() }
856868
trait_ref: &ty::TraitRef<'tcx>,
857869
bounds: hir::GenericBounds<'_>,
858870
assoc: &ty::AssocItem,
871+
assoc_substs: &[ty::GenericArg<'tcx>],
859872
ty: Ty<'tcx>,
860873
msg: &str,
861874
) -> bool {
@@ -865,7 +878,12 @@ fn foo(&self) -> Self::T { String::new() }
865878
// Relate the type param against `T` in `<A as T>::Foo`.
866879
ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)
867880
&& self.constrain_associated_type_structured_suggestion(
868-
db, ptr.span, assoc, ty, msg,
881+
db,
882+
ptr.span,
883+
assoc,
884+
assoc_substs,
885+
ty,
886+
msg,
869887
)
870888
}
871889
_ => false,
@@ -879,6 +897,7 @@ fn foo(&self) -> Self::T { String::new() }
879897
db: &mut DiagnosticBuilder<'_>,
880898
span: Span,
881899
assoc: &ty::AssocItem,
900+
assoc_substs: &[ty::GenericArg<'tcx>],
882901
ty: Ty<'tcx>,
883902
msg: &str,
884903
) -> bool {
@@ -890,11 +909,20 @@ fn foo(&self) -> Self::T { String::new() }
890909
let span = Span::new(pos, pos, span.ctxt());
891910
(span, format!(", {} = {}", assoc.ident, ty))
892911
} else {
893-
(span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty))
912+
let item_args = self.format_generic_args(assoc_substs);
913+
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident, item_args, ty))
894914
};
895915
db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
896916
return true;
897917
}
898918
false
899919
}
920+
921+
fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String {
922+
let mut item_args = String::new();
923+
FmtPrinter::new(self, &mut item_args, hir::def::Namespace::TypeNS)
924+
.path_generic_args(Ok, args)
925+
.expect("could not write to `String`.");
926+
item_args
927+
}
900928
}

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1289,8 +1289,22 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
12891289
self.skip_binder().projection_ty.item_def_id
12901290
}
12911291

1292+
/// Returns the `DefId` of the trait of the associated item being projected.
12921293
#[inline]
1293-
pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> {
1294+
pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
1295+
self.skip_binder().projection_ty.trait_def_id(tcx)
1296+
}
1297+
1298+
#[inline]
1299+
pub fn projection_self_ty(&self) -> Binder<Ty<'tcx>> {
1300+
self.map_bound(|predicate| predicate.projection_ty.self_ty())
1301+
}
1302+
1303+
/// Get the [PolyTraitRef] required for this projection to be well formed.
1304+
/// Note that for generic associated types the predicates of the associated
1305+
/// type also need to be checked.
1306+
#[inline]
1307+
pub fn required_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> {
12941308
// Note: unlike with `TraitRef::to_poly_trait_ref()`,
12951309
// `self.0.trait_ref` is permitted to have escaping regions.
12961310
// This is because here `self` has a `Binder` and so does our

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir as hir;
1717
use rustc_hir::def_id::DefId;
1818
use rustc_index::vec::Idx;
1919
use rustc_macros::HashStable;
20-
use rustc_span::symbol::{kw, Ident, Symbol};
20+
use rustc_span::symbol::{kw, Symbol};
2121
use rustc_target::abi::VariantIdx;
2222
use rustc_target::spec::abi;
2323
use std::borrow::Cow;
@@ -1112,20 +1112,35 @@ pub struct ProjectionTy<'tcx> {
11121112
}
11131113

11141114
impl<'tcx> ProjectionTy<'tcx> {
1115+
pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
1116+
tcx.associated_item(self.item_def_id).container.id()
1117+
}
1118+
1119+
/// Extracts the underlying trait reference and own substs from this projection.
1120+
/// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
1121+
/// then this function would return a `T: Iterator` trait reference and `['a]` as the own substs
1122+
pub fn trait_ref_and_own_substs(
1123+
&self,
1124+
tcx: TyCtxt<'tcx>,
1125+
) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
1126+
let def_id = tcx.associated_item(self.item_def_id).container.id();
1127+
let trait_generics = tcx.generics_of(def_id);
1128+
(
1129+
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, trait_generics) },
1130+
&self.substs[trait_generics.count()..],
1131+
)
1132+
}
1133+
11151134
/// Extracts the underlying trait reference from this projection.
11161135
/// For example, if this is a projection of `<T as Iterator>::Item`,
11171136
/// then this function would return a `T: Iterator` trait reference.
1137+
///
1138+
/// WARNING: This will drop the substs for generic associated types
1139+
/// consider calling [Self::trait_ref_and_own_substs] to get those
1140+
/// as well.
11181141
pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
1119-
// FIXME: This method probably shouldn't exist at all, since it's not
1120-
// clear what this method really intends to do. Be careful when
1121-
// using this method since the resulting TraitRef additionally
1122-
// contains the substs for the assoc_item, which strictly speaking
1123-
// is not correct
1124-
let def_id = tcx.associated_item(self.item_def_id).container.id();
1125-
// Include substitutions for generic arguments of associated types
1126-
let assoc_item = tcx.associated_item(self.item_def_id);
1127-
let substs_assoc_item = self.substs.truncate_to(tcx, tcx.generics_of(assoc_item.def_id));
1128-
ty::TraitRef { def_id, substs: substs_assoc_item }
1142+
let def_id = self.trait_def_id(tcx);
1143+
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, tcx.generics_of(def_id)) }
11291144
}
11301145

11311146
pub fn self_ty(&self) -> Ty<'tcx> {

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,8 +1589,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
15891589
self.emit_inference_failure_err(body_id, span, a.into(), vec![], ErrorCode::E0282)
15901590
}
15911591
ty::PredicateKind::Projection(data) => {
1592-
let trait_ref = bound_predicate.rebind(data).to_poly_trait_ref(self.tcx);
1593-
let self_ty = trait_ref.skip_binder().self_ty();
1592+
let self_ty = data.projection_ty.self_ty();
15941593
let ty = data.ty;
15951594
if predicate.references_error() {
15961595
return;

compiler/rustc_traits/src/chalk/lowering.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -779,14 +779,11 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>
779779
self,
780780
interner: &RustInterner<'tcx>,
781781
) -> chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>> {
782-
let trait_ref = self.projection_ty.trait_ref(interner.tcx);
782+
let (trait_ref, own_substs) = self.projection_ty.trait_ref_and_own_substs(interner.tcx);
783783
chalk_solve::rust_ir::AliasEqBound {
784784
trait_bound: trait_ref.lower_into(interner),
785785
associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.item_def_id),
786-
parameters: self.projection_ty.substs[trait_ref.substs.len()..]
787-
.iter()
788-
.map(|arg| arg.lower_into(interner))
789-
.collect(),
786+
parameters: own_substs.iter().map(|arg| arg.lower_into(interner)).collect(),
790787
value: self.ty.lower_into(interner),
791788
}
792789
}

compiler/rustc_typeck/src/check/closure.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
208208
});
209209

210210
// Even if we can't infer the full signature, we may be able to
211-
// infer the kind. This can occur if there is a trait-reference
211+
// infer the kind. This can occur when we elaborate a predicate
212212
// like `F : Fn<A>`. Note that due to subtyping we could encounter
213213
// many viable options, so pick the most restrictive.
214214
let expected_kind = self
@@ -234,11 +234,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
234234

235235
debug!("deduce_sig_from_projection({:?})", projection);
236236

237-
let trait_ref = projection.to_poly_trait_ref(tcx);
237+
let trait_def_id = projection.trait_def_id(tcx);
238238

239-
let is_fn = tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some();
239+
let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some();
240240
let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span);
241-
let is_gen = gen_trait == trait_ref.def_id();
241+
let is_gen = gen_trait == trait_def_id;
242242
if !is_fn && !is_gen {
243243
debug!("deduce_sig_from_projection: not fn or generator");
244244
return None;
@@ -256,7 +256,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
256256
}
257257

258258
let input_tys = if is_fn {
259-
let arg_param_ty = trait_ref.skip_binder().substs.type_at(1);
259+
let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1);
260260
let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty);
261261
debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty);
262262

@@ -662,9 +662,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
662662
};
663663

664664
// Check that this is a projection from the `Future` trait.
665-
let trait_ref = predicate.projection_ty.trait_ref(self.tcx);
665+
let trait_def_id = predicate.projection_ty.trait_def_id(self.tcx);
666666
let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span));
667-
if trait_ref.def_id != future_trait {
667+
if trait_def_id != future_trait {
668668
debug!("deduce_future_output_from_projection: not a future");
669669
return None;
670670
}

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -769,9 +769,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
769769
.filter_map(move |obligation| {
770770
let bound_predicate = obligation.predicate.kind();
771771
match bound_predicate.skip_binder() {
772-
ty::PredicateKind::Projection(data) => {
773-
Some((bound_predicate.rebind(data).to_poly_trait_ref(self.tcx), obligation))
774-
}
772+
ty::PredicateKind::Projection(data) => Some((
773+
bound_predicate.rebind(data).required_poly_trait_ref(self.tcx),
774+
obligation,
775+
)),
775776
ty::PredicateKind::Trait(data, _) => {
776777
Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation))
777778
}

compiler/rustc_typeck/src/check/method/suggest.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
2424
use rustc_trait_selection::traits::Obligation;
2525

2626
use std::cmp::Ordering;
27+
use std::iter;
2728

2829
use super::probe::Mode;
2930
use super::{CandidateSource, MethodError, NoMatchData};
@@ -649,21 +650,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
649650
ty::PredicateKind::Projection(pred) => {
650651
let pred = bound_predicate.rebind(pred);
651652
// `<Foo as Iterator>::Item = String`.
652-
let trait_ref =
653-
pred.skip_binder().projection_ty.trait_ref(self.tcx);
654-
let assoc = self
655-
.tcx
656-
.associated_item(pred.skip_binder().projection_ty.item_def_id);
657-
let ty = pred.skip_binder().ty;
658-
let obligation = format!("{}::{} = {}", trait_ref, assoc.ident, ty);
659-
let quiet = format!(
660-
"<_ as {}>::{} = {}",
661-
trait_ref.print_only_trait_path(),
662-
assoc.ident,
663-
ty
653+
let projection_ty = pred.skip_binder().projection_ty;
654+
655+
let substs_with_infer_self = tcx.mk_substs(
656+
iter::once(tcx.mk_ty_var(ty::TyVid { index: 0 }).into())
657+
.chain(projection_ty.substs.iter().skip(1)),
664658
);
665-
bound_span_label(trait_ref.self_ty(), &obligation, &quiet);
666-
Some((obligation, trait_ref.self_ty()))
659+
660+
let quiet_projection_ty = ty::ProjectionTy {
661+
substs: substs_with_infer_self,
662+
item_def_id: projection_ty.item_def_id,
663+
};
664+
665+
let ty = pred.skip_binder().ty;
666+
667+
let obligation = format!("{} = {}", projection_ty, ty);
668+
let quiet = format!("{} = {}", quiet_projection_ty, ty);
669+
670+
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
671+
Some((obligation, projection_ty.self_ty()))
667672
}
668673
ty::PredicateKind::Trait(poly_trait_ref, _) => {
669674
let p = poly_trait_ref.trait_ref;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test that correct syntax is used in suggestion to constrain associated type
2+
3+
#![feature(generic_associated_types)]
4+
//~^ WARNING the feature `generic_associated_types` is incomplete
5+
6+
trait X {
7+
type Y<T>;
8+
}
9+
10+
fn f<T: X>(a: T::Y<i32>) {
11+
//~^ HELP consider constraining the associated type `<T as X>::Y<i32>` to `Vec<i32>`
12+
//~| SUGGESTION Y<i32> = Vec<i32>>
13+
let b: Vec<i32> = a;
14+
//~^ ERROR mismatched types
15+
}
16+
17+
fn main() {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/constraint-assoc-type-suggestion.rs:3:12
3+
|
4+
LL | #![feature(generic_associated_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/constraint-assoc-type-suggestion.rs:13:23
12+
|
13+
LL | let b: Vec<i32> = a;
14+
| -------- ^ expected struct `Vec`, found associated type
15+
| |
16+
| expected due to this
17+
|
18+
= note: expected struct `Vec<i32>`
19+
found associated type `<T as X>::Y<i32>`
20+
help: consider constraining the associated type `<T as X>::Y<i32>` to `Vec<i32>`
21+
|
22+
LL | fn f<T: X<Y<i32> = Vec<i32>>>(a: T::Y<i32>) {
23+
| ^^^^^^^^^^^^^^^^^^^
24+
25+
error: aborting due to previous error; 1 warning emitted
26+
27+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)