forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#125929 - Bryanskiy:delegation-generics-3, r=p…
…etrochenkov Delegation: support generics for delegation from free functions (The PR was split from rust-lang#123958, explainer - https://github.com/Bryanskiy/posts/blob/master/delegation%20in%20generic%20contexts.md) This PR implements generics inheritance from free functions to free functions and trait methods. #### free functions to free functions: ```rust fn to_reuse<T: Clone>(_: T) {} reuse to_reuse as bar; // desugaring: fn bar<T: Clone>(x: T) { to_reuse(x) } ``` Generics, predicates and signature are simply copied. Generic arguments in paths are ignored during generics inheritance: ```rust fn to_reuse<T: Clone>(_: T) {} reuse to_reuse::<u8> as bar; // desugaring: fn bar<T: Clone>(x: T) { to_reuse::<u8>(x) // ERROR: mismatched types } ``` Due to implementation limitations callee path is lowered without modifications. Therefore, it is a compilation error at the moment. #### free functions to trait methods: ```rust trait Trait<'a, A> { fn foo<'b, B>(&self, x: A, y: B) {...} } reuse Trait::foo; // desugaring: fn foo<'a, 'b, This: Trait<'a, A>, A, B>(this: &This, x: A, y: B) { Trait::foo(this, x, y) } ``` The inheritance is similar to the previous case but with some corrections: - `Self` parameter converted into `T: Trait` - generic parameters need to be reordered so that lifetimes go first Arguments are similarly ignored. --- In the future, we plan to support generic inheritance for delegating from all contexts to all contexts (from free/trait/impl to free/trait /impl). These cases were considered first as the simplest from the implementation perspective.
- Loading branch information
Showing
24 changed files
with
702 additions
and
233 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(¶m.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(¶m.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(¶m.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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.