Rollup merge of #150024 - aerooneqq:recursive-delegation-2, r=petrochenkov

Support recursive delegation

This PR adds support for recursive delegations and is a part of the delegation feature rust-lang/rust#118212.

r? ``@petrochenkov``
This commit is contained in:
Jonathan Brouwer 2025-12-18 18:37:17 +01:00 committed by GitHub
commit 1e496bc9e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 331 additions and 70 deletions

View file

@ -56,6 +56,8 @@ ast_lowering_coroutine_too_many_parameters =
ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs
.label = default fields are only supported on structs
ast_lowering_delegation_cycle_in_signature_resolution = encountered a cycle during delegation signature resolution
ast_lowering_delegation_unresolved_callee = failed to resolve delegation callee
ast_lowering_does_not_support_modifiers =
the `{$class_name}` register class does not support template modifiers

View file

@ -44,6 +44,7 @@ use hir::{BodyId, HirId};
use rustc_abi::ExternAbi;
use rustc_ast::*;
use rustc_attr_parsing::{AttributeParser, ShouldEmit};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::Target;
use rustc_hir::attrs::{AttributeKind, InlineAttr};
@ -55,6 +56,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
use {rustc_ast as ast, rustc_hir as hir};
use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode};
use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee};
use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt};
pub(crate) struct DelegationResults<'hir> {
@ -119,10 +121,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
delegation: &Delegation,
item_id: NodeId,
is_in_trait_impl: bool,
) -> DelegationResults<'hir> {
let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span);
let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl);
let sig_id = self.get_delegation_sig_id(
self.resolver.delegation_sig_resolution_nodes[&self.local_def_id(item_id)],
span,
);
match sig_id {
Ok(sig_id) => {
self.add_attributes_if_needed(span, sig_id);
@ -238,24 +244,48 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn get_delegation_sig_id(
&self,
item_id: NodeId,
path_id: NodeId,
mut node_id: NodeId,
span: Span,
is_in_trait_impl: bool,
) -> Result<DefId, ErrorGuaranteed> {
let sig_id = if is_in_trait_impl { item_id } else { path_id };
self.get_resolution_id(sig_id, span)
let mut visited: FxHashSet<NodeId> = Default::default();
loop {
visited.insert(node_id);
let Some(def_id) = self.get_resolution_id(node_id) else {
return Err(self.tcx.dcx().span_delayed_bug(
span,
format!(
"LoweringContext: couldn't resolve node {:?} in delegation item",
node_id
),
));
};
// If def_id is in local crate and it corresponds to another delegation
// it means that we refer to another delegation as a callee, so in order to obtain
// a signature DefId we obtain NodeId of the callee delegation and try to get signature from it.
if let Some(local_id) = def_id.as_local()
&& let Some(next_node_id) =
self.resolver.delegation_sig_resolution_nodes.get(&local_id)
{
node_id = *next_node_id;
if visited.contains(&node_id) {
// We encountered a cycle in the resolution, or delegation callee refers to non-existent
// entity, in this case emit an error.
return Err(match visited.len() {
1 => self.dcx().emit_err(UnresolvedDelegationCallee { span }),
_ => self.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
});
}
} else {
return Ok(def_id);
}
}
}
fn get_resolution_id(&self, node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
let def_id =
self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id());
def_id.ok_or_else(|| {
self.tcx.dcx().span_delayed_bug(
span,
format!("LoweringContext: couldn't resolve node {:?} in delegation item", node_id),
)
})
fn get_resolution_id(&self, node_id: NodeId) -> Option<DefId> {
self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id())
}
fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> {
@ -271,8 +301,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Function parameter count, including C variadic `...` if present.
fn param_count(&self, sig_id: DefId) -> (usize, bool /*c_variadic*/) {
if let Some(local_sig_id) = sig_id.as_local() {
// Map may be filled incorrectly due to recursive delegation.
// Error will be emitted later during HIR ty lowering.
match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
Some(sig) => (sig.param_count, sig.c_variadic),
None => (0, false),
@ -489,8 +517,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some());
let call = if self
.get_resolution_id(delegation.id, span)
.and_then(|def_id| Ok(self.is_method(def_id, span)))
.get_resolution_id(delegation.id)
.map(|def_id| self.is_method(def_id, span))
.unwrap_or_default()
&& delegation.qself.is_none()
&& !has_generic_args

View file

@ -475,3 +475,17 @@ pub(crate) struct UnionWithDefault {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_delegation_unresolved_callee)]
pub(crate) struct UnresolvedDelegationCallee {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_delegation_cycle_in_signature_resolution)]
pub(crate) struct CycleInDelegationSignatureResolution {
#[primary_span]
pub span: Span,
}

View file

@ -541,7 +541,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
}
ItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, id, false);
let delegation_results = self.lower_delegation(delegation, id);
hir::ItemKind::Fn {
sig: delegation_results.sig,
ident: delegation_results.ident,
@ -1026,7 +1026,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
(*ident, generics, kind, ty.is_some())
}
AssocItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, i.id, false);
let delegation_results = self.lower_delegation(delegation, i.id);
let item_kind = hir::TraitItemKind::Fn(
delegation_results.sig,
hir::TraitFn::Provided(delegation_results.body_id),
@ -1196,7 +1196,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
AssocItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, i.id, is_in_trait_impl);
let delegation_results = self.lower_delegation(delegation, i.id);
(
delegation.ident,
(

View file

@ -401,12 +401,6 @@ fn check_constraints<'tcx>(
}));
};
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 tcx.fn_sig(sig_id).skip_binder().skip_binder().c_variadic {
// See issue #127443 for explanation.
emit("delegation to C-variadic functions is not allowed");

View file

@ -221,6 +221,9 @@ pub struct ResolverAstLowering {
/// Information about functions signatures for delegation items expansion
pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
// NodeIds (either delegation.id or item_id in case of a trait impl) for signature resolution,
// for details see https://github.com/rust-lang/rust/issues/118212#issuecomment-2160686914
pub delegation_sig_resolution_nodes: LocalDefIdMap<ast::NodeId>,
}
bitflags::bitflags! {

View file

@ -2928,7 +2928,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
item.id,
LifetimeBinderKind::Function,
span,
|this| this.resolve_delegation(delegation),
|this| this.resolve_delegation(delegation, item.id, false),
);
}
@ -3257,7 +3257,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
item.id,
LifetimeBinderKind::Function,
delegation.path.segments.last().unwrap().ident.span,
|this| this.resolve_delegation(delegation),
|this| this.resolve_delegation(delegation, item.id, false),
);
}
AssocItemKind::Type(box TyAlias { generics, .. }) => self
@ -3550,7 +3550,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|i, s, c| MethodNotMemberOfTrait(i, s, c),
);
this.resolve_delegation(delegation)
this.resolve_delegation(delegation, item.id, trait_id.is_some());
},
);
}
@ -3699,17 +3699,30 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
})
}
fn resolve_delegation(&mut self, delegation: &'ast Delegation) {
fn resolve_delegation(
&mut self,
delegation: &'ast Delegation,
item_id: NodeId,
is_in_trait_impl: bool,
) {
self.smart_resolve_path(
delegation.id,
&delegation.qself,
&delegation.path,
PathSource::Delegation,
);
if let Some(qself) = &delegation.qself {
self.visit_ty(&qself.ty);
}
self.visit_path(&delegation.path);
self.r.delegation_sig_resolution_nodes.insert(
self.r.local_def_id(item_id),
if is_in_trait_impl { item_id } else { delegation.id },
);
let Some(body) = &delegation.body else { return };
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
let span = delegation.path.segments.last().unwrap().ident.span;
@ -4294,7 +4307,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
);
}
#[instrument(level = "debug", skip(self))]
fn smart_resolve_path_fragment(
&mut self,
qself: &Option<Box<QSelf>>,

View file

@ -1275,6 +1275,7 @@ pub struct Resolver<'ra, 'tcx> {
/// Amount of lifetime parameters for each item in the crate.
item_generics_num_lifetimes: FxHashMap<LocalDefId, usize>,
delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
delegation_sig_resolution_nodes: LocalDefIdMap<NodeId>,
main_def: Option<MainDefinition> = None,
trait_impls: FxIndexMap<DefId, Vec<LocalDefId>>,
@ -1693,6 +1694,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
current_crate_outer_attr_insert_span,
mods_with_parse_errors: Default::default(),
impl_trait_names: Default::default(),
delegation_sig_resolution_nodes: Default::default(),
..
};
@ -1821,6 +1823,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
lifetime_elision_allowed: self.lifetime_elision_allowed,
lint_buffer: Steal::new(self.lint_buffer),
delegation_fn_sigs: self.delegation_fn_sigs,
delegation_sig_resolution_nodes: self.delegation_sig_resolution_nodes,
};
ResolverOutputs { global_ctxt, ast_lowering }
}

View file

@ -0,0 +1,7 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
fn foo() {}
reuse foo as bar;
pub reuse bar as goo;

View file

@ -1,12 +1,12 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
// FIXME(fn_delegation): `recursive delegation` error should be emitted here
trait Trait {
reuse Trait::foo { &self.0 }
//~^ ERROR failed to resolve delegation callee
}
reuse foo;
//~^ ERROR cycle detected when computing generics of `foo`
//~^ ERROR failed to resolve delegation callee
fn main() {}

View file

@ -1,17 +1,14 @@
error[E0391]: cycle detected when computing generics of `foo`
error: failed to resolve delegation callee
--> $DIR/ice-issue-124347.rs:5:18
|
LL | reuse Trait::foo { &self.0 }
| ^^^
error: failed to resolve delegation callee
--> $DIR/ice-issue-124347.rs:9:7
|
LL | reuse foo;
| ^^^
|
= note: ...which immediately requires computing generics of `foo` again
note: cycle used when checking that `foo` is well-formed
--> $DIR/ice-issue-124347.rs:9:7
|
LL | reuse foo;
| ^^^
= 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: aborting due to 1 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0391`.

View file

@ -0,0 +1,54 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
mod first_mod {
reuse foo;
//~^ ERROR failed to resolve delegation callee
}
mod second_mod {
reuse foo as bar;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse bar as foo;
//~^ ERROR encountered a cycle during delegation signature resolution
}
mod third_mod {
reuse foo as foo1;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse foo1 as foo2;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse foo2 as foo3;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse foo3 as foo4;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse foo4 as foo5;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse foo5 as foo;
//~^ ERROR encountered a cycle during delegation signature resolution
}
mod fourth_mod {
trait Trait {
reuse Trait::foo as bar;
//~^ ERROR encountered a cycle during delegation signature resolution
reuse Trait::bar as foo;
//~^ ERROR encountered a cycle during delegation signature resolution
}
}
mod fifth_mod {
reuse super::fifth_mod::{bar as foo, foo as bar};
//~^ ERROR encountered a cycle during delegation signature resolution
//~| ERROR encountered a cycle during delegation signature resolution
trait GlobReuse {
reuse GlobReuse::{foo as bar, bar as goo, goo as foo};
//~^ ERROR encountered a cycle during delegation signature resolution
//~| ERROR encountered a cycle during delegation signature resolution
//~| ERROR encountered a cycle during delegation signature resolution
}
}
fn main() {}

View file

@ -0,0 +1,98 @@
error: failed to resolve delegation callee
--> $DIR/recursive-delegation-errors.rs:6:11
|
LL | reuse foo;
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:11:11
|
LL | reuse foo as bar;
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:13:11
|
LL | reuse bar as foo;
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:18:11
|
LL | reuse foo as foo1;
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:20:11
|
LL | reuse foo1 as foo2;
| ^^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:22:11
|
LL | reuse foo2 as foo3;
| ^^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:24:11
|
LL | reuse foo3 as foo4;
| ^^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:26:11
|
LL | reuse foo4 as foo5;
| ^^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:28:11
|
LL | reuse foo5 as foo;
| ^^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:34:22
|
LL | reuse Trait::foo as bar;
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:36:22
|
LL | reuse Trait::bar as foo;
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:42:30
|
LL | reuse super::fifth_mod::{bar as foo, foo as bar};
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:42:42
|
LL | reuse super::fifth_mod::{bar as foo, foo as bar};
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:47:27
|
LL | reuse GlobReuse::{foo as bar, bar as goo, goo as foo};
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:47:39
|
LL | reuse GlobReuse::{foo as bar, bar as goo, goo as foo};
| ^^^
error: encountered a cycle during delegation signature resolution
--> $DIR/recursive-delegation-errors.rs:47:51
|
LL | reuse GlobReuse::{foo as bar, bar as goo, goo as foo};
| ^^^
error: aborting due to 16 previous errors

View file

@ -0,0 +1,68 @@
//@ check-pass
//@ edition:2018
//@ aux-crate:recursive_delegation_aux=recursive-delegation-aux.rs
#![feature(fn_delegation)]
#![allow(incomplete_features)]
mod first_mod {
pub mod to_reuse {
pub fn foo(x: usize) -> usize {
x + 1
}
}
mod single_reuse {
reuse crate::first_mod::to_reuse::foo;
reuse foo as bar;
reuse foo as bar1;
reuse bar as goo;
reuse goo as koo;
reuse koo as too;
}
mod glob_reuse {
reuse super::to_reuse::{foo as bar, foo as bar1} { self }
reuse super::glob_reuse::{bar as goo, goo as koo, koo as too} { self }
}
}
mod second_mod {
trait T {
fn foo(&self);
reuse T::foo as bar;
reuse T::bar as goo;
reuse T::goo as poo;
}
trait TGlob {
fn xd(&self) -> &Self;
fn foo1(&self);
fn foo2(&self);
fn foo3(&self);
fn foo4(&self);
reuse TGlob::{foo1 as bar1, foo3 as bar3, bar1 as bar11, bar11 as bar111} { self.xd() }
}
}
mod third_mod {
reuse crate::first_mod::to_reuse::foo {
reuse foo as bar {
reuse bar as goo {
bar(123)
}
goo(123)
}
bar(123)
}
}
mod fourth_mod {
reuse recursive_delegation_aux::goo as bar;
reuse bar as foo;
}
fn main() {}

View file

@ -36,24 +36,15 @@ LL | reuse ToReuse::opaque_ret;
| ^^^^^^^^^^
= 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/unsupported.rs:46:22
|
LL | pub reuse to_reuse2::foo;
| --- callee defined here
...
LL | reuse to_reuse1::foo;
| ^^^
error[E0283]: type annotations needed
--> $DIR/unsupported.rs:55:18
--> $DIR/unsupported.rs:54:18
|
LL | reuse Trait::foo;
| ^^^ cannot infer type
|
= note: cannot satisfy `_: effects::Trait`
error: aborting due to 4 previous errors
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0283, E0391.
For more information about an error, try `rustc --explain E0283`.

View file

@ -28,24 +28,15 @@ LL | reuse ToReuse::opaque_ret;
= note: cycle used when computing implied outlives bounds for `<u16 as opaque::ToReuse>::opaque_ret::{anon_assoc#0}` (hack disabled = false)
= 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/unsupported.rs:46:22
|
LL | pub reuse to_reuse2::foo;
| --- callee defined here
...
LL | reuse to_reuse1::foo;
| ^^^
error[E0283]: type annotations needed
--> $DIR/unsupported.rs:55:18
--> $DIR/unsupported.rs:54:18
|
LL | reuse Trait::foo;
| ^^^ cannot infer type
|
= note: cannot satisfy `_: effects::Trait`
error: aborting due to 4 previous errors
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0283, E0391.
For more information about an error, try `rustc --explain E0283`.

View file

@ -44,7 +44,6 @@ mod recursive {
}
reuse to_reuse1::foo;
//~^ ERROR recursive delegation is not supported yet
}
mod effects {