1
Fork 0

Rollup merge of #79554 - b-naber:generic-associated-types-in-trait-paths, r=jackh726

Generic associated types in trait paths

This is the second part of https://github.com/rust-lang/rust/pull/78978

This should fix:

Fixes #67510
Fixes #68648
Fixes #68649
Fixes #68650
Fixes #68652
Fixes #74684
Fixes #76535
Fixes #79422
Fixes #80433

and implement the remaining functionality needed for https://github.com/rust-lang/rust/issues/44265

r? ``@matthewjasper``
This commit is contained in:
Mara Bos 2021-02-05 12:25:50 +01:00 committed by GitHub
commit deec6a96d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 1051 additions and 112 deletions

View file

@ -1076,16 +1076,40 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_assoc_ty_constraint(
&mut self,
constraint: &AssocTyConstraint,
itctx: ImplTraitContext<'_, 'hir>,
mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::TypeBinding<'hir> {
debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", constraint, itctx);
if let Some(ref gen_args) = constraint.gen_args {
self.sess.span_fatal(
gen_args.span(),
"generic associated types in trait paths are currently not implemented",
);
}
// lower generic arguments of identifier in constraint
let gen_args = if let Some(ref gen_args) = constraint.gen_args {
let gen_args_ctor = match gen_args {
GenericArgs::AngleBracketed(ref data) => {
self.lower_angle_bracketed_parameter_data(
data,
ParamMode::Explicit,
itctx.reborrow(),
)
.0
}
GenericArgs::Parenthesized(ref data) => {
let mut err = self.sess.struct_span_err(
gen_args.span(),
"parenthesized generic arguments cannot be used in associated type constraints"
);
// FIXME: try to write a suggestion here
err.emit();
self.lower_angle_bracketed_parameter_data(
&data.as_angle_bracketed_args(),
ParamMode::Explicit,
itctx.reborrow(),
)
.0
}
};
self.arena.alloc(gen_args_ctor.into_generic_args(&self.arena))
} else {
self.arena.alloc(hir::GenericArgs::none())
};
let kind = match constraint.kind {
AssocTyConstraintKind::Equality { ref ty } => {
@ -1182,6 +1206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::TypeBinding {
hir_id: self.lower_node_id(constraint.id),
ident: constraint.ident,
gen_args,
kind,
span: constraint.span,
}

View file

@ -362,7 +362,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
fn lower_angle_bracketed_parameter_data(
pub(crate) fn lower_angle_bracketed_parameter_data(
&mut self,
data: &AngleBracketedArgs,
param_mode: ParamMode,
@ -426,6 +426,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> hir::TypeBinding<'hir> {
let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME);
let kind = hir::TypeBindingKind::Equality { ty };
hir::TypeBinding { hir_id: self.next_id(), span, ident, kind }
let args = arena_vec![self;];
let bindings = arena_vec![self;];
let gen_args = self.arena.alloc(hir::GenericArgs { args, bindings, parenthesized: false });
hir::TypeBinding { hir_id: self.next_id(), gen_args, span, ident, kind }
}
}

View file

@ -2015,6 +2015,7 @@ pub struct TypeBinding<'hir> {
pub hir_id: HirId,
#[stable_hasher(project(name))]
pub ident: Ident,
pub gen_args: &'hir GenericArgs<'hir>,
pub kind: TypeBindingKind<'hir>,
pub span: Span,
}

View file

@ -781,6 +781,7 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(
) {
visitor.visit_id(type_binding.hir_id);
visitor.visit_ident(type_binding.ident);
visitor.visit_generic_args(type_binding.span, type_binding.gen_args);
match type_binding.kind {
TypeBindingKind::Equality { ref ty } => {
visitor.visit_ty(ty);

View file

@ -1840,6 +1840,7 @@ impl<'a> State<'a> {
for binding in generic_args.bindings.iter() {
start_or_comma(self);
self.print_ident(binding.ident);
self.print_generic_args(binding.gen_args, false, false);
self.s.space();
match generic_args.bindings[0].kind {
hir::TypeBindingKind::Equality { ref ty } => {

View file

@ -1132,8 +1132,16 @@ impl<'tcx> ProjectionTy<'tcx> {
/// For example, if this is a projection of `<T as Iterator>::Item`,
/// then this function would return a `T: Iterator` trait reference.
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();
ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, tcx.generics_of(def_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> {

View file

@ -257,13 +257,11 @@ fn predicates_reference_self(
}
fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> {
let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
tcx.associated_items(trait_def_id)
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.flat_map(|item| tcx.explicit_item_bounds(item.def_id))
.map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp))
.filter_map(|predicate| predicate_references_self(tcx, predicate))
.filter_map(|pred_span| predicate_references_self(tcx, *pred_span))
.collect()
}

View file

@ -112,12 +112,15 @@ pub enum SizedByDefault {
No,
}
#[derive(Debug)]
struct ConvertedBinding<'a, 'tcx> {
item_name: Ident,
kind: ConvertedBindingKind<'a, 'tcx>,
gen_args: &'a GenericArgs<'a>,
span: Span,
}
#[derive(Debug)]
enum ConvertedBindingKind<'a, 'tcx> {
Equality(Ty<'tcx>),
Constraint(&'a [hir::GenericBound<'a>]),
@ -323,6 +326,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let tcx = self.tcx();
let generics = tcx.generics_of(def_id);
debug!("generics: {:?}", generics);
if generics.has_self {
if generics.parent.is_some() {
@ -557,7 +561,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
ConvertedBindingKind::Constraint(bounds)
}
};
ConvertedBinding { item_name: binding.ident, kind, span: binding.span }
ConvertedBinding {
item_name: binding.ident,
kind,
gen_args: binding.gen_args,
span: binding.span,
}
})
.collect();
@ -918,61 +927,28 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
dup_bindings: &mut FxHashMap<DefId, Span>,
path_span: Span,
) -> Result<(), ErrorReported> {
// Given something like `U: SomeTrait<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
// subtle in the event that `T` is defined in a supertrait of
// `SomeTrait`, because in that case we need to upcast.
//
// That is, consider this case:
//
// ```
// trait SubTrait: SuperTrait<i32> { }
// trait SuperTrait<A> { type T; }
//
// ... B: SubTrait<T = foo> ...
// ```
//
// We want to produce `<B as SuperTrait<i32>>::T == foo`.
debug!(
"add_predicates_for_ast_type_binding(hir_ref_id {:?}, trait_ref {:?}, binding {:?}, bounds {:?}",
hir_ref_id, trait_ref, binding, bounds
);
let tcx = self.tcx();
if !speculative {
// Given something like `U: SomeTrait<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
// subtle in the event that `T` is defined in a supertrait of
// `SomeTrait`, because in that case we need to upcast.
//
// That is, consider this case:
//
// ```
// trait SubTrait: SuperTrait<i32> { }
// trait SuperTrait<A> { type T; }
//
// ... B: SubTrait<T = foo> ...
// ```
//
// We want to produce `<B as SuperTrait<i32>>::T == foo`.
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
if let ConvertedBindingKind::Equality(ty) = binding.kind {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&trait_ref);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty));
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
self.validate_late_bound_regions(
late_bound_in_trait_ref,
late_bound_in_ty,
|br_name| {
struct_span_err!(
tcx.sess,
binding.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
binding.item_name,
br_name
)
},
);
}
}
let candidate =
if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
// Simple case: X is defined in the current trait.
@ -1030,6 +1006,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.or_insert(binding.span);
}
// Include substitutions for generic parameters of associated types
let projection_ty = candidate.map_bound(|trait_ref| {
let item_segment = hir::PathSegment {
ident: assoc_ty.ident,
hir_id: None,
res: None,
args: Some(binding.gen_args),
infer_args: false,
};
let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
tcx,
path_span,
assoc_ty.def_id,
&item_segment,
trait_ref.substs,
);
debug!(
"add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}",
substs_trait_ref_and_assoc_item
);
ty::ProjectionTy {
item_def_id: assoc_ty.def_id,
substs: substs_trait_ref_and_assoc_item,
}
});
if !speculative {
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_ty. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
if let ConvertedBindingKind::Equality(ty) = binding.kind {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty));
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
self.validate_late_bound_regions(
late_bound_in_trait_ref,
late_bound_in_ty,
|br_name| {
struct_span_err!(
tcx.sess,
binding.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
binding.item_name,
br_name
)
},
);
}
}
match binding.kind {
ConvertedBindingKind::Equality(ref ty) => {
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
@ -1037,13 +1079,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
//
// `<T as Iterator>::Item = u32`
bounds.projection_bounds.push((
candidate.map_bound(|trait_ref| ty::ProjectionPredicate {
projection_ty: ty::ProjectionTy::from_ref_and_name(
tcx,
trait_ref,
binding.item_name,
),
ty,
projection_ty.map_bound(|projection_ty| {
debug!(
"add_predicates_for_ast_type_binding: projection_ty {:?}, substs: {:?}",
projection_ty, projection_ty.substs
);
ty::ProjectionPredicate { projection_ty, ty }
}),
binding.span,
));
@ -1055,7 +1096,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
//
// Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
// parameter to have a skipped binder.
let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs);
let param_ty =
tcx.mk_projection(assoc_ty.def_id, projection_ty.skip_binder().substs);
self.add_bounds(param_ty, ast_bounds, bounds);
}
}