1
Fork 0

delegation: Implement glob delegation

This commit is contained in:
Vadim Petrochenkov 2024-03-15 14:21:03 +03:00
parent 7ac6c2fc68
commit 22d0b1ee18
31 changed files with 892 additions and 135 deletions

View file

@ -19,6 +19,7 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
use rustc_attr as attr;
use rustc_data_structures::sync::Lrc;
use rustc_expand::base::ResolverExpand;
use rustc_expand::expand::AstFragment;
use rustc_hir::def::{self, *};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
@ -1358,6 +1359,14 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
self.visit_invoc_in_module(item.id);
}
AssocCtxt::Impl => {
let invoc_id = item.id.placeholder_to_expn_id();
if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) {
self.r
.impl_unexpanded_invocations
.entry(self.r.invocation_parent(invoc_id))
.or_default()
.insert(invoc_id);
}
self.visit_invoc(item.id);
}
}
@ -1379,18 +1388,21 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
self.r.feed_visibility(feed, vis);
}
let ns = match item.kind {
AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => {
ValueNS
}
AssocItemKind::Type(..) => TypeNS,
AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
};
if ctxt == AssocCtxt::Trait {
let ns = match item.kind {
AssocItemKind::Const(..)
| AssocItemKind::Delegation(..)
| AssocItemKind::Fn(..) => ValueNS,
AssocItemKind::Type(..) => TypeNS,
AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
};
let parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion;
self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
let impl_def_id = self.r.tcx.local_parent(local_def_id);
let key = BindingKey::new(item.ident.normalize_to_macros_2_0(), ns);
self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
}
visit::walk_assoc_item(self, item, ctxt);

View file

@ -144,8 +144,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
}
ItemKind::GlobalAsm(..) => DefKind::GlobalAsm,
ItemKind::Use(..) => return visit::walk_item(self, i),
ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
ItemKind::DelegationMac(..) => unreachable!(),
ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
return self.visit_macro_invoc(i.id);
}
};
let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span);
@ -294,8 +295,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn,
AssocItemKind::Const(..) => DefKind::AssocConst,
AssocItemKind::Type(..) => DefKind::AssocTy,
AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
AssocItemKind::DelegationMac(..) => unreachable!(),
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
return self.visit_macro_invoc(i.id);
}
};
let def = self.create_def(i.id, i.ident.name, def_kind, i.span);

View file

@ -3281,17 +3281,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
self.visit_path(&delegation.path, delegation.id);
if let Some(body) = &delegation.body {
// `PatBoundCtx` is not necessary in this context
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
// `PatBoundCtx` is not necessary in this context
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
let span = delegation.path.segments.last().unwrap().ident.span;
self.fresh_binding(
Ident::new(kw::SelfLower, span),
delegation.id,
PatternSource::FnParam,
&mut bindings,
);
self.visit_block(body);
let span = delegation.path.segments.last().unwrap().ident.span;
this.fresh_binding(
Ident::new(kw::SelfLower, span),
delegation.id,
PatternSource::FnParam,
&mut bindings,
);
this.visit_block(body);
});
}
}

View file

@ -1089,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> {
single_segment_macro_resolutions:
Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
multi_segment_macro_resolutions:
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>,
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>, Namespace)>,
builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
/// `derive(Copy)` marks items they are applied to so they are treated specially later.
/// Derive macros cannot modify the item themselves and have to store the markers in the global
@ -1163,6 +1163,15 @@ pub struct Resolver<'a, 'tcx> {
doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
all_macro_rules: FxHashMap<Symbol, Res>,
/// Invocation ids of all glob delegations.
glob_delegation_invoc_ids: FxHashSet<LocalExpnId>,
/// Analogue of module `unexpanded_invocations` but in trait impls, excluding glob delegations.
/// Needed because glob delegations wait for all other neighboring macros to expand.
impl_unexpanded_invocations: FxHashMap<LocalDefId, FxHashSet<LocalExpnId>>,
/// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations.
/// Needed because glob delegations exclude explicitly defined names.
impl_binding_keys: FxHashMap<LocalDefId, FxHashSet<BindingKey>>,
}
/// Nothing really interesting here; it just provides memory for the rest of the crate.
@ -1504,6 +1513,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
doc_link_traits_in_scope: Default::default(),
all_macro_rules: Default::default(),
delegation_fn_sigs: Default::default(),
glob_delegation_invoc_ids: Default::default(),
impl_unexpanded_invocations: Default::default(),
impl_binding_keys: Default::default(),
};
let root_parent_scope = ParentScope::module(graph_root, &resolver);

View file

@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution;
use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope};
use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive};
use crate::Namespace::*;
use crate::{BuiltinMacroState, Determinacy, MacroData, Used};
use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, Used};
use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc_ast::expand::StrippedCfgItem;
@ -198,6 +198,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
if let Some(unexpanded_invocations) =
self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion))
{
unexpanded_invocations.remove(&expansion);
}
}
fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) {
@ -262,15 +267,21 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
}
};
let (path, kind, inner_attr, derives) = match invoc.kind {
InvocationKind::Attr { ref attr, ref derives, .. } => (
&attr.get_normal_item().path,
MacroKind::Attr,
attr.style == ast::AttrStyle::Inner,
self.arenas.alloc_ast_paths(derives),
),
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]),
InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]),
let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None);
let (path, kind) = match invoc.kind {
InvocationKind::Attr { ref attr, derives: ref attr_derives, .. } => {
derives = self.arenas.alloc_ast_paths(attr_derives);
inner_attr = attr.style == ast::AttrStyle::Inner;
(&attr.get_normal_item().path, MacroKind::Attr)
}
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang),
InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive),
InvocationKind::GlobDelegation { ref item } => {
let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() };
deleg_impl = Some(self.invocation_parent(invoc_id));
// It is sufficient to consider glob delegation a bang macro for now.
(&deleg.prefix, MacroKind::Bang)
}
};
// Derives are not included when `invocations` are collected, so we have to add them here.
@ -286,10 +297,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
node_id,
force,
soft_custom_inner_attributes_gate(path, invoc),
deleg_impl,
)?;
let span = invoc.span();
let def_id = res.opt_def_id();
let def_id = if deleg_impl.is_some() { None } else { res.opt_def_id() };
invoc_id.set_expn_data(
ext.expn_data(
parent_scope.expansion,
@ -452,6 +464,45 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
fn registered_tools(&self) -> &RegisteredTools {
self.registered_tools
}
fn register_glob_delegation(&mut self, invoc_id: LocalExpnId) {
self.glob_delegation_invoc_ids.insert(invoc_id);
}
fn glob_delegation_suffixes(
&mut self,
trait_def_id: DefId,
impl_def_id: LocalDefId,
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> {
let target_trait = self.expect_module(trait_def_id);
if !target_trait.unexpanded_invocations.borrow().is_empty() {
return Err(Indeterminate);
}
// FIXME: Instead of waiting try generating all trait methods, and pruning
// the shadowed ones a bit later, e.g. when all macro expansion completes.
// Pros: expansion will be stuck less (but only in exotic cases), the implementation may be
// less hacky.
// Cons: More code is generated just to be deleted later, deleting already created `DefId`s
// may be nontrivial.
if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get(&impl_def_id)
&& !unexpanded_invocations.is_empty()
{
return Err(Indeterminate);
}
let mut idents = Vec::new();
target_trait.for_each_child(self, |this, ident, ns, _binding| {
// FIXME: Adjust hygiene for idents from globs, like for glob imports.
if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id)
&& overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns))
{
// The name is overridden, do not produce it from the glob delegation.
} else {
idents.push((ident, None));
}
});
Ok(idents)
}
}
impl<'a, 'tcx> Resolver<'a, 'tcx> {
@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
node_id: NodeId,
force: bool,
soft_custom_inner_attributes_gate: bool,
deleg_impl: Option<LocalDefId>,
) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> {
let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force)
{
let (ext, res) = match self.resolve_macro_or_delegation_path(
path,
Some(kind),
parent_scope,
true,
force,
deleg_impl,
) {
Ok((Some(ext), res)) => (ext, res),
Ok((None, res)) => (self.dummy_ext(kind), res),
Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err),
Err(Determinacy::Undetermined) => return Err(Indeterminate),
};
// Everything below is irrelevant to glob delegation, take a shortcut.
if deleg_impl.is_some() {
if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) {
self.dcx().emit_err(MacroExpectedFound {
span: path.span,
expected: "trait",
article: "a",
found: res.descr(),
macro_path: &pprust::path_to_string(path),
remove_surrounding_derive: None,
add_as_non_derive: None,
});
return Ok((self.dummy_ext(kind), Res::Err));
}
return Ok((ext, res));
}
// Report errors for the resolved macro.
for segment in &path.segments {
if let Some(args) = &segment.args {
@ -605,12 +681,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
parent_scope: &ParentScope<'a>,
trace: bool,
force: bool,
) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None)
}
fn resolve_macro_or_delegation_path(
&mut self,
path: &ast::Path,
kind: Option<MacroKind>,
parent_scope: &ParentScope<'a>,
trace: bool,
force: bool,
deleg_impl: Option<LocalDefId>,
) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
let path_span = path.span;
let mut path = Segment::from_path(path);
// Possibly apply the macro helper hack
if kind == Some(MacroKind::Bang)
if deleg_impl.is_none()
&& kind == Some(MacroKind::Bang)
&& path.len() == 1
&& path[0].ident.span.ctxt().outer_expn_data().local_inner_macros
{
@ -618,13 +707,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
path.insert(0, Segment::from_ident(root));
}
let res = if path.len() > 1 {
let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) {
let res = if deleg_impl.is_some() || path.len() > 1 {
let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS };
let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) {
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res),
PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
PathResult::NonModule(..)
| PathResult::Indeterminate
| PathResult::Failed { .. } => Err(Determinacy::Determined),
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
Ok(module.res().unwrap())
}
PathResult::Module(..) => unreachable!(),
};
@ -636,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
kind,
*parent_scope,
res.ok(),
ns,
));
}
@ -670,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
res
};
res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res))
let res = res?;
let ext = match deleg_impl {
Some(impl_def_id) => match res {
def::Res::Def(DefKind::Trait, def_id) => {
let edition = self.tcx.sess.edition();
Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition)))
}
_ => None,
},
None => self.get_macro(res).map(|macro_data| macro_data.ext.clone()),
};
Ok((ext, res))
}
pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) {
@ -706,14 +811,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
};
let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions);
for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions {
for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions {
// FIXME: Path resolution will ICE if segment IDs present.
for seg in &mut path {
seg.id = None;
}
match self.resolve_path(
&path,
Some(MacroNS),
Some(ns),
&parent_scope,
Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
None,
@ -721,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => {
check_consistency(self, &path, path_span, kind, initial_res, res)
}
// This may be a trait for glob delegation expansions.
PathResult::Module(ModuleOrUniformRoot::Module(module)) => check_consistency(
self,
&path,
path_span,
kind,
initial_res,
module.res().unwrap(),
),
path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => {
let mut suggestion = None;
let (span, label, module) =