delegation: Implement glob delegation
This commit is contained in:
parent
7ac6c2fc68
commit
22d0b1ee18
31 changed files with 892 additions and 135 deletions
|
@ -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) =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue