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:
commit
deec6a96d4
43 changed files with 1051 additions and 112 deletions
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 } => {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue