Skip to content

Commit f31cdc5

Browse files
committed
Delegation: support generics for delegation from free functions
1 parent f2e1a3a commit f31cdc5

21 files changed

+567
-212
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
//! HIR ty lowering.
3434
//!
3535
//! Similarly generics, predicates and header are set to the "default" values.
36-
//! In case of discrepancy with callee function the `NotSupportedDelegation` error will
36+
//! In case of discrepancy with callee function the `UnsupportedDelegation` error will
3737
//! also be emitted during HIR ty lowering.
3838
3939
use crate::{ImplTraitPosition, ResolverAstLoweringExt};

compiler/rustc_hir_analysis/src/collect/generics_of.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::delegation::inherit_generics_for_delegation_item;
12
use crate::middle::resolve_bound_vars as rbv;
23
use hir::{
34
intravisit::{self, Visitor},
@@ -51,6 +52,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
5152
};
5253
}
5354

55+
// For a delegation item inherit generics from callee.
56+
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
57+
&& let Some(generics) = inherit_generics_for_delegation_item(tcx, def_id, sig_id)
58+
{
59+
return generics;
60+
}
61+
5462
let hir_id = tcx.local_def_id_to_hir_id(def_id);
5563

5664
let node = tcx.hir_node(hir_id);

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::bounds::Bounds;
22
use crate::collect::ItemCtxt;
33
use crate::constrained_generic_params as cgp;
4+
use crate::delegation::inherit_predicates_for_delegation_item;
45
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter};
56
use hir::{HirId, Node};
67
use rustc_data_structures::fx::FxIndexSet;
@@ -111,6 +112,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
111112
None => {}
112113
}
113114

115+
// For a delegation item inherit predicates from callee.
116+
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
117+
&& let Some(predicates) = inherit_predicates_for_delegation_item(tcx, def_id, sig_id)
118+
{
119+
return predicates;
120+
}
121+
114122
let hir_id = tcx.local_def_id_to_hir_id(def_id);
115123
let node = tcx.hir_node(hir_id);
116124

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
use rustc_data_structures::fx::FxHashMap;
2+
use rustc_hir::def::DefKind;
3+
use rustc_hir::def_id::{DefId, LocalDefId};
4+
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
5+
use rustc_middle::ty::{self, Ty, TyCtxt};
6+
use rustc_span::ErrorGuaranteed;
7+
8+
type RemapTable = FxHashMap<u32, u32>;
9+
10+
struct IndicesFolder<'tcx, 'a> {
11+
tcx: TyCtxt<'tcx>,
12+
remap_table: &'a RemapTable,
13+
}
14+
15+
impl<'tcx, 'a> TypeFolder<TyCtxt<'tcx>> for IndicesFolder<'tcx, 'a> {
16+
fn interner(&self) -> TyCtxt<'tcx> {
17+
self.tcx
18+
}
19+
20+
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+
}
24+
ty.super_fold_with(self)
25+
}
26+
27+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
28+
if let ty::ReEarlyParam(param) = r.kind() {
29+
return ty::Region::new_early_param(
30+
self.tcx,
31+
ty::EarlyParamRegion { index: self.remap_table[&param.index], name: param.name },
32+
);
33+
}
34+
r
35+
}
36+
}
37+
38+
#[derive(Clone, Copy, Debug, PartialEq)]
39+
enum FnKind {
40+
Free,
41+
AssocInherentImpl,
42+
AssocTrait,
43+
AssocTraitImpl,
44+
}
45+
46+
fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
47+
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn));
48+
49+
let parent = tcx.parent(def_id);
50+
match tcx.def_kind(parent) {
51+
DefKind::Trait => FnKind::AssocTrait,
52+
DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl,
53+
DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl,
54+
_ => FnKind::Free,
55+
}
56+
}
57+
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 {
72+
let caller_generics = tcx.generics_of(def_id);
73+
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);
78+
}
79+
remap_table
80+
}
81+
82+
pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
83+
tcx: TyCtxt<'tcx>,
84+
def_id: LocalDefId,
85+
sig_id: DefId,
86+
) -> Option<ty::Generics> {
87+
let caller_kind = fn_kind(tcx, def_id.into());
88+
let callee_kind = fn_kind(tcx, sig_id);
89+
90+
// FIXME(fn_delegation): Support generics on associated delegation items.
91+
// Error was reported in `check_delegation_constraints`.
92+
match (caller_kind, callee_kind) {
93+
(FnKind::Free, _) => {
94+
let mut own_params = vec![];
95+
96+
let callee_generics = tcx.generics_of(sig_id);
97+
if let Some(parent_sig_id) = callee_generics.parent {
98+
let parent_sig_generics = tcx.generics_of(parent_sig_id);
99+
own_params.append(&mut parent_sig_generics.own_params.clone());
100+
}
101+
own_params.append(&mut callee_generics.own_params.clone());
102+
103+
// lifetimes go first
104+
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
105+
106+
for (idx, param) in own_params.iter_mut().enumerate() {
107+
param.index = idx as u32;
108+
// Default type parameters are not inherited: they are not allowed
109+
// in fn's.
110+
if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind {
111+
param.kind = ty::GenericParamDefKind::Type { has_default: false, synthetic }
112+
}
113+
}
114+
115+
let param_def_id_to_index =
116+
own_params.iter().map(|param| (param.def_id, param.index)).collect();
117+
118+
Some(ty::Generics {
119+
parent: None,
120+
parent_count: 0,
121+
own_params,
122+
param_def_id_to_index,
123+
has_self: false,
124+
has_late_bound_regions: callee_generics.has_late_bound_regions,
125+
host_effect_index: None,
126+
})
127+
}
128+
_ => None,
129+
}
130+
}
131+
132+
pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
133+
tcx: TyCtxt<'tcx>,
134+
def_id: LocalDefId,
135+
sig_id: DefId,
136+
) -> Option<ty::GenericPredicates<'tcx>> {
137+
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);
151+
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);
155+
156+
Some(ty::GenericPredicates {
157+
parent: None,
158+
predicates: tcx.arena.alloc_from_iter(predicates),
159+
})
160+
}
161+
_ => None,
162+
}
163+
}
164+
165+
fn check_constraints<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
166+
let mut ret = Ok(());
167+
let sig_id = tcx.hir().delegation_sig_id(def_id);
168+
169+
let mut emit = |descr| {
170+
ret = Err(tcx.dcx().emit_err(crate::errors::UnsupportedDelegation {
171+
span: tcx.def_span(def_id),
172+
descr,
173+
callee_span: tcx.def_span(sig_id),
174+
}));
175+
};
176+
177+
if let Some(local_sig_id) = sig_id.as_local()
178+
&& tcx.hir().opt_delegation_sig_id(local_sig_id).is_some()
179+
{
180+
emit("recursive delegation");
181+
}
182+
183+
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);
188+
189+
let parent_is_trait = (tcx.def_kind(parent) == DefKind::Trait) as usize;
190+
let sig_has_self = sig_generics.has_self as usize;
191+
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");
194+
}
195+
}
196+
197+
ret
198+
}
199+
200+
pub(crate) fn inherit_sig_for_delegation_item<'tcx>(
201+
tcx: TyCtxt<'tcx>,
202+
def_id: LocalDefId,
203+
) -> &'tcx [Ty<'tcx>] {
204+
let sig_id = tcx.hir().delegation_sig_id(def_id);
205+
let caller_sig = tcx.fn_sig(sig_id);
206+
if let Err(err) = check_constraints(tcx, def_id) {
207+
let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1;
208+
let err_type = Ty::new_error(tcx, err);
209+
return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type));
210+
}
211+
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+
};
234+
// Bound vars are also inherited from `sig_id`.
235+
// They will be rebound later in `lower_fn_ty`.
236+
let sig = sig.skip_binder();
237+
let sig_it = sig.inputs().iter().cloned().chain(std::iter::once(sig.output()));
238+
tcx.arena.alloc_from_iter(sig_it)
239+
}

compiler/rustc_hir_analysis/src/errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1571,7 +1571,7 @@ pub enum RefOfMutStaticSugg {
15711571

15721572
#[derive(Diagnostic)]
15731573
#[diag(hir_analysis_not_supported_delegation)]
1574-
pub struct NotSupportedDelegation<'a> {
1574+
pub struct UnsupportedDelegation<'a> {
15751575
#[primary_span]
15761576
pub span: Span,
15771577
pub descr: &'a str,

0 commit comments

Comments
 (0)