Skip to content

Commit f2f9aab

Browse files
committed
Delegation: support generics for delegation from free functions
1 parent 80d8270 commit f2f9aab

24 files changed

+702
-233
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 std::iter;

compiler/rustc_hir_analysis/messages.ftl

+1-2
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,7 @@ hir_analysis_must_implement_not_function_span_note = required by this annotation
341341
342342
hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
343343
344-
hir_analysis_not_supported_delegation =
345-
{$descr} is not supported yet
344+
hir_analysis_not_supported_delegation = {$descr}
346345
.label = callee defined here
347346
348347
hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate

compiler/rustc_hir_analysis/src/collect/generics_of.rs

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_session::lint;
1010
use rustc_span::symbol::{kw, Symbol};
1111
use rustc_span::Span;
1212

13+
use crate::delegation::inherit_generics_for_delegation_item;
1314
use crate::middle::resolve_bound_vars as rbv;
1415

1516
#[instrument(level = "debug", skip(tcx), ret)]
@@ -53,6 +54,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
5354
};
5455
}
5556

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

5866
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
@@ -12,6 +12,7 @@ use rustc_span::{Span, DUMMY_SP};
1212
use crate::bounds::Bounds;
1313
use crate::collect::ItemCtxt;
1414
use crate::constrained_generic_params as cgp;
15+
use crate::delegation::inherit_predicates_for_delegation_item;
1516
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason};
1617

1718
/// Returns a list of all type predicates (explicit and implicit) for the definition with
@@ -114,6 +115,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
114115
None => {}
115116
}
116117

118+
// For a delegation item inherit predicates from callee.
119+
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
120+
&& let Some(predicates) = inherit_predicates_for_delegation_item(tcx, def_id, sig_id)
121+
{
122+
return predicates;
123+
}
124+
117125
let hir_id = tcx.local_def_id_to_hir_id(def_id);
118126
let node = tcx.hir_node(hir_id);
119127

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
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+
use rustc_type_ir::visit::TypeVisitableExt;
8+
9+
type RemapTable = FxHashMap<u32, u32>;
10+
11+
struct ParamIndexRemapper<'tcx> {
12+
tcx: TyCtxt<'tcx>,
13+
remap_table: RemapTable,
14+
}
15+
16+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> {
17+
fn cx(&self) -> TyCtxt<'tcx> {
18+
self.tcx
19+
}
20+
21+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
22+
if !ty.has_param() {
23+
return ty;
24+
}
25+
26+
if let ty::Param(param) = ty.kind()
27+
&& let Some(index) = self.remap_table.get(&param.index)
28+
{
29+
return Ty::new_param(self.tcx, *index, param.name);
30+
}
31+
ty.super_fold_with(self)
32+
}
33+
34+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
35+
if let ty::ReEarlyParam(param) = r.kind()
36+
&& let Some(index) = self.remap_table.get(&param.index).copied()
37+
{
38+
return ty::Region::new_early_param(
39+
self.tcx,
40+
ty::EarlyParamRegion { index, name: param.name },
41+
);
42+
}
43+
r
44+
}
45+
46+
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
47+
if let ty::ConstKind::Param(param) = ct.kind()
48+
&& let Some(idx) = self.remap_table.get(&param.index)
49+
{
50+
let param = ty::ParamConst::new(*idx, param.name);
51+
return ty::Const::new_param(self.tcx, param);
52+
}
53+
ct.super_fold_with(self)
54+
}
55+
}
56+
57+
#[derive(Clone, Copy, Debug, PartialEq)]
58+
enum FnKind {
59+
Free,
60+
AssocInherentImpl,
61+
AssocTrait,
62+
AssocTraitImpl,
63+
}
64+
65+
fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
66+
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn));
67+
68+
let parent = tcx.parent(def_id);
69+
match tcx.def_kind(parent) {
70+
DefKind::Trait => FnKind::AssocTrait,
71+
DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl,
72+
DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl,
73+
_ => FnKind::Free,
74+
}
75+
}
76+
77+
fn create_generic_args<'tcx>(
78+
tcx: TyCtxt<'tcx>,
79+
def_id: LocalDefId,
80+
sig_id: DefId,
81+
) -> ty::GenericArgsRef<'tcx> {
82+
let caller_generics = tcx.generics_of(def_id);
83+
let callee_generics = tcx.generics_of(sig_id);
84+
85+
let caller_kind = fn_kind(tcx, def_id.into());
86+
let callee_kind = fn_kind(tcx, sig_id);
87+
// FIXME(fn_delegation): Support generics on associated delegation items.
88+
// Error will be reported in `check_constraints`.
89+
match (caller_kind, callee_kind) {
90+
(FnKind::Free, _) => {
91+
// Lifetime parameters must be declared before type and const parameters.
92+
// Therefore, When delegating from a free function to a associated function,
93+
// generic parameters need to be reordered:
94+
//
95+
// trait Trait<'a, A> {
96+
// fn foo<'b, B>(...) {...}
97+
// }
98+
//
99+
// reuse Trait::foo;
100+
// desugaring:
101+
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
102+
// Trait::foo(...)
103+
// }
104+
let mut remap_table = RemapTable::default();
105+
for caller_param in &caller_generics.own_params {
106+
let callee_index =
107+
callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
108+
remap_table.insert(callee_index, caller_param.index);
109+
}
110+
let mut folder = ParamIndexRemapper { tcx, remap_table };
111+
ty::GenericArgs::identity_for_item(tcx, sig_id).fold_with(&mut folder)
112+
}
113+
// FIXME(fn_delegation): Only `Self` param supported here.
114+
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
115+
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
116+
let parent = tcx.parent(def_id.into());
117+
let self_ty = tcx.type_of(parent).instantiate_identity();
118+
let generic_self_ty = ty::GenericArg::from(self_ty);
119+
tcx.mk_args_from_iter(std::iter::once(generic_self_ty))
120+
}
121+
_ => ty::GenericArgs::identity_for_item(tcx, sig_id),
122+
}
123+
}
124+
125+
pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
126+
tcx: TyCtxt<'tcx>,
127+
def_id: LocalDefId,
128+
sig_id: DefId,
129+
) -> Option<ty::Generics> {
130+
// FIXME(fn_delegation): Support generics on associated delegation items.
131+
// Error will be reported in `check_constraints`.
132+
if fn_kind(tcx, def_id.into()) != FnKind::Free {
133+
return None;
134+
}
135+
136+
let mut own_params = vec![];
137+
138+
let callee_generics = tcx.generics_of(sig_id);
139+
if let Some(parent_sig_id) = callee_generics.parent {
140+
let parent_sig_generics = tcx.generics_of(parent_sig_id);
141+
own_params.append(&mut parent_sig_generics.own_params.clone());
142+
}
143+
own_params.append(&mut callee_generics.own_params.clone());
144+
145+
// Lifetimes go first.
146+
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
147+
148+
for (idx, param) in own_params.iter_mut().enumerate() {
149+
param.index = idx as u32;
150+
// Default parameters are not inherited: they are not allowed
151+
// in fn's.
152+
if let ty::GenericParamDefKind::Type { has_default, .. }
153+
| ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
154+
{
155+
*has_default = false;
156+
}
157+
}
158+
159+
let param_def_id_to_index =
160+
own_params.iter().map(|param| (param.def_id, param.index)).collect();
161+
162+
Some(ty::Generics {
163+
parent: None,
164+
parent_count: 0,
165+
own_params,
166+
param_def_id_to_index,
167+
has_self: false,
168+
has_late_bound_regions: callee_generics.has_late_bound_regions,
169+
host_effect_index: callee_generics.host_effect_index,
170+
})
171+
}
172+
173+
pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
174+
tcx: TyCtxt<'tcx>,
175+
def_id: LocalDefId,
176+
sig_id: DefId,
177+
) -> Option<ty::GenericPredicates<'tcx>> {
178+
// FIXME(fn_delegation): Support generics on associated delegation items.
179+
// Error will be reported in `check_constraints`.
180+
if fn_kind(tcx, def_id.into()) != FnKind::Free {
181+
return None;
182+
}
183+
184+
let callee_predicates = tcx.predicates_of(sig_id);
185+
let args = create_generic_args(tcx, def_id, sig_id);
186+
187+
let mut preds = vec![];
188+
if let Some(parent_id) = callee_predicates.parent {
189+
preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args));
190+
}
191+
preds.extend(callee_predicates.instantiate_own(tcx, args));
192+
193+
Some(ty::GenericPredicates {
194+
parent: None,
195+
predicates: tcx.arena.alloc_from_iter(preds),
196+
effects_min_tys: ty::List::empty(),
197+
})
198+
}
199+
200+
fn check_constraints<'tcx>(
201+
tcx: TyCtxt<'tcx>,
202+
def_id: LocalDefId,
203+
sig_id: DefId,
204+
) -> Result<(), ErrorGuaranteed> {
205+
let mut ret = Ok(());
206+
207+
let mut emit = |descr| {
208+
ret = Err(tcx.dcx().emit_err(crate::errors::UnsupportedDelegation {
209+
span: tcx.def_span(def_id),
210+
descr,
211+
callee_span: tcx.def_span(sig_id),
212+
}));
213+
};
214+
215+
if tcx.has_host_param(sig_id) {
216+
emit("delegation to a function with effect parameter is not supported yet");
217+
}
218+
219+
if let Some(local_sig_id) = sig_id.as_local()
220+
&& tcx.hir().opt_delegation_sig_id(local_sig_id).is_some()
221+
{
222+
emit("recursive delegation is not supported yet");
223+
}
224+
225+
if fn_kind(tcx, def_id.into()) != FnKind::Free {
226+
let sig_generics = tcx.generics_of(sig_id);
227+
let parent = tcx.parent(def_id.into());
228+
let parent_generics = tcx.generics_of(parent);
229+
230+
let parent_has_self = parent_generics.has_self as usize;
231+
let sig_has_self = sig_generics.has_self as usize;
232+
233+
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_has_self {
234+
emit("early bound generics are not supported for associated delegation items");
235+
}
236+
}
237+
238+
ret
239+
}
240+
241+
pub(crate) fn inherit_sig_for_delegation_item<'tcx>(
242+
tcx: TyCtxt<'tcx>,
243+
def_id: LocalDefId,
244+
) -> &'tcx [Ty<'tcx>] {
245+
let sig_id = tcx.hir().delegation_sig_id(def_id);
246+
let caller_sig = tcx.fn_sig(sig_id);
247+
if let Err(err) = check_constraints(tcx, def_id, sig_id) {
248+
let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1;
249+
let err_type = Ty::new_error(tcx, err);
250+
return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type));
251+
}
252+
let args = create_generic_args(tcx, def_id, sig_id);
253+
254+
// Bound vars are also inherited from `sig_id`.
255+
// They will be rebound later in `lower_fn_ty`.
256+
let sig = caller_sig.instantiate(tcx, args).skip_binder();
257+
let sig_iter = sig.inputs().iter().cloned().chain(std::iter::once(sig.output()));
258+
tcx.arena.alloc_from_iter(sig_iter)
259+
}

compiler/rustc_hir_analysis/src/errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1575,7 +1575,7 @@ pub struct RefOfMutStatic<'a> {
15751575

15761576
#[derive(Diagnostic)]
15771577
#[diag(hir_analysis_not_supported_delegation)]
1578-
pub struct NotSupportedDelegation<'a> {
1578+
pub struct UnsupportedDelegation<'a> {
15791579
#[primary_span]
15801580
pub span: Span,
15811581
pub descr: &'a str,

0 commit comments

Comments
 (0)