Skip to content
Draft
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
9 changes: 8 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3908,6 +3908,13 @@ pub struct EiiImpl {
pub is_default: bool,
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum DelegationSource {
Single,
List,
Glob,
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct Delegation {
/// Path resolution id.
Expand All @@ -3918,7 +3925,7 @@ pub struct Delegation {
pub rename: Option<Ident>,
pub body: Option<Box<Block>>,
/// The item was expanded from a glob delegation item.
pub from_glob: bool,
pub source: DelegationSource,
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,9 @@ macro_rules! common_visitor_and_walkers {
ConstItem,
ConstItemRhsKind,
Defaultness,
Delegation,
DelegationMac,
DelegationSuffixes,
DelegationSource,
DelimArgs,
DelimSpan,
EnumDef,
Expand Down Expand Up @@ -573,6 +573,7 @@ macro_rules! common_visitor_and_walkers {
fn visit_fn_decl(FnDecl);
fn visit_fn_header(FnHeader);
fn visit_fn_ret_ty(FnRetTy);
fn visit_delegation(Delegation);
//fn visit_foreign_item(ForeignItem);
fn visit_foreign_mod(ForeignMod);
fn visit_format_args(FormatArgs);
Expand Down
79 changes: 61 additions & 18 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ use rustc_span::{Ident, Span, Symbol};
use smallvec::SmallVec;

use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults};
use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee};
use crate::errors::{
CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
};
use crate::{
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
ResolverAstLoweringExt,
Expand Down Expand Up @@ -122,9 +125,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span);

// Delegation can be unresolved in illegal places such as function bodies in extern blocks (see #151356)
let sig_id = if let Some(delegation_info) = self.resolver.delegation_info(self.owner.def_id)
let sig_id = if let Some(resolution_node) =
self.resolver.delegation_info(self.owner.def_id).and_then(|i| i.resolution_node)
{
self.get_sig_id(delegation_info.resolution_node, span)
self.get_sig_id(resolution_node, span)
} else {
self.dcx().span_delayed_bug(
span,
Expand All @@ -142,11 +146,15 @@ impl<'hir> LoweringContext<'_, 'hir> {

let (param_count, c_variadic) = self.param_count(sig_id);

if !self.check_block_soundness(delegation, sig_id, is_method, param_count) {
return self.generate_delegation_error(span, delegation);
}

let mut generics = self.uplift_delegation_generics(delegation, sig_id, is_method);

let (body_id, call_expr_id) = self.lower_delegation_body(
delegation,
is_method,
sig_id,
param_count,
&mut generics,
span,
Expand Down Expand Up @@ -179,6 +187,48 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}

fn check_block_soundness(
&self,
delegation: &Delegation,
sig_id: DefId,
is_method: bool,
param_count: usize,
) -> bool {
let mut result = true;

// Report an error if user has explicitly specified delegation's block
// in a single delegation when reused function has no params.
if let Some(block) = delegation.body.as_ref() {
if param_count == 0 && matches!(delegation.source, DelegationSource::Single) {
self.dcx().emit_err(DelegationBlockSpecifiedWhenNoParams { span: block.span });
result = false;
}

if !self.should_generate_block(delegation, sig_id, is_method)
&& self
.resolver
.delegation_info(self.owner.def_id)
.is_some_and(|i| i.block_contains_defs)
{
self.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span });
result = false;
}
}

result
}

fn should_generate_block(
&self,
delegation: &Delegation,
sig_id: DefId,
is_method: bool,
) -> bool {
is_method
|| matches!(self.tcx.def_kind(sig_id), DefKind::Fn)
|| matches!(delegation.source, DelegationSource::Single)
}

fn add_attrs_if_needed(&mut self, span: Span, sig_id: DefId) {
let new_attrs =
self.create_new_attrs(ATTRS_ADDITIONS, span, sig_id, self.attrs.get(&PARENT_ID));
Expand Down Expand Up @@ -253,9 +303,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
// 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(delegation_info) = self.resolver.delegation_info(local_id)
&& let Some(resolution_node) =
self.resolver.delegation_info(local_id).and_then(|i| i.resolution_node)
{
node_id = delegation_info.resolution_node;
node_id = resolution_node;
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.
Expand Down Expand Up @@ -407,7 +458,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_delegation_body(
&mut self,
delegation: &Delegation,
is_method: bool,
sig_id: DefId,
param_count: usize,
generics: &mut GenericsGenerationResults<'hir>,
span: Span,
Expand All @@ -419,12 +470,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
let mut parameters: Vec<hir::Param<'_>> = Vec::with_capacity(param_count);
let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);

let is_method = this.is_method(sig_id, span);

for idx in 0..param_count {
let (param, pat_node_id) = this.generate_param(is_method, idx, span);
parameters.push(param);

let arg = if let Some(block) = block
&& idx == 0
&& this.should_generate_block(delegation, sig_id, is_method)
{
let mut self_resolver = SelfResolver {
ctxt: this,
Expand All @@ -441,17 +495,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
args.push(arg);
}

// If we have no params in signature function but user still wrote some code in
// delegation body, then add this code as first arg, eventually an error will be shown,
// also nested delegations may need to access information about this code (#154332),
// so it is better to leave this code as opposed to bodies of extern functions,
// which are completely erased from existence.
if param_count == 0
&& let Some(block) = block
{
args.push(this.lower_target_expr(&block));
}

let (final_expr, hir_id) =
this.finalize_body_lowering(delegation, args, generics, span);

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 @@ -535,3 +535,17 @@ pub(crate) struct CycleInDelegationSignatureResolution {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("delegation block is specified for function with no params")]
pub(crate) struct DelegationBlockSpecifiedWhenNoParams {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("attempted to delete delegation's block that contains definitions inside")]
pub(crate) struct DelegationAttemptedBlockWithDefsDeletion {
#[primary_span]
pub span: Span,
}
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> mid_hir::Crate<'_> {
delayed_ids.insert(def_id);
}
_ => lowerer.lower_node(def_id),
};
}
}

// Don't hash unless necessary, because it's expensive.
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2066,7 +2066,11 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>(
ident: rename.unwrap_or(ident),
rename,
body: deleg.body.clone(),
from_glob,
source: if from_glob {
ast::DelegationSource::Glob
} else {
ast::DelegationSource::List
},
})),
tokens: None,
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,12 @@ pub struct ResolverAstLowering<'tcx> {
pub disambiguators: LocalDefIdMap<Steal<PerParentDisambiguatorState>>,
}

#[derive(Debug)]
#[derive(Debug, Default)]
pub struct DelegationInfo {
// NodeId (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 resolution_node: ast::NodeId,
pub resolution_node: Option<ast::NodeId>,
pub block_contains_defs: bool,
}

#[derive(Clone, Copy, Debug, StableHash)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ impl<'a> Parser<'a> {
ident,
rename,
body: self.parse_delegation_body()?,
from_glob: false,
source: DelegationSource::Single,
}))
})
}
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use std::sync::Arc;

use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
use rustc_ast::{
self as ast, AssocItem, AssocItemKind, Block, ConstItem, DUMMY_NODE_ID, Delegation, Fn,
ForeignItem, ForeignItemKind, Inline, Item, ItemKind, NodeId, StaticItem, StmtKind, TraitAlias,
TyAlias,
self as ast, AssocItem, AssocItemKind, Block, ConstItem, DUMMY_NODE_ID, Delegation,
DelegationSource, Fn, ForeignItem, ForeignItemKind, Inline, Item, ItemKind, NodeId, StaticItem,
StmtKind, TraitAlias, TyAlias,
};
use rustc_attr_parsing::AttributeParser;
use rustc_expand::base::{ResolverExpand, SyntaxExtension, SyntaxExtensionKind};
Expand Down Expand Up @@ -1462,7 +1462,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
let parent = self.parent_scope.module.expect_local();
let expansion = self.parent_scope.expansion;
self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob)
} else if !matches!(&item.kind, AssocItemKind::Delegation(d) if matches!(d.source, DelegationSource::Glob))
&& ident.name != kw::Underscore
{
// Don't add underscore names, they cannot be looked up anyway.
Expand Down
52 changes: 39 additions & 13 deletions compiler/rustc_resolve/src/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,39 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
name: Option<Symbol>,
def_kind: DefKind,
span: Span,
) -> TyCtxtFeed<'tcx, LocalDefId> {
self.create_feed(node_id, name, def_kind, span, false)
}

fn create_feed(
&mut self,
node_id: NodeId,
name: Option<Symbol>,
def_kind: DefKind,
span: Span,
is_owner: bool,
) -> TyCtxtFeed<'tcx, LocalDefId> {
let parent_def = self.invocation_parent.parent_def;
debug!(
"create_def(node_id={:?}, def_kind={:?}, parent_def={:?})",
node_id, def_kind, parent_def
);
self.r.create_def(

let feed = self.r.create_def(
parent_def,
node_id,
name,
def_kind,
self.parent_scope.expansion.to_expn_id(),
span.with_parent(None),
false,
)
is_owner,
);

if let Some(last_delegation) = self.invocation_parent.last_delegation {
self.r.delegation_infos.entry(last_delegation).or_default().block_contains_defs = true;
}

feed
}

fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: LocalDefId, f: F) {
Expand All @@ -86,16 +104,8 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
// We only get here if the owner didn't exist yet. After the owner has been created,
// future invocations of `collect_definitions` will get the owner out of the `owners`
// table.
let parent_def = self.invocation_parent.parent_def;
let feed = self.r.create_def(
parent_def,
owner,
name,
def_kind,
self.parent_scope.expansion.to_expn_id(),
span.with_parent(None),
true,
);

let feed = self.create_feed(owner, name, def_kind, span, true);
let tables = PerOwnerResolverData::new(owner, feed.key());

let orig_invoc_owner = mem::replace(&mut self.invocation_parent.owner, owner);
Expand Down Expand Up @@ -240,6 +250,22 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
);
}

fn visit_delegation(&mut self, node: &'a Delegation) {
// Do not map defs in path (i.e., consts with blocks), consider only
// delegation's block which is its first argument.
visit::walk_path(self, &node.path);
node.qself.as_ref().inspect(|qself| visit::walk_qself(self, qself));

let orig_last_delegation = mem::replace(
&mut self.invocation_parent.last_delegation,
Some(self.invocation_parent.parent_def),
);

node.body.as_ref().inspect(|block| self.brg_visit_block(block));

self.invocation_parent.last_delegation = orig_last_delegation;
}

fn visit_block(&mut self, block: &'a Block) {
self.brg_visit_block(block);
}
Expand Down
12 changes: 5 additions & 7 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use rustc_hir::def::{CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRe
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate};
use rustc_middle::middle::resolve_bound_vars::Set1;
use rustc_middle::ty::{AssocTag, DelegationInfo, Visibility};
use rustc_middle::ty::{AssocTag, Visibility};
use rustc_middle::{bug, span_bug};
use rustc_session::config::{CrateType, ResolveDocLinks};
use rustc_session::errors::feature_err;
Expand Down Expand Up @@ -3923,12 +3923,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
this.visit_path(&delegation.path);
});

self.r.delegation_infos.insert(
self.r.current_owner.def_id,
DelegationInfo {
resolution_node: if is_in_trait_impl { item_id } else { delegation.id },
},
);
let info = self.r.delegation_infos.entry(self.r.current_owner.def_id).or_default();
let resolution_node = if is_in_trait_impl { item_id } else { delegation.id };

info.resolution_node = Some(resolution_node);

let Some(body) = &delegation.body else { return };
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
Expand Down
Loading
Loading