From 59885f5065c95d52497220668debf76b4ad1cd0a Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Mon, 26 Aug 2024 16:18:05 +0300 Subject: [PATCH] Delegation refactoring: add builders for generics inheritance --- compiler/rustc_hir_analysis/src/delegation.rs | 264 ++++++++++++------ .../{not-supported.rs => unsupported.rs} | 0 ...ot-supported.stderr => unsupported.stderr} | 60 ++-- 3 files changed, 214 insertions(+), 110 deletions(-) rename tests/ui/delegation/{not-supported.rs => unsupported.rs} (100%) rename tests/ui/delegation/{not-supported.stderr => unsupported.stderr} (83%) diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 20aaa43219f34..d7a0698b6b3fb 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -76,41 +76,153 @@ fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind { } } +struct GenericsBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + sig_id: DefId, + parent: Option, +} + +impl<'tcx> GenericsBuilder<'tcx> { + fn new(tcx: TyCtxt<'tcx>, sig_id: DefId) -> GenericsBuilder<'tcx> { + GenericsBuilder { tcx, sig_id, parent: None } + } + + fn build(self) -> ty::Generics { + let mut own_params = vec![]; + + let sig_generics = self.tcx.generics_of(self.sig_id); + if 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()); + } + 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(); + + for (idx, param) in own_params.iter_mut().enumerate() { + param.index = idx 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: 0, + own_params, + param_def_id_to_index, + has_self: false, + has_late_bound_regions: sig_generics.has_late_bound_regions, + host_effect_index: sig_generics.host_effect_index, + } + } +} + +struct PredicatesBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + args: ty::GenericArgsRef<'tcx>, + parent: Option, + sig_id: DefId, +} + +impl<'tcx> PredicatesBuilder<'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + args: ty::GenericArgsRef<'tcx>, + sig_id: DefId, + ) -> PredicatesBuilder<'tcx> { + PredicatesBuilder { tcx, args, parent: None, sig_id } + } + + fn build(self) -> ty::GenericPredicates<'tcx> { + let mut preds = vec![]; + + let sig_predicates = self.tcx.predicates_of(self.sig_id); + if let Some(parent) = sig_predicates.parent { + let sig_parent_preds = self.tcx.predicates_of(parent); + preds.extend(sig_parent_preds.instantiate_own(self.tcx, self.args)); + } + preds.extend(sig_predicates.instantiate_own(self.tcx, self.args)); + + 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) @@ -120,7 +232,11 @@ fn create_generic_args<'tcx>( 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), + // 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!(), } } @@ -129,47 +245,29 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>( def_id: LocalDefId, sig_id: DefId, ) -> Option { - // 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 builder = GenericsBuilder::new(tcx, sig_id); - 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()); + let caller_kind = fn_kind(tcx, def_id.into()); + let callee_kind = fn_kind(tcx, sig_id); - // Lifetimes go first. - own_params.sort_by_key(|key| key.kind.is_ty_or_const()); + // FIXME(fn_delegation): Support generics on associated delegation items. + // Error will be reported in `check_constraints`. + match (caller_kind, callee_kind) { + (FnKind::Free, FnKind::Free) + | (FnKind::Free, FnKind::AssocTrait) => Some(builder.build()), - 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) + | (FnKind::AssocInherentImpl, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) => None, + + // 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>( @@ -177,26 +275,32 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( def_id: LocalDefId, sig_id: DefId, ) -> Option> { + let args = create_generic_args(tcx, def_id, sig_id); + let builder = PredicatesBuilder::new(tcx, args, 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`. - 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); + match (caller_kind, callee_kind) { + (FnKind::Free, FnKind::Free) + | (FnKind::Free, FnKind::AssocTrait) => { + Some(builder.build()) + } - let mut preds = vec![]; - if let Some(parent_id) = callee_predicates.parent { - preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args)); + (FnKind::AssocTraitImpl, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::AssocTrait) + | (FnKind::AssocTrait, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) => None, + + // 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>( diff --git a/tests/ui/delegation/not-supported.rs b/tests/ui/delegation/unsupported.rs similarity index 100% rename from tests/ui/delegation/not-supported.rs rename to tests/ui/delegation/unsupported.rs diff --git a/tests/ui/delegation/not-supported.stderr b/tests/ui/delegation/unsupported.stderr similarity index 83% rename from tests/ui/delegation/not-supported.stderr rename to tests/ui/delegation/unsupported.stderr index 14d6b374bd2c4..bb150254f48b9 100644 --- a/tests/ui/delegation/not-supported.stderr +++ b/tests/ui/delegation/unsupported.stderr @@ -4,7 +4,7 @@ error: using `#![feature(effects)]` without enabling next trait solver globally = help: use `-Znext-solver` to enable error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:18:29 + --> $DIR/unsupported.rs:18:29 | LL | fn bar(&self, x: T) -> T { x } | ------------------------ callee defined here @@ -13,7 +13,7 @@ LL | reuse GenericTrait::bar; | ^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:20:29 + --> $DIR/unsupported.rs:20:29 | LL | fn bar1() {} | --------- callee defined here @@ -22,7 +22,7 @@ LL | reuse GenericTrait::bar1; | ^^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:31:39 + --> $DIR/unsupported.rs:31:39 | LL | fn bar(&self, x: T) -> T { x } | ------------------------ callee defined here @@ -31,7 +31,7 @@ LL | reuse >::bar { &self.0 } | ^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:33:34 + --> $DIR/unsupported.rs:33:34 | LL | fn bar1() {} | --------- callee defined here @@ -40,7 +40,7 @@ LL | reuse GenericTrait::::bar1; | ^^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:38:29 + --> $DIR/unsupported.rs:38:29 | LL | fn bar(&self, x: T) -> T { x } | ------------------------ callee defined here @@ -49,7 +49,7 @@ LL | reuse GenericTrait::bar { &F } | ^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:40:29 + --> $DIR/unsupported.rs:40:29 | LL | fn bar1() {} | --------- callee defined here @@ -58,7 +58,7 @@ LL | reuse GenericTrait::bar1; | ^^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:45:22 + --> $DIR/unsupported.rs:45:22 | LL | fn foo(&self, x: i32) -> i32 { x } | ---------------------------- callee defined here @@ -67,7 +67,7 @@ LL | reuse Trait::foo; | ^^^ error[E0049]: method `foo2` has 0 type parameters but its trait declaration has 1 type parameter - --> $DIR/not-supported.rs:51:22 + --> $DIR/unsupported.rs:51:22 | LL | fn foo2(&self, x: T) -> T { x } | - expected 1 type parameter @@ -76,7 +76,7 @@ LL | reuse Trait::foo2 { &self.0 } | ^^^^ found 0 type parameters error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:54:29 + --> $DIR/unsupported.rs:54:29 | LL | fn foo3<'a: 'a>(_: &'a u32) {} | --------------------------- callee defined here @@ -85,7 +85,7 @@ LL | reuse ::foo3; | ^^^^ error[E0195]: lifetime parameters or bounds on associated function `foo3` do not match the trait declaration - --> $DIR/not-supported.rs:54:29 + --> $DIR/unsupported.rs:54:29 | LL | fn foo3<'a: 'a>(_: &'a u32) {} | -------- lifetimes in impl do not match this associated function in trait @@ -94,7 +94,7 @@ LL | reuse ::foo3; | ^^^^ lifetimes do not match associated function in trait error: delegation to a function with effect parameter is not supported yet - --> $DIR/not-supported.rs:112:18 + --> $DIR/unsupported.rs:112:18 | LL | fn foo(); | --------- callee defined here @@ -103,7 +103,7 @@ LL | reuse Trait::foo; | ^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:61:22 + --> $DIR/unsupported.rs:61:22 | LL | fn foo(&self, x: i32) -> i32 { x } | ---------------------------- callee defined here @@ -112,7 +112,7 @@ LL | reuse Trait::foo { &self.0 } | ^^^ error: early bound generics are not supported for associated delegation items - --> $DIR/not-supported.rs:51:22 + --> $DIR/unsupported.rs:51:22 | LL | fn foo2(&self, x: T) -> T { x } | ---------------------------- callee defined here @@ -121,7 +121,7 @@ LL | reuse Trait::foo2 { &self.0 } | ^^^^ warning: this function depends on never type fallback being `()` - --> $DIR/not-supported.rs:79:9 + --> $DIR/unsupported.rs:79:9 | LL | fn opaque_ret() -> impl Trait { unimplemented!() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,33 +130,33 @@ LL | fn opaque_ret() -> impl Trait { unimplemented!() } = note: for more information, see issue #123748 = help: specify the types explicitly note: in edition 2024, the requirement `!: opaque::Trait` will fail - --> $DIR/not-supported.rs:79:28 + --> $DIR/unsupported.rs:79:28 | LL | fn opaque_ret() -> impl Trait { unimplemented!() } | ^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default -error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` - --> $DIR/not-supported.rs:86:25 +error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` + --> $DIR/unsupported.rs:86:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/not-supported.rs:86:25 + --> $DIR/unsupported.rs:86:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle -note: cycle used when checking that `opaque::` is well-formed - --> $DIR/not-supported.rs:85:5 + = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle +note: cycle used when checking that `opaque::` is well-formed + --> $DIR/unsupported.rs:85:5 | LL | impl ToReuse for u8 { | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information warning: this function depends on never type fallback being `()` - --> $DIR/not-supported.rs:73:9 + --> $DIR/unsupported.rs:73:9 | LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,32 +165,32 @@ LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } = note: for more information, see issue #123748 = help: specify the types explicitly note: in edition 2024, the requirement `!: opaque::Trait` will fail - --> $DIR/not-supported.rs:73:32 + --> $DIR/unsupported.rs:73:32 | LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } | ^^^^^^^^^^ -error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` - --> $DIR/not-supported.rs:89:24 +error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` + --> $DIR/unsupported.rs:89:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/not-supported.rs:89:24 + --> $DIR/unsupported.rs:89:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle -note: cycle used when checking that `opaque::` is well-formed - --> $DIR/not-supported.rs:88:5 + = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle +note: cycle used when checking that `opaque::` is well-formed + --> $DIR/unsupported.rs:88:5 | LL | impl ToReuse for u16 { | ^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: recursive delegation is not supported yet - --> $DIR/not-supported.rs:102:22 + --> $DIR/unsupported.rs:102:22 | LL | pub reuse to_reuse2::foo; | --- callee defined here