Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 63 additions & 13 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,19 @@ 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};
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::span_bug;
use rustc_middle::ty::{Asyncness, DelegationFnSigAttrs, ResolverAstLowering};
use rustc_span::symbol::kw;
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> {
Expand Down Expand Up @@ -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.get_delegation_sig_node_id(&self.local_def_id(item_id)),
span,
);

match sig_id {
Ok(sig_id) => {
self.add_attributes_if_needed(span, sig_id);
Expand Down Expand Up @@ -238,18 +244,59 @@ 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 def_id = self.opt_get_partial_res_id(node_id);

// If def_id is in local crate and there is no signature in delegation_fn_sigs
// 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(def_id) = def_id
&& let Some(local_id) = def_id.as_local()
&& !self.resolver.delegation_fn_sigs.contains_key(&local_id)
{
node_id = self.get_delegation_sig_node_id(&local_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 }),
});
}

continue;
}

// DefId is either None or is from non-local crate, fallback to the original routine.
return self.def_id_or_guaranteed_err(def_id, node_id, span);
}
}

fn get_delegation_sig_node_id(&self, local_id: &LocalDefId) -> NodeId {
*self
.resolver
.delegation_sig_resolution_nodes
.get(local_id)
.expect("All delegations should have signature resolution NodeId")
}

fn opt_get_partial_res_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 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());
fn def_id_or_guaranteed_err(
&self,
def_id: Option<DefId>,
node_id: NodeId,
span: Span,
) -> Result<DefId, ErrorGuaranteed> {
def_id.ok_or_else(|| {
self.tcx.dcx().span_delayed_bug(
span,
Expand All @@ -258,6 +305,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}

fn get_resolution_id(&mut self, node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
let def_id = self.opt_get_partial_res_id(node_id);
self.def_id_or_guaranteed_err(def_id, node_id, span)
}

fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> {
self.arena.alloc(hir::Generics {
params: &[],
Expand All @@ -271,8 +323,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),
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
6 changes: 3 additions & 3 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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,
(
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_hir_analysis/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ pub struct ResolverAstLowering {

/// Information about functions signatures for delegation items expansion
pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
// NodeIds for delegation signature resolution
pub delegation_sig_resolution_nodes: LocalDefIdMap<ast::NodeId>,
}

bitflags::bitflags! {
Expand Down
22 changes: 17 additions & 5 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
);
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
},
);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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>>,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,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>>,
Expand Down Expand Up @@ -1694,6 +1695,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(),
..
};

Expand Down Expand Up @@ -1822,6 +1824,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 }
}
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/delegation/auxiliary/recursive-delegation-aux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]

fn foo() {}

reuse foo as bar;
pub reuse bar as goo;
4 changes: 2 additions & 2 deletions tests/ui/delegation/ice-issue-124347.rs
Original file line number Diff line number Diff line change
@@ -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() {}
17 changes: 7 additions & 10 deletions tests/ui/delegation/ice-issue-124347.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
error[E0391]: cycle detected when computing generics of `foo`
--> $DIR/ice-issue-124347.rs:9:7
error: failed to resolve delegation callee
--> $DIR/ice-issue-124347.rs:5:18
|
LL | reuse foo;
| ^^^
|
= note: ...which immediately requires computing generics of `foo` again
note: cycle used when checking that `foo` is well-formed
LL | reuse Trait::foo { &self.0 }
| ^^^

error: failed to resolve delegation callee
--> $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`.
57 changes: 57 additions & 0 deletions tests/ui/delegation/recursive_delegation_errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
#![allow(dead_code)]
#![allow(unused_variables)]


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() {}
Loading
Loading