Skip to content

Commit db39028

Browse files
committed
Delegation: support generics in trait impls
1 parent f31cdc5 commit db39028

File tree

9 files changed

+510
-344
lines changed

9 files changed

+510
-344
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ hir_analysis_must_implement_not_function_span_note = required by this annotation
310310
hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
311311
312312
hir_analysis_not_supported_delegation =
313-
{$descr} is not supported yet
313+
{$descr}
314314
.label = callee defined here
315315
316316
hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate
Lines changed: 164 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use itertools::Itertools;
12
use rustc_data_structures::fx::FxHashMap;
23
use rustc_hir::def::DefKind;
34
use rustc_hir::def_id::{DefId, LocalDefId};
45
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
56
use rustc_middle::ty::{self, Ty, TyCtxt};
67
use rustc_span::ErrorGuaranteed;
8+
use rustc_type_ir::visit::TypeVisitableExt;
79

810
type RemapTable = FxHashMap<u32, u32>;
911

@@ -18,21 +20,39 @@ impl<'tcx, 'a> TypeFolder<TyCtxt<'tcx>> for IndicesFolder<'tcx, 'a> {
1820
}
1921

2022
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
21-
if let ty::Param(param) = ty.kind() {
22-
return Ty::new_param(self.tcx, self.remap_table[&param.index], param.name);
23+
if !ty.has_param() {
24+
return ty;
25+
}
26+
27+
if let ty::Param(param) = ty.kind()
28+
&& let Some(idx) = self.remap_table.get(&param.index)
29+
{
30+
return Ty::new_param(self.tcx, *idx, param.name);
2331
}
2432
ty.super_fold_with(self)
2533
}
2634

2735
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
28-
if let ty::ReEarlyParam(param) = r.kind() {
36+
if let ty::ReEarlyParam(param) = r.kind()
37+
&& let Some(idx) = self.remap_table.get(&param.index)
38+
{
2939
return ty::Region::new_early_param(
3040
self.tcx,
31-
ty::EarlyParamRegion { index: self.remap_table[&param.index], name: param.name },
41+
ty::EarlyParamRegion { index: *idx, name: param.name },
3242
);
3343
}
3444
r
3545
}
46+
47+
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
48+
if let ty::ConstKind::Param(param) = ct.kind()
49+
&& let Some(idx) = self.remap_table.get(&param.index)
50+
{
51+
let param = ty::ParamConst::new(*idx, param.name);
52+
return ty::Const::new_param(self.tcx, param, ct.ty());
53+
}
54+
ct.super_fold_with(self)
55+
}
3656
}
3757

3858
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -55,28 +75,76 @@ fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
5575
}
5676
}
5777

58-
// Lifetime parameters must be declared before type and const parameters.
59-
// Therefore, When delegating from a free function to a associated function,
60-
// generic parameters need to be reordered:
61-
//
62-
// trait Trait<'a, A> {
63-
// fn foo<'b, B>(...) {...}
64-
// }
65-
//
66-
// reuse Trait::foo;
67-
// desugaring:
68-
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
69-
// Trait::foo(...)
70-
// }
71-
fn create_remap_table<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig_id: DefId) -> RemapTable {
78+
fn create_generic_args<'tcx>(
79+
tcx: TyCtxt<'tcx>,
80+
def_id: LocalDefId,
81+
sig_id: DefId,
82+
) -> ty::GenericArgsRef<'tcx> {
7283
let caller_generics = tcx.generics_of(def_id);
7384
let callee_generics = tcx.generics_of(sig_id);
74-
let mut remap_table: RemapTable = FxHashMap::default();
75-
for caller_param in &caller_generics.own_params {
76-
let callee_index = callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
77-
remap_table.insert(callee_index, caller_param.index);
85+
86+
let caller_kind = fn_kind(tcx, def_id.into());
87+
let callee_kind = fn_kind(tcx, sig_id);
88+
// FIXME(fn_delegation): early bound generics are only supported for trait
89+
// implementations and free functions. Error was reported in `check_constraints`.
90+
match (caller_kind, callee_kind) {
91+
(FnKind::Free, _) => {
92+
let args = ty::GenericArgs::identity_for_item(tcx, sig_id);
93+
// Lifetime parameters must be declared before type and const parameters.
94+
// Therefore, When delegating from a free function to a associated function,
95+
// generic parameters need to be reordered:
96+
//
97+
// trait Trait<'a, A> {
98+
// fn foo<'b, B>(...) {...}
99+
// }
100+
//
101+
// reuse Trait::foo;
102+
// desugaring:
103+
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
104+
// Trait::foo(...)
105+
// }
106+
let mut remap_table: RemapTable = FxHashMap::default();
107+
for caller_param in &caller_generics.own_params {
108+
let callee_index =
109+
callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
110+
remap_table.insert(callee_index, caller_param.index);
111+
}
112+
let mut folder = IndicesFolder { tcx, remap_table: &remap_table };
113+
args.fold_with(&mut folder)
114+
}
115+
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
116+
let parent = tcx.parent(def_id.into());
117+
let parent_args =
118+
tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args;
119+
120+
let callee_generics = tcx.generics_of(sig_id);
121+
let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
122+
let method_args = trait_args.iter().skip(callee_generics.parent_count).collect_vec();
123+
124+
// For trait implementations only the method's own parameters are copied.
125+
// They need to be reindexed adjusted for impl parameters.
126+
let mut remap_table: RemapTable = FxHashMap::default();
127+
let parent_count = caller_generics.parent_count as u32;
128+
for (idx, callee_own_param) in callee_generics.own_params.iter().enumerate() {
129+
let callee_index = callee_own_param.index as u32;
130+
remap_table.insert(callee_index, idx as u32 + parent_count);
131+
}
132+
let mut folder = IndicesFolder { tcx, remap_table: &remap_table };
133+
let method_args = method_args.fold_with(&mut folder);
134+
135+
tcx.mk_args_from_iter(parent_args.iter().chain(method_args))
136+
}
137+
// only `Self` param supported here
138+
(FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
139+
let parent = tcx.parent(def_id.into());
140+
let self_ty = tcx.type_of(parent).instantiate_identity();
141+
let generic_self_ty = ty::GenericArg::from(self_ty);
142+
tcx.mk_args_from_iter(std::iter::once(generic_self_ty))
143+
}
144+
// `sig_id` is taken from corresponding trait method
145+
(FnKind::AssocTraitImpl, _) => unreachable!(),
146+
_ => ty::GenericArgs::identity_for_item(tcx, sig_id),
78147
}
79-
remap_table
80148
}
81149

82150
pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
@@ -87,8 +155,12 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
87155
let caller_kind = fn_kind(tcx, def_id.into());
88156
let callee_kind = fn_kind(tcx, sig_id);
89157

90-
// FIXME(fn_delegation): Support generics on associated delegation items.
91-
// Error was reported in `check_delegation_constraints`.
158+
let param_def_id_to_index = |own_params: &Vec<ty::GenericParamDef>| {
159+
own_params.iter().map(|param| (param.def_id, param.index)).collect()
160+
};
161+
162+
// FIXME(fn_delegation): early bound generics are only supported for trait
163+
// implementations and free functions. Error was reported in `check_constraints`.
92164
match (caller_kind, callee_kind) {
93165
(FnKind::Free, _) => {
94166
let mut own_params = vec![];
@@ -112,14 +184,33 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
112184
}
113185
}
114186

115-
let param_def_id_to_index =
116-
own_params.iter().map(|param| (param.def_id, param.index)).collect();
117-
118187
Some(ty::Generics {
119188
parent: None,
120189
parent_count: 0,
190+
param_def_id_to_index: param_def_id_to_index(&own_params),
191+
own_params,
192+
has_self: false,
193+
has_late_bound_regions: callee_generics.has_late_bound_regions,
194+
host_effect_index: None,
195+
})
196+
}
197+
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
198+
let callee_generics = tcx.generics_of(sig_id);
199+
200+
let parent = tcx.parent(def_id.into());
201+
let parent_generics = tcx.generics_of(parent);
202+
let parent_count = parent_generics.count();
203+
204+
let mut own_params = tcx.generics_of(sig_id).own_params.clone();
205+
for (idx, param) in own_params.iter_mut().enumerate() {
206+
param.index = (parent_count + idx) as u32;
207+
}
208+
209+
Some(ty::Generics {
210+
parent: Some(parent),
211+
parent_count,
212+
param_def_id_to_index: param_def_id_to_index(&own_params),
121213
own_params,
122-
param_def_id_to_index,
123214
has_self: false,
124215
has_late_bound_regions: callee_generics.has_late_bound_regions,
125216
host_effect_index: None,
@@ -134,41 +225,38 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
134225
def_id: LocalDefId,
135226
sig_id: DefId,
136227
) -> Option<ty::GenericPredicates<'tcx>> {
228+
// FIXME(fn_delegation): early bound generics are only supported for trait
229+
// implementations and free functions. Error was reported in `check_constraints`.
137230
let caller_kind = fn_kind(tcx, def_id.into());
138-
let callee_kind = fn_kind(tcx, sig_id);
139-
140-
// FIXME(fn_delegation): Support generics on associated delegation items.
141-
// Error was reported in `check_delegation_constraints`.
142-
match (caller_kind, callee_kind) {
143-
(FnKind::Free, _) => {
144-
let mut predicates = vec![];
145-
let callee_predicates = tcx.predicates_of(sig_id);
146-
if let Some(parent_sig_id) = callee_predicates.parent {
147-
let parent_sig_predicates = tcx.predicates_of(parent_sig_id);
148-
predicates.extend_from_slice(parent_sig_predicates.predicates);
149-
}
150-
predicates.extend_from_slice(callee_predicates.predicates);
231+
if caller_kind != FnKind::Free && caller_kind != FnKind::AssocTraitImpl {
232+
return None;
233+
}
151234

152-
let remap_table = create_remap_table(tcx, def_id, sig_id);
153-
let mut folder = IndicesFolder { tcx, remap_table: &remap_table };
154-
let predicates = predicates.fold_with(&mut folder);
235+
let callee_predicates = tcx.predicates_of(sig_id);
236+
let args = create_generic_args(tcx, def_id, sig_id);
155237

156-
Some(ty::GenericPredicates {
157-
parent: None,
158-
predicates: tcx.arena.alloc_from_iter(predicates),
159-
})
160-
}
161-
_ => None,
238+
let mut preds = vec![];
239+
if let Some(parent_id) = callee_predicates.parent {
240+
preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args));
162241
}
242+
preds.extend(callee_predicates.instantiate_own(tcx, args));
243+
244+
let parent = match fn_kind(tcx, def_id.to_def_id()) {
245+
FnKind::Free => None,
246+
_ => Some(tcx.parent(def_id.into())),
247+
};
248+
249+
Some(ty::GenericPredicates { parent, predicates: tcx.arena.alloc_from_iter(preds) })
163250
}
164251

165252
fn check_constraints<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
166253
let mut ret = Ok(());
167254
let sig_id = tcx.hir().delegation_sig_id(def_id);
255+
let span = tcx.def_span(def_id);
168256

169257
let mut emit = |descr| {
170258
ret = Err(tcx.dcx().emit_err(crate::errors::UnsupportedDelegation {
171-
span: tcx.def_span(def_id),
259+
span,
172260
descr,
173261
callee_span: tcx.def_span(sig_id),
174262
}));
@@ -177,20 +265,33 @@ fn check_constraints<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Result<(),
177265
if let Some(local_sig_id) = sig_id.as_local()
178266
&& tcx.hir().opt_delegation_sig_id(local_sig_id).is_some()
179267
{
180-
emit("recursive delegation");
268+
emit("recursive delegation is not supported yet");
181269
}
182270

183271
let caller_kind = fn_kind(tcx, def_id.into());
184-
if caller_kind != FnKind::Free {
185-
let sig_generics = tcx.generics_of(sig_id);
186-
let parent = tcx.parent(def_id.into());
187-
let parent_generics = tcx.generics_of(parent);
272+
let callee_kind = fn_kind(tcx, sig_id);
273+
274+
match (caller_kind, callee_kind) {
275+
(FnKind::Free, _) | (FnKind::AssocTraitImpl, FnKind::AssocTrait) => {}
276+
// `sig_id` is taken from corresponding trait method
277+
(FnKind::AssocTraitImpl, _) => {
278+
return Err(tcx
279+
.dcx()
280+
.span_delayed_bug(span, "unexpected callee path resolution in delegation item"));
281+
}
282+
_ => {
283+
let sig_generics = tcx.generics_of(sig_id);
284+
let parent = tcx.parent(def_id.into());
285+
let parent_generics = tcx.generics_of(parent);
188286

189-
let parent_is_trait = (tcx.def_kind(parent) == DefKind::Trait) as usize;
190-
let sig_has_self = sig_generics.has_self as usize;
287+
let sig_has_self = sig_generics.has_self as usize;
288+
let parent_has_self = parent_generics.has_self as usize;
191289

192-
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait {
193-
emit("early bound generics are not supported for associated delegation items");
290+
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_has_self {
291+
emit(
292+
"early bound generics are only supported for trait implementations and free functions",
293+
);
294+
}
194295
}
195296
}
196297

@@ -208,32 +309,11 @@ pub(crate) fn inherit_sig_for_delegation_item<'tcx>(
208309
let err_type = Ty::new_error(tcx, err);
209310
return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type));
210311
}
312+
let args = create_generic_args(tcx, def_id, sig_id);
211313

212-
let caller_kind = fn_kind(tcx, def_id.into());
213-
let callee_kind = fn_kind(tcx, sig_id);
214-
215-
// FIXME(fn_delegation): Support generics on associated delegation items.
216-
// Error was reported in `check_constraints`.
217-
let sig = match (caller_kind, callee_kind) {
218-
(FnKind::Free, _) => {
219-
let remap_table = create_remap_table(tcx, def_id, sig_id);
220-
let mut folder = IndicesFolder { tcx, remap_table: &remap_table };
221-
caller_sig.instantiate_identity().fold_with(&mut folder)
222-
}
223-
// only `Self` param supported here
224-
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
225-
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
226-
let parent = tcx.parent(def_id.into());
227-
let self_ty = tcx.type_of(parent).instantiate_identity();
228-
let generic_self_ty = ty::GenericArg::from(self_ty);
229-
let args = tcx.mk_args_from_iter(std::iter::once(generic_self_ty));
230-
caller_sig.instantiate(tcx, args)
231-
}
232-
_ => caller_sig.instantiate_identity(),
233-
};
234314
// Bound vars are also inherited from `sig_id`.
235315
// They will be rebound later in `lower_fn_ty`.
236-
let sig = sig.skip_binder();
316+
let sig = caller_sig.instantiate(tcx, args).skip_binder();
237317
let sig_it = sig.inputs().iter().cloned().chain(std::iter::once(sig.output()));
238318
tcx.arena.alloc_from_iter(sig_it)
239319
}

0 commit comments

Comments
 (0)