1
Fork 0

Delegation: support generics for delegation from free functions

This commit is contained in:
Bryanskiy 2024-05-30 22:00:44 +03:00
parent 80d8270d84
commit f2f9aab380
24 changed files with 702 additions and 233 deletions

View file

@ -341,8 +341,7 @@ hir_analysis_must_implement_not_function_span_note = required by this annotation
hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
hir_analysis_not_supported_delegation =
{$descr} is not supported yet
hir_analysis_not_supported_delegation = {$descr}
.label = callee defined here
hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate

View file

@ -10,6 +10,7 @@ use rustc_session::lint;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::Span;
use crate::delegation::inherit_generics_for_delegation_item;
use crate::middle::resolve_bound_vars as rbv;
#[instrument(level = "debug", skip(tcx), ret)]
@ -53,6 +54,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
};
}
// For a delegation item inherit generics from callee.
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
&& let Some(generics) = inherit_generics_for_delegation_item(tcx, def_id, sig_id)
{
return generics;
}
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let node = tcx.hir_node(hir_id);

View file

@ -12,6 +12,7 @@ use rustc_span::{Span, DUMMY_SP};
use crate::bounds::Bounds;
use crate::collect::ItemCtxt;
use crate::constrained_generic_params as cgp;
use crate::delegation::inherit_predicates_for_delegation_item;
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason};
/// 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
None => {}
}
// For a delegation item inherit predicates from callee.
if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id)
&& let Some(predicates) = inherit_predicates_for_delegation_item(tcx, def_id, sig_id)
{
return predicates;
}
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let node = tcx.hir_node(hir_id);

View file

@ -0,0 +1,259 @@
use rustc_data_structures::fx::FxHashMap;
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_type_ir::visit::TypeVisitableExt;
type RemapTable = FxHashMap<u32, u32>;
struct ParamIndexRemapper<'tcx> {
tcx: TyCtxt<'tcx>,
remap_table: RemapTable,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_param() {
return ty;
}
if let ty::Param(param) = ty.kind()
&& let Some(index) = self.remap_table.get(&param.index)
{
return Ty::new_param(self.tcx, *index, param.name);
}
ty.super_fold_with(self)
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
if let ty::ReEarlyParam(param) = r.kind()
&& let Some(index) = self.remap_table.get(&param.index).copied()
{
return ty::Region::new_early_param(
self.tcx,
ty::EarlyParamRegion { index, name: param.name },
);
}
r
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
if let ty::ConstKind::Param(param) = ct.kind()
&& let Some(idx) = self.remap_table.get(&param.index)
{
let param = ty::ParamConst::new(*idx, param.name);
return ty::Const::new_param(self.tcx, param);
}
ct.super_fold_with(self)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum FnKind {
Free,
AssocInherentImpl,
AssocTrait,
AssocTraitImpl,
}
fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn));
let parent = tcx.parent(def_id);
match tcx.def_kind(parent) {
DefKind::Trait => FnKind::AssocTrait,
DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl,
DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl,
_ => FnKind::Free,
}
}
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 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)
}
// FIXME(fn_delegation): Only `Self` param supported here.
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
| (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))
}
_ => ty::GenericArgs::identity_for_item(tcx, sig_id),
}
}
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;
}
let mut own_params = vec![];
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;
}
}
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);
let args = create_generic_args(tcx, def_id, 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));
}
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>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
sig_id: DefId,
) -> Result<(), ErrorGuaranteed> {
let mut ret = Ok(());
let mut emit = |descr| {
ret = Err(tcx.dcx().emit_err(crate::errors::UnsupportedDelegation {
span: tcx.def_span(def_id),
descr,
callee_span: tcx.def_span(sig_id),
}));
};
if tcx.has_host_param(sig_id) {
emit("delegation to a function with effect parameter is not supported yet");
}
if let Some(local_sig_id) = sig_id.as_local()
&& tcx.hir().opt_delegation_sig_id(local_sig_id).is_some()
{
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
}
pub(crate) fn inherit_sig_for_delegation_item<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> &'tcx [Ty<'tcx>] {
let sig_id = tcx.hir().delegation_sig_id(def_id);
let caller_sig = tcx.fn_sig(sig_id);
if let Err(err) = check_constraints(tcx, def_id, sig_id) {
let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1;
let err_type = Ty::new_error(tcx, err);
return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type));
}
let args = create_generic_args(tcx, def_id, sig_id);
// Bound vars are also inherited from `sig_id`.
// They will be rebound later in `lower_fn_ty`.
let sig = caller_sig.instantiate(tcx, args).skip_binder();
let sig_iter = sig.inputs().iter().cloned().chain(std::iter::once(sig.output()));
tcx.arena.alloc_from_iter(sig_iter)
}

View file

@ -1575,7 +1575,7 @@ pub struct RefOfMutStatic<'a> {
#[derive(Diagnostic)]
#[diag(hir_analysis_not_supported_delegation)]
pub struct NotSupportedDelegation<'a> {
pub struct UnsupportedDelegation<'a> {
#[primary_span]
pub span: Span,
pub descr: &'a str,

View file

@ -2007,93 +2007,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.lower_ty_common(hir_ty, false, true)
}
fn check_delegation_constraints(&self, sig_id: DefId, span: Span, emit: bool) -> bool {
let mut error_occured = false;
let sig_span = self.tcx().def_span(sig_id);
let mut try_emit = |descr| {
if emit {
self.dcx().emit_err(crate::errors::NotSupportedDelegation {
span,
descr,
callee_span: sig_span,
});
}
error_occured = true;
};
if let Some(node) = self.tcx().hir().get_if_local(sig_id)
&& let Some(decl) = node.fn_decl()
&& let hir::FnRetTy::Return(ty) = decl.output
&& let hir::TyKind::InferDelegation(_, _) = ty.kind
{
try_emit("recursive delegation");
}
let sig_generics = self.tcx().generics_of(sig_id);
let parent = self.tcx().local_parent(self.item_def_id());
let parent_generics = self.tcx().generics_of(parent);
let parent_is_trait = (self.tcx().def_kind(parent) == DefKind::Trait) as usize;
let sig_has_self = sig_generics.has_self as usize;
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait {
try_emit("delegation with early bound generics");
}
// There is no way to instantiate `Self` param for caller if
// 1. callee is a trait method
// 2. delegation item isn't an associative item
if let DefKind::AssocFn = self.tcx().def_kind(sig_id)
&& let DefKind::Fn = self.tcx().def_kind(self.item_def_id())
&& self.tcx().associated_item(sig_id).container
== ty::AssocItemContainer::TraitContainer
{
try_emit("delegation to a trait method from a free function");
}
error_occured
}
fn lower_delegation_ty(
&self,
sig_id: DefId,
idx: hir::InferDelegationKind,
span: Span,
) -> Ty<'tcx> {
if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output)
{
let e = self.dcx().span_delayed_bug(span, "not supported delegation case");
return Ty::new_error(self.tcx(), e);
};
let sig = self.tcx().fn_sig(sig_id);
let sig_generics = self.tcx().generics_of(sig_id);
let parent = self.tcx().local_parent(self.item_def_id());
let parent_def_kind = self.tcx().def_kind(parent);
let sig = if let DefKind::Impl { .. } = parent_def_kind
&& sig_generics.has_self
{
// Generic params can't be here except the trait self type.
// They are not supported yet.
assert_eq!(sig_generics.count(), 1);
assert_eq!(self.tcx().generics_of(parent).count(), 0);
let self_ty = self.tcx().type_of(parent).instantiate_identity();
let generic_self_ty = ty::GenericArg::from(self_ty);
let args = self.tcx().mk_args_from_iter(std::iter::once(generic_self_ty));
sig.instantiate(self.tcx(), args)
} else {
sig.instantiate_identity()
};
// Bound vars are also inherited from `sig_id`.
// They will be rebound later in `lower_fn_ty`.
let sig = sig.skip_binder();
fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> {
let delegation_sig = self.tcx().inherit_sig_for_delegation_item(self.item_def_id());
match idx {
hir::InferDelegationKind::Input(id) => sig.inputs()[id],
hir::InferDelegationKind::Output => sig.output(),
hir::InferDelegationKind::Input(idx) => delegation_sig[idx],
hir::InferDelegationKind::Output => *delegation_sig.last().unwrap(),
}
}
@ -2110,9 +2028,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let tcx = self.tcx();
let result_ty = match &hir_ty.kind {
hir::TyKind::InferDelegation(sig_id, idx) => {
self.lower_delegation_ty(*sig_id, *idx, hir_ty.span)
}
hir::TyKind::InferDelegation(_, idx) => self.lower_delegation_ty(*idx),
hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.lower_ty(ty)),
hir::TyKind::Ptr(mt) => Ty::new_ptr(tcx, self.lower_ty(mt.ty), mt.mutbl),
hir::TyKind::Ref(region, mt) => {

View file

@ -83,6 +83,7 @@ pub mod autoderef;
mod bounds;
mod check_unused;
mod coherence;
mod delegation;
pub mod hir_ty_lowering;
// FIXME: This module shouldn't be public.
pub mod collect;
@ -146,6 +147,10 @@ pub fn provide(providers: &mut Providers) {
variance::provide(providers);
outlives::provide(providers);
hir_wf_check::provide(providers);
*providers = Providers {
inherit_sig_for_delegation_item: delegation::inherit_sig_for_delegation_item,
..*providers
};
}
pub fn check_crate(tcx: TyCtxt<'_>) {