Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure valid TraitRefs are created for GATs #82066

Merged
merged 8 commits into from
Feb 18, 2021
1 change: 1 addition & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ impl<'a> State<'a> {

pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) {
self.print_ident(constraint.ident);
constraint.gen_args.as_ref().map(|args| self.print_generic_args(args, false));
self.s.space();
match &constraint.kind {
ast::AssocTyConstraintKind::Equality { ty } => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ language_item_table! {

Deref, sym::deref, deref_trait, Target::Trait;
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait;
DerefTarget, sym::deref_target, deref_target, Target::AssocTy;
Receiver, sym::receiver, receiver_trait, Target::Trait;

Fn, kw::Fn, fn_trait, Target::Trait;
Expand Down
25 changes: 24 additions & 1 deletion compiler/rustc_infer/src/infer/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
fn to_trace(
tcx: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
Expand Down Expand Up @@ -178,7 +179,7 @@ impl<'a, 'tcx> At<'a, 'tcx> {
where
T: ToTrace<'tcx>,
{
let trace = ToTrace::to_trace(self.cause, a_is_expected, a, b);
let trace = ToTrace::to_trace(self.infcx.tcx, self.cause, a_is_expected, a, b);
Trace { at: self, trace, a_is_expected }
}
}
Expand Down Expand Up @@ -251,6 +252,7 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {

impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
Expand All @@ -262,6 +264,7 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {

impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
Expand All @@ -273,6 +276,7 @@ impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> {

impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
Expand All @@ -284,6 +288,7 @@ impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> {

impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
Expand All @@ -298,6 +303,7 @@ impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {

impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
Expand All @@ -309,3 +315,20 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> {
}
}
}

impl<'tcx> ToTrace<'tcx> for ty::ProjectionTy<'tcx> {
fn to_trace(
tcx: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
b: Self,
) -> TypeTrace<'tcx> {
let a_ty = tcx.mk_projection(a.item_def_id, a.substs);
let b_ty = tcx.mk_projection(b.item_def_id, b.substs);
TypeTrace {
cause: cause.clone(),
values: Types(ExpectedFound::new(a_is_expected, a_ty, b_ty)),
}
}
}
40 changes: 34 additions & 6 deletions compiler/rustc_middle/src/ty/error.rs
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
70 changes: 41 additions & 29 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
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,36 +1112,35 @@ pub struct ProjectionTy<'tcx> {
}

impl<'tcx> ProjectionTy<'tcx> {
/// Construct a `ProjectionTy` by searching the trait from `trait_ref` for the
/// associated item named `item_name`.
pub fn from_ref_and_name(
tcx: TyCtxt<'_>,
trait_ref: ty::TraitRef<'tcx>,
item_name: Ident,
) -> ProjectionTy<'tcx> {
let item_def_id = tcx
.associated_items(trait_ref.def_id)
.find_by_name_and_kind(tcx, item_name, ty::AssocKind::Type, trait_ref.def_id)
.unwrap()
.def_id;
pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
tcx.associated_item(self.item_def_id).container.id()
}

ProjectionTy { substs: trait_ref.substs, item_def_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 @@ -1493,12 +1492,11 @@ impl<'tcx> ExistentialProjection<'tcx> {
/// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`,
/// then this function would return a `exists T. T: Iterator` existential trait
/// reference.
pub fn trait_ref(&self, tcx: TyCtxt<'_>) -> ty::ExistentialTraitRef<'tcx> {
// FIXME(generic_associated_types): substs is the substs of the
// associated type, which should be truncated to get the correct substs
// for the trait.
pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::ExistentialTraitRef<'tcx> {
let def_id = tcx.associated_item(self.item_def_id).container.id();
ty::ExistentialTraitRef { def_id, substs: self.substs }
let subst_count = tcx.generics_of(def_id).count() - 1;
let substs = tcx.intern_substs(&self.substs[..subst_count]);
ty::ExistentialTraitRef { def_id, substs }
}

pub fn with_self_ty(
Expand All @@ -1517,6 +1515,20 @@ impl<'tcx> ExistentialProjection<'tcx> {
ty: self.ty,
}
}

pub fn erase_self_ty(
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
tcx: TyCtxt<'tcx>,
projection_predicate: ty::ProjectionPredicate<'tcx>,
) -> Self {
// Assert there is a Self.
projection_predicate.projection_ty.substs.type_at(0);

Self {
item_def_id: projection_predicate.projection_ty.item_def_id,
substs: tcx.intern_substs(&projection_predicate.projection_ty.substs[1..]),
ty: projection_predicate.ty,
}
}
}

impl<'tcx> PolyExistentialProjection<'tcx> {
Expand Down
Loading