Auto merge of #126161 - Bryanskiy:delegation-generics-4, r=petrochenkov

Delegation: support generics in associated delegation items

This is a continuation of https://github.com/rust-lang/rust/pull/125929.

[design](https://github.com/Bryanskiy/posts/blob/master/delegation%20in%20generic%20contexts.md)

Generic parameters inheritance was implemented in all contexts. Generic arguments are not yet supported.

r? `@petrochenkov`
This commit is contained in:
bors 2024-09-07 18:12:05 +00:00
commit ec867f03bc
16 changed files with 778 additions and 446 deletions

View file

@ -59,6 +59,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let node = tcx.hir_node(hir_id);
if let Some(sig) = node.fn_sig()
&& let Some(sig_id) = sig.decl.opt_delegation_sig_id()
{
return inherit_generics_for_delegation_item(tcx, def_id, sig_id);
}
let parent_def_id = match node {
Node::ImplItem(_)
| Node::TraitItem(_)
@ -229,16 +235,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
// inherit the generics of the item.
Some(parent.to_def_id())
}
ItemKind::Fn(sig, _, _) => {
// For a delegation item inherit generics from callee.
if let Some(sig_id) = sig.decl.opt_delegation_sig_id()
&& let Some(generics) =
inherit_generics_for_delegation_item(tcx, def_id, sig_id)
{
return generics;
}
None
}
_ => None,
},
_ => None,

View file

@ -138,6 +138,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let node = tcx.hir_node(hir_id);
if let Some(sig) = node.fn_sig()
&& let Some(sig_id) = sig.decl.opt_delegation_sig_id()
{
return inherit_predicates_for_delegation_item(tcx, def_id, sig_id);
}
let mut is_trait = None;
let mut is_default_impl_trait = None;
@ -164,16 +170,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => {
is_trait = Some(self_bounds);
}
ItemKind::Fn(sig, _, _) => {
// For a delegation item inherit predicates from callee.
if let Some(sig_id) = sig.decl.opt_delegation_sig_id()
&& let Some(predicates) =
inherit_predicates_for_delegation_item(tcx, def_id, sig_id)
{
return predicates;
}
}
_ => {}
}
};

View file

@ -1,3 +1,7 @@
//! Support inheriting generic parameters and predicates for function delegation.
//!
//! For more information about delegation design, see the tracking issue #118212.
use std::assert_matches::debug_assert_matches;
use rustc_data_structures::fx::FxHashMap;
@ -5,7 +9,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::ErrorGuaranteed;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_type_ir::visit::TypeVisitableExt;
type RemapTable = FxHashMap<u32, u32>;
@ -76,127 +80,381 @@ fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
}
}
/// Given the current context(caller and callee `FnKind`), it specifies
/// the policy of predicates and generic parameters inheritance.
#[derive(Clone, Copy, Debug, PartialEq)]
enum InheritanceKind {
/// Copying all predicates and parameters, including those of the parent
/// container.
///
/// Boolean value defines whether the `Self` parameter or `Self: Trait`
/// predicate are copied. It's always equal to `false` except when
/// delegating from a free function to a trait method.
///
/// FIXME(fn_delegation): This often leads to type inference
/// errors. Support providing generic arguments or restrict use sites.
WithParent(bool),
/// The trait implementation should be compatible with the original trait.
/// Therefore, for trait implementations only the method's own parameters
/// and predicates are copied.
Own,
}
struct GenericsBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
sig_id: DefId,
parent: Option<DefId>,
inh_kind: InheritanceKind,
}
impl<'tcx> GenericsBuilder<'tcx> {
fn new(tcx: TyCtxt<'tcx>, sig_id: DefId) -> GenericsBuilder<'tcx> {
GenericsBuilder { tcx, sig_id, parent: None, inh_kind: InheritanceKind::WithParent(false) }
}
fn with_parent(mut self, parent: DefId) -> Self {
self.parent = Some(parent);
self
}
fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self {
self.inh_kind = inh_kind;
self
}
fn build(self) -> ty::Generics {
let mut own_params = vec![];
let sig_generics = self.tcx.generics_of(self.sig_id);
if let InheritanceKind::WithParent(has_self) = self.inh_kind
&& let Some(parent_def_id) = sig_generics.parent
{
let sig_parent_generics = self.tcx.generics_of(parent_def_id);
own_params.append(&mut sig_parent_generics.own_params.clone());
if !has_self {
own_params.remove(0);
}
}
own_params.append(&mut sig_generics.own_params.clone());
// Lifetime parameters must be declared before type and const parameters.
// Therefore, When delegating from a free function to a associated function,
// generic parameters need to be reordered:
//
// trait Trait<'a, A> {
// fn foo<'b, B>(...) {...}
// }
//
// reuse Trait::foo;
// desugaring:
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
// Trait::foo(...)
// }
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
let param_def_id_to_index =
own_params.iter().map(|param| (param.def_id, param.index)).collect();
let (parent_count, has_self) = if let Some(def_id) = self.parent {
let parent_generics = self.tcx.generics_of(def_id);
let parent_kind = self.tcx.def_kind(def_id);
(parent_generics.count(), parent_kind == DefKind::Trait)
} else {
(0, false)
};
for (idx, param) in own_params.iter_mut().enumerate() {
param.index = (idx + parent_count) as u32;
// FIXME(fn_delegation): Default parameters are not inherited, because they are
// not permitted in functions. Therefore, there are 2 options here:
//
// - We can create non-default generic parameters.
// - We can substitute default parameters into the signature.
//
// At the moment, first option has been selected as the most general.
if let ty::GenericParamDefKind::Type { has_default, .. }
| ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
{
*has_default = false;
}
}
ty::Generics {
parent: self.parent,
parent_count,
own_params,
param_def_id_to_index,
has_self,
has_late_bound_regions: sig_generics.has_late_bound_regions,
host_effect_index: sig_generics.host_effect_index,
}
}
}
struct PredicatesBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
sig_id: DefId,
parent: Option<DefId>,
inh_kind: InheritanceKind,
args: ty::GenericArgsRef<'tcx>,
}
impl<'tcx> PredicatesBuilder<'tcx> {
fn new(
tcx: TyCtxt<'tcx>,
args: ty::GenericArgsRef<'tcx>,
sig_id: DefId,
) -> PredicatesBuilder<'tcx> {
PredicatesBuilder {
tcx,
sig_id,
parent: None,
inh_kind: InheritanceKind::WithParent(false),
args,
}
}
fn with_parent(mut self, parent: DefId) -> Self {
self.parent = Some(parent);
self
}
fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self {
self.inh_kind = inh_kind;
self
}
fn build(self) -> ty::GenericPredicates<'tcx> {
struct PredicatesCollector<'tcx> {
tcx: TyCtxt<'tcx>,
preds: Vec<(ty::Clause<'tcx>, Span)>,
args: ty::GenericArgsRef<'tcx>,
}
impl<'tcx> PredicatesCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> {
PredicatesCollector { tcx, preds: vec![], args }
}
fn with_own_preds(
mut self,
f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>,
def_id: DefId,
) -> Self {
let preds = f(def_id).instantiate_own(self.tcx, self.args);
self.preds.extend(preds);
self
}
fn with_preds(
mut self,
f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy,
def_id: DefId,
) -> Self {
let preds = f(def_id);
if let Some(parent_def_id) = preds.parent {
self = self.with_own_preds(f, parent_def_id);
}
self.with_own_preds(f, def_id)
}
}
let collector = PredicatesCollector::new(self.tcx, self.args);
// `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate.
// Note: `predicates_of` query can also add inferred outlives predicates, but that
// is not the case here as `sig_id` is either a trait or a function.
let preds = match self.inh_kind {
InheritanceKind::WithParent(false) => {
collector.with_preds(|def_id| self.tcx.explicit_predicates_of(def_id), self.sig_id)
}
InheritanceKind::WithParent(true) => {
collector.with_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id)
}
InheritanceKind::Own => {
collector.with_own_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id)
}
}
.preds;
ty::GenericPredicates {
parent: self.parent,
predicates: self.tcx.arena.alloc_from_iter(preds),
// FIXME(fn_delegation): Support effects.
effects_min_tys: ty::List::empty(),
}
}
}
struct GenericArgsBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
remap_table: RemapTable,
sig_id: DefId,
def_id: LocalDefId,
}
impl<'tcx> GenericArgsBuilder<'tcx> {
fn new(tcx: TyCtxt<'tcx>, sig_id: DefId, def_id: LocalDefId) -> GenericArgsBuilder<'tcx> {
GenericArgsBuilder { tcx, remap_table: FxHashMap::default(), sig_id, def_id }
}
fn build_from_args(mut self, args: ty::GenericArgsRef<'tcx>) -> ty::GenericArgsRef<'tcx> {
let caller_generics = self.tcx.generics_of(self.def_id);
let callee_generics = self.tcx.generics_of(self.sig_id);
for caller_param in &caller_generics.own_params {
let callee_index =
callee_generics.param_def_id_to_index(self.tcx, caller_param.def_id).unwrap();
self.remap_table.insert(callee_index, caller_param.index);
}
let mut folder = ParamIndexRemapper { tcx: self.tcx, remap_table: self.remap_table };
args.fold_with(&mut folder)
}
}
fn create_generic_args<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
sig_id: DefId,
) -> ty::GenericArgsRef<'tcx> {
let caller_generics = tcx.generics_of(def_id);
let callee_generics = tcx.generics_of(sig_id);
let builder = GenericArgsBuilder::new(tcx, sig_id, def_id);
let caller_kind = fn_kind(tcx, def_id.into());
let callee_kind = fn_kind(tcx, sig_id);
// FIXME(fn_delegation): Support generics on associated delegation items.
// Error will be reported in `check_constraints`.
match (caller_kind, callee_kind) {
(FnKind::Free, _) => {
// Lifetime parameters must be declared before type and const parameters.
// Therefore, When delegating from a free function to a associated function,
// generic parameters need to be reordered:
//
// trait Trait<'a, A> {
// fn foo<'b, B>(...) {...}
// }
//
// reuse Trait::foo;
// desugaring:
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
// Trait::foo(...)
// }
let mut remap_table = RemapTable::default();
for caller_param in &caller_generics.own_params {
let callee_index =
callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
remap_table.insert(callee_index, caller_param.index);
}
let mut folder = ParamIndexRemapper { tcx, remap_table };
ty::GenericArgs::identity_for_item(tcx, sig_id).fold_with(&mut folder)
(FnKind::Free, FnKind::Free)
| (FnKind::Free, FnKind::AssocTrait)
| (FnKind::AssocInherentImpl, FnKind::Free)
| (FnKind::AssocTrait, FnKind::Free)
| (FnKind::AssocTrait, FnKind::AssocTrait) => {
let args = ty::GenericArgs::identity_for_item(tcx, sig_id);
builder.build_from_args(args)
}
// FIXME(fn_delegation): Only `Self` param supported here.
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
let callee_generics = tcx.generics_of(sig_id);
let parent = tcx.parent(def_id.into());
let parent_args =
tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args;
let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
let method_args = tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count));
let method_args = builder.build_from_args(method_args);
tcx.mk_args_from_iter(parent_args.iter().chain(method_args))
}
(FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
let parent = tcx.parent(def_id.into());
let self_ty = tcx.type_of(parent).instantiate_identity();
let generic_self_ty = ty::GenericArg::from(self_ty);
tcx.mk_args_from_iter(std::iter::once(generic_self_ty))
let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
let trait_args = builder.build_from_args(trait_args);
let args = std::iter::once(generic_self_ty).chain(trait_args.iter().skip(1));
tcx.mk_args_from_iter(args)
}
_ => ty::GenericArgs::identity_for_item(tcx, sig_id),
// For trait impl's `sig_id` is always equal to the corresponding trait method.
(FnKind::AssocTraitImpl, _)
| (_, FnKind::AssocTraitImpl)
// Delegation to inherent methods is not yet supported.
| (_, FnKind::AssocInherentImpl) => unreachable!(),
}
}
// FIXME(fn_delegation): Move generics inheritance to the AST->HIR lowering.
// For now, generic parameters are not propagated to the generated call,
// which leads to inference errors:
//
// fn foo<T>(x: i32) {}
//
// reuse foo as bar;
// desugaring:
// fn bar<T>() {
// foo::<_>() // ERROR: type annotations needed
// }
pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
sig_id: DefId,
) -> Option<ty::Generics> {
// FIXME(fn_delegation): Support generics on associated delegation items.
// Error will be reported in `check_constraints`.
if fn_kind(tcx, def_id.into()) != FnKind::Free {
return None;
}
) -> ty::Generics {
let builder = GenericsBuilder::new(tcx, sig_id);
let mut own_params = vec![];
let caller_kind = fn_kind(tcx, def_id.into());
let callee_kind = fn_kind(tcx, sig_id);
match (caller_kind, callee_kind) {
(FnKind::Free, FnKind::Free)
| (FnKind::Free, FnKind::AssocTrait) => builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build(),
let callee_generics = tcx.generics_of(sig_id);
if let Some(parent_sig_id) = callee_generics.parent {
let parent_sig_generics = tcx.generics_of(parent_sig_id);
own_params.append(&mut parent_sig_generics.own_params.clone());
}
own_params.append(&mut callee_generics.own_params.clone());
// Lifetimes go first.
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
for (idx, param) in own_params.iter_mut().enumerate() {
param.index = idx as u32;
// Default parameters are not inherited: they are not allowed
// in fn's.
if let ty::GenericParamDefKind::Type { has_default, .. }
| ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
{
*has_default = false;
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
builder
.with_parent(tcx.parent(def_id.into()))
.with_inheritance_kind(InheritanceKind::Own)
.build()
}
(FnKind::AssocInherentImpl, FnKind::AssocTrait)
| (FnKind::AssocTrait, FnKind::AssocTrait) => {
builder
.with_parent(tcx.parent(def_id.into()))
.build()
}
(FnKind::AssocInherentImpl, FnKind::Free)
| (FnKind::AssocTrait, FnKind::Free) => {
builder
.with_parent(tcx.parent(def_id.into()))
.build()
}
// For trait impl's `sig_id` is always equal to the corresponding trait method.
(FnKind::AssocTraitImpl, _)
| (_, FnKind::AssocTraitImpl)
// Delegation to inherent methods is not yet supported.
| (_, FnKind::AssocInherentImpl) => unreachable!(),
}
let param_def_id_to_index =
own_params.iter().map(|param| (param.def_id, param.index)).collect();
Some(ty::Generics {
parent: None,
parent_count: 0,
own_params,
param_def_id_to_index,
has_self: false,
has_late_bound_regions: callee_generics.has_late_bound_regions,
host_effect_index: callee_generics.host_effect_index,
})
}
pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
sig_id: DefId,
) -> Option<ty::GenericPredicates<'tcx>> {
// FIXME(fn_delegation): Support generics on associated delegation items.
// Error will be reported in `check_constraints`.
if fn_kind(tcx, def_id.into()) != FnKind::Free {
return None;
}
let callee_predicates = tcx.predicates_of(sig_id);
) -> ty::GenericPredicates<'tcx> {
let args = create_generic_args(tcx, def_id, sig_id);
let builder = PredicatesBuilder::new(tcx, args, sig_id);
let mut preds = vec![];
if let Some(parent_id) = callee_predicates.parent {
preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args));
let caller_kind = fn_kind(tcx, def_id.into());
let callee_kind = fn_kind(tcx, sig_id);
match (caller_kind, callee_kind) {
(FnKind::Free, FnKind::Free)
| (FnKind::Free, FnKind::AssocTrait) => {
builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build()
}
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
builder
.with_parent(tcx.parent(def_id.into()))
.with_inheritance_kind(InheritanceKind::Own)
.build()
}
(FnKind::AssocInherentImpl, FnKind::AssocTrait)
| (FnKind::AssocTrait, FnKind::AssocTrait)
| (FnKind::AssocInherentImpl, FnKind::Free)
| (FnKind::AssocTrait, FnKind::Free) => {
builder
.with_parent(tcx.parent(def_id.into()))
.build()
}
// For trait impl's `sig_id` is always equal to the corresponding trait method.
(FnKind::AssocTraitImpl, _)
| (_, FnKind::AssocTraitImpl)
// Delegation to inherent methods is not yet supported.
| (_, FnKind::AssocInherentImpl) => unreachable!(),
}
preds.extend(callee_predicates.instantiate_own(tcx, args));
Some(ty::GenericPredicates {
parent: None,
predicates: tcx.arena.alloc_from_iter(preds),
effects_min_tys: ty::List::empty(),
})
}
fn check_constraints<'tcx>(
@ -224,19 +482,6 @@ fn check_constraints<'tcx>(
emit("recursive delegation is not supported yet");
}
if fn_kind(tcx, def_id.into()) != FnKind::Free {
let sig_generics = tcx.generics_of(sig_id);
let parent = tcx.parent(def_id.into());
let parent_generics = tcx.generics_of(parent);
let parent_has_self = parent_generics.has_self as usize;
let sig_has_self = sig_generics.has_self as usize;
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_has_self {
emit("early bound generics are not supported for associated delegation items");
}
}
ret
}