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

@ -3158,13 +3158,16 @@ pub struct Delegation {
pub path: Path, pub path: Path,
pub rename: Option<Ident>, pub rename: Option<Ident>,
pub body: Option<P<Block>>, pub body: Option<P<Block>>,
/// The item was expanded from a glob delegation item.
pub from_glob: bool,
} }
#[derive(Clone, Encodable, Decodable, Debug)] #[derive(Clone, Encodable, Decodable, Debug)]
pub struct DelegationMac { pub struct DelegationMac {
pub qself: Option<P<QSelf>>, pub qself: Option<P<QSelf>>,
pub prefix: Path, pub prefix: Path,
pub suffixes: ThinVec<(Ident, Option<Ident>)>, // Some for list delegation, and None for glob delegation.
pub suffixes: Option<ThinVec<(Ident, Option<Ident>)>>,
pub body: Option<P<Block>>, pub body: Option<P<Block>>,
} }
@ -3291,7 +3294,7 @@ pub enum ItemKind {
/// ///
/// E.g. `reuse <Type as Trait>::name { target_expr_template }`. /// E.g. `reuse <Type as Trait>::name { target_expr_template }`.
Delegation(Box<Delegation>), Delegation(Box<Delegation>),
/// A list delegation item (`reuse prefix::{a, b, c}`). /// A list or glob delegation item (`reuse prefix::{a, b, c}`, `reuse prefix::*`).
/// Treated similarly to a macro call and expanded early. /// Treated similarly to a macro call and expanded early.
DelegationMac(Box<DelegationMac>), DelegationMac(Box<DelegationMac>),
} }
@ -3372,7 +3375,7 @@ pub enum AssocItemKind {
MacCall(P<MacCall>), MacCall(P<MacCall>),
/// An associated delegation item. /// An associated delegation item.
Delegation(Box<Delegation>), Delegation(Box<Delegation>),
/// An associated delegation item list. /// An associated list or glob delegation item.
DelegationMac(Box<DelegationMac>), DelegationMac(Box<DelegationMac>),
} }

View File

@ -1162,7 +1162,14 @@ impl NoopVisitItemKind for ItemKind {
} }
ItemKind::MacCall(m) => vis.visit_mac_call(m), ItemKind::MacCall(m) => vis.visit_mac_call(m),
ItemKind::MacroDef(def) => vis.visit_macro_def(def), ItemKind::MacroDef(def) => vis.visit_macro_def(def),
ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { ItemKind::Delegation(box Delegation {
id,
qself,
path,
rename,
body,
from_glob: _,
}) => {
vis.visit_id(id); vis.visit_id(id);
vis.visit_qself(qself); vis.visit_qself(qself);
vis.visit_path(path); vis.visit_path(path);
@ -1176,10 +1183,12 @@ impl NoopVisitItemKind for ItemKind {
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
vis.visit_qself(qself); vis.visit_qself(qself);
vis.visit_path(prefix); vis.visit_path(prefix);
for (ident, rename) in suffixes { if let Some(suffixes) = suffixes {
vis.visit_ident(ident); for (ident, rename) in suffixes {
if let Some(rename) = rename { vis.visit_ident(ident);
vis.visit_ident(rename); if let Some(rename) = rename {
vis.visit_ident(rename);
}
} }
} }
if let Some(body) = body { if let Some(body) = body {
@ -1218,7 +1227,14 @@ impl NoopVisitItemKind for AssocItemKind {
visit_opt(ty, |ty| visitor.visit_ty(ty)); visit_opt(ty, |ty| visitor.visit_ty(ty));
} }
AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { AssocItemKind::Delegation(box Delegation {
id,
qself,
path,
rename,
body,
from_glob: _,
}) => {
visitor.visit_id(id); visitor.visit_id(id);
visitor.visit_qself(qself); visitor.visit_qself(qself);
visitor.visit_path(path); visitor.visit_path(path);
@ -1232,10 +1248,12 @@ impl NoopVisitItemKind for AssocItemKind {
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
visitor.visit_qself(qself); visitor.visit_qself(qself);
visitor.visit_path(prefix); visitor.visit_path(prefix);
for (ident, rename) in suffixes { if let Some(suffixes) = suffixes {
visitor.visit_ident(ident); for (ident, rename) in suffixes {
if let Some(rename) = rename { visitor.visit_ident(ident);
visitor.visit_ident(rename); if let Some(rename) = rename {
visitor.visit_ident(rename);
}
} }
} }
if let Some(body) = body { if let Some(body) = body {

View File

@ -398,7 +398,14 @@ impl WalkItemKind for ItemKind {
} }
ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)), ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, item.id)), ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, item.id)),
ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { ItemKind::Delegation(box Delegation {
id,
qself,
path,
rename,
body,
from_glob: _,
}) => {
if let Some(qself) = qself { if let Some(qself) = qself {
try_visit!(visitor.visit_ty(&qself.ty)); try_visit!(visitor.visit_ty(&qself.ty));
} }
@ -411,10 +418,12 @@ impl WalkItemKind for ItemKind {
try_visit!(visitor.visit_ty(&qself.ty)); try_visit!(visitor.visit_ty(&qself.ty));
} }
try_visit!(visitor.visit_path(prefix, item.id)); try_visit!(visitor.visit_path(prefix, item.id));
for (ident, rename) in suffixes { if let Some(suffixes) = suffixes {
visitor.visit_ident(*ident); for (ident, rename) in suffixes {
if let Some(rename) = rename { visitor.visit_ident(*ident);
visitor.visit_ident(*rename); if let Some(rename) = rename {
visitor.visit_ident(*rename);
}
} }
} }
visit_opt!(visitor, visit_block, body); visit_opt!(visitor, visit_block, body);
@ -828,7 +837,14 @@ impl WalkItemKind for AssocItemKind {
AssocItemKind::MacCall(mac) => { AssocItemKind::MacCall(mac) => {
try_visit!(visitor.visit_mac_call(mac)); try_visit!(visitor.visit_mac_call(mac));
} }
AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { AssocItemKind::Delegation(box Delegation {
id,
qself,
path,
rename,
body,
from_glob: _,
}) => {
if let Some(qself) = qself { if let Some(qself) = qself {
try_visit!(visitor.visit_ty(&qself.ty)); try_visit!(visitor.visit_ty(&qself.ty));
} }
@ -841,10 +857,12 @@ impl WalkItemKind for AssocItemKind {
try_visit!(visitor.visit_ty(&qself.ty)); try_visit!(visitor.visit_ty(&qself.ty));
} }
try_visit!(visitor.visit_path(prefix, item.id)); try_visit!(visitor.visit_path(prefix, item.id));
for (ident, rename) in suffixes { if let Some(suffixes) = suffixes {
visitor.visit_ident(*ident); for (ident, rename) in suffixes {
if let Some(rename) = rename { visitor.visit_ident(*ident);
visitor.visit_ident(*rename); if let Some(rename) = rename {
visitor.visit_ident(*rename);
}
} }
} }
visit_opt!(visitor, visit_block, body); visit_opt!(visitor, visit_block, body);

View File

@ -9,6 +9,12 @@ use rustc_ast::ptr::P;
use rustc_ast::ModKind; use rustc_ast::ModKind;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
enum DelegationKind<'a> {
Single,
List(&'a [(Ident, Option<Ident>)]),
Glob,
}
fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s)
} }
@ -387,7 +393,7 @@ impl<'a> State<'a> {
&item.vis, &item.vis,
&deleg.qself, &deleg.qself,
&deleg.path, &deleg.path,
None, DelegationKind::Single,
&deleg.body, &deleg.body,
), ),
ast::ItemKind::DelegationMac(deleg) => self.print_delegation( ast::ItemKind::DelegationMac(deleg) => self.print_delegation(
@ -395,7 +401,7 @@ impl<'a> State<'a> {
&item.vis, &item.vis,
&deleg.qself, &deleg.qself,
&deleg.prefix, &deleg.prefix,
Some(&deleg.suffixes), deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)),
&deleg.body, &deleg.body,
), ),
} }
@ -579,7 +585,7 @@ impl<'a> State<'a> {
vis, vis,
&deleg.qself, &deleg.qself,
&deleg.path, &deleg.path,
None, DelegationKind::Single,
&deleg.body, &deleg.body,
), ),
ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation( ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation(
@ -587,20 +593,20 @@ impl<'a> State<'a> {
vis, vis,
&deleg.qself, &deleg.qself,
&deleg.prefix, &deleg.prefix,
Some(&deleg.suffixes), deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)),
&deleg.body, &deleg.body,
), ),
} }
self.ann.post(self, AnnNode::SubItem(id)) self.ann.post(self, AnnNode::SubItem(id))
} }
pub(crate) fn print_delegation( fn print_delegation(
&mut self, &mut self,
attrs: &[ast::Attribute], attrs: &[ast::Attribute],
vis: &ast::Visibility, vis: &ast::Visibility,
qself: &Option<P<ast::QSelf>>, qself: &Option<P<ast::QSelf>>,
path: &ast::Path, path: &ast::Path,
suffixes: Option<&[(Ident, Option<Ident>)]>, kind: DelegationKind<'_>,
body: &Option<P<ast::Block>>, body: &Option<P<ast::Block>>,
) { ) {
if body.is_some() { if body.is_some() {
@ -614,21 +620,28 @@ impl<'a> State<'a> {
} else { } else {
self.print_path(path, false, 0); self.print_path(path, false, 0);
} }
if let Some(suffixes) = suffixes { match kind {
self.word("::"); DelegationKind::Single => {}
self.word("{"); DelegationKind::List(suffixes) => {
for (i, (ident, rename)) in suffixes.iter().enumerate() { self.word("::");
self.print_ident(*ident); self.word("{");
if let Some(rename) = rename { for (i, (ident, rename)) in suffixes.iter().enumerate() {
self.nbsp(); self.print_ident(*ident);
self.word_nbsp("as"); if let Some(rename) = rename {
self.print_ident(*rename); self.nbsp();
} self.word_nbsp("as");
if i != suffixes.len() - 1 { self.print_ident(*rename);
self.word_space(","); }
if i != suffixes.len() - 1 {
self.word_space(",");
}
} }
self.word("}");
}
DelegationKind::Glob => {
self.word("::");
self.word("*");
} }
self.word("}");
} }
if let Some(body) = body { if let Some(body) = body {
self.nbsp(); self.nbsp();

View File

@ -35,8 +35,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding
.label = duplicate binding .label = duplicate binding
.label2 = previous binding .label2 = previous binding
expand_empty_delegation_list = expand_empty_delegation_mac =
empty list delegation is not supported empty {$kind} delegation is not supported
expand_expected_paren_or_brace = expand_expected_paren_or_brace =
expected `(` or `{"{"}`, found `{$token}` expected `(` or `{"{"}`, found `{$token}`
@ -58,6 +58,9 @@ expand_feature_removed =
.label = feature has been removed .label = feature has been removed
.reason = {$reason} .reason = {$reason}
expand_glob_delegation_outside_impls =
glob delegation is only supported in impls
expand_helper_attribute_name_invalid = expand_helper_attribute_name_invalid =
`{$name}` cannot be a name of derive helper attribute `{$name}` cannot be a name of derive helper attribute

View File

@ -357,6 +357,10 @@ where
} }
} }
pub trait GlobDelegationExpander {
fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult<Vec<(Ident, Option<Ident>)>, ()>;
}
// Use a macro because forwarding to a simple function has type system issues // Use a macro because forwarding to a simple function has type system issues
macro_rules! make_stmts_default { macro_rules! make_stmts_default {
($me:expr) => { ($me:expr) => {
@ -714,6 +718,9 @@ pub enum SyntaxExtensionKind {
/// The produced AST fragment is appended to the input AST fragment. /// The produced AST fragment is appended to the input AST fragment.
Box<dyn MultiItemModifier + sync::DynSync + sync::DynSend>, Box<dyn MultiItemModifier + sync::DynSync + sync::DynSend>,
), ),
/// A glob delegation.
GlobDelegation(Box<dyn GlobDelegationExpander + sync::DynSync + sync::DynSend>),
} }
/// A struct representing a macro definition in "lowered" form ready for expansion. /// A struct representing a macro definition in "lowered" form ready for expansion.
@ -748,7 +755,9 @@ impl SyntaxExtension {
/// Returns which kind of macro calls this syntax extension. /// Returns which kind of macro calls this syntax extension.
pub fn macro_kind(&self) -> MacroKind { pub fn macro_kind(&self) -> MacroKind {
match self.kind { match self.kind {
SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang, SyntaxExtensionKind::Bang(..)
| SyntaxExtensionKind::LegacyBang(..)
| SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang,
SyntaxExtensionKind::Attr(..) SyntaxExtensionKind::Attr(..)
| SyntaxExtensionKind::LegacyAttr(..) | SyntaxExtensionKind::LegacyAttr(..)
| SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr, | SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr,
@ -922,6 +931,32 @@ impl SyntaxExtension {
SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition) SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition)
} }
pub fn glob_delegation(
trait_def_id: DefId,
impl_def_id: LocalDefId,
edition: Edition,
) -> SyntaxExtension {
struct GlobDelegationExpanderImpl {
trait_def_id: DefId,
impl_def_id: LocalDefId,
}
impl GlobDelegationExpander for GlobDelegationExpanderImpl {
fn expand(
&self,
ecx: &mut ExtCtxt<'_>,
) -> ExpandResult<Vec<(Ident, Option<Ident>)>, ()> {
match ecx.resolver.glob_delegation_suffixes(self.trait_def_id, self.impl_def_id) {
Ok(suffixes) => ExpandResult::Ready(suffixes),
Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()),
Err(Indeterminate) => ExpandResult::Retry(()),
}
}
}
let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id };
SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Box::new(expander)), edition)
}
pub fn expn_data( pub fn expn_data(
&self, &self,
parent: LocalExpnId, parent: LocalExpnId,
@ -1030,6 +1065,16 @@ pub trait ResolverExpand {
/// Tools registered with `#![register_tool]` and used by tool attributes and lints. /// Tools registered with `#![register_tool]` and used by tool attributes and lints.
fn registered_tools(&self) -> &RegisteredTools; fn registered_tools(&self) -> &RegisteredTools;
/// Mark this invocation id as a glob delegation.
fn register_glob_delegation(&mut self, invoc_id: LocalExpnId);
/// Names of specific methods to which glob delegation expands.
fn glob_delegation_suffixes(
&mut self,
trait_def_id: DefId,
impl_def_id: LocalDefId,
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>;
} }
pub trait LintStoreExpand { pub trait LintStoreExpand {

View File

@ -435,8 +435,16 @@ pub struct ExpectedParenOrBrace<'a> {
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(expand_empty_delegation_list)] #[diag(expand_empty_delegation_mac)]
pub(crate) struct EmptyDelegationList { pub(crate) struct EmptyDelegationMac {
#[primary_span]
pub span: Span,
pub kind: String,
}
#[derive(Diagnostic)]
#[diag(expand_glob_delegation_outside_impls)]
pub(crate) struct GlobDelegationOutsideImpls {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
} }

View File

@ -1,8 +1,8 @@
use crate::base::*; use crate::base::*;
use crate::config::StripUnconfigured; use crate::config::StripUnconfigured;
use crate::errors::{ use crate::errors::{
EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached,
RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
}; };
use crate::mbe::diagnostics::annotate_err_with_kind; use crate::mbe::diagnostics::annotate_err_with_kind;
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
@ -343,6 +343,9 @@ pub enum InvocationKind {
is_const: bool, is_const: bool,
item: Annotatable, item: Annotatable,
}, },
GlobDelegation {
item: P<ast::AssocItem>,
},
} }
impl InvocationKind { impl InvocationKind {
@ -370,6 +373,7 @@ impl Invocation {
InvocationKind::Bang { span, .. } => *span, InvocationKind::Bang { span, .. } => *span,
InvocationKind::Attr { attr, .. } => attr.span, InvocationKind::Attr { attr, .. } => attr.span,
InvocationKind::Derive { path, .. } => path.span, InvocationKind::Derive { path, .. } => path.span,
InvocationKind::GlobDelegation { item } => item.span,
} }
} }
@ -378,6 +382,7 @@ impl Invocation {
InvocationKind::Bang { span, .. } => span, InvocationKind::Bang { span, .. } => span,
InvocationKind::Attr { attr, .. } => &mut attr.span, InvocationKind::Attr { attr, .. } => &mut attr.span,
InvocationKind::Derive { path, .. } => &mut path.span, InvocationKind::Derive { path, .. } => &mut path.span,
InvocationKind::GlobDelegation { item } => &mut item.span,
} }
} }
} }
@ -800,6 +805,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
} }
_ => unreachable!(), _ => unreachable!(),
}, },
InvocationKind::GlobDelegation { item } => {
let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() };
let suffixes = match ext {
SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx)
{
ExpandResult::Ready(suffixes) => suffixes,
ExpandResult::Retry(()) => {
// Reassemble the original invocation for retrying.
return ExpandResult::Retry(Invocation {
kind: InvocationKind::GlobDelegation { item },
..invoc
});
}
},
SyntaxExtensionKind::LegacyBang(..) => {
let msg = "expanded a dummy glob delegation";
let guar = self.cx.dcx().span_delayed_bug(span, msg);
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
}
_ => unreachable!(),
};
type Node = AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>;
let single_delegations = build_single_delegations::<Node>(
self.cx, deleg, &item, &suffixes, item.span, true,
);
fragment_kind.expect_from_annotatables(
single_delegations.map(|item| Annotatable::ImplItem(P(item))),
)
}
}) })
} }
@ -1067,7 +1102,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) { fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
unreachable!() unreachable!()
} }
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
None None
} }
fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind { fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind {
@ -1126,7 +1161,7 @@ impl InvocationCollectorNode for P<ast::Item> {
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.kind { match &self.kind {
ItemKind::DelegationMac(deleg) => Some((deleg, self)), ItemKind::DelegationMac(deleg) => Some((deleg, self)),
_ => None, _ => None,
@ -1270,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag>
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.wrapped.kind { match &self.wrapped.kind {
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
_ => None, _ => None,
@ -1311,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.wrapped.kind { match &self.wrapped.kind {
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
_ => None, _ => None,
@ -1487,7 +1522,7 @@ impl InvocationCollectorNode for ast::Stmt {
}; };
(mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No })
} }
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.kind { match &self.kind {
StmtKind::Item(item) => match &item.kind { StmtKind::Item(item) => match &item.kind {
ItemKind::DelegationMac(deleg) => Some((deleg, item)), ItemKind::DelegationMac(deleg) => Some((deleg, item)),
@ -1684,6 +1719,44 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag>
} }
} }
fn build_single_delegations<'a, Node: InvocationCollectorNode>(
ecx: &ExtCtxt<'_>,
deleg: &'a ast::DelegationMac,
item: &'a ast::Item<Node::ItemKind>,
suffixes: &'a [(Ident, Option<Ident>)],
item_span: Span,
from_glob: bool,
) -> impl Iterator<Item = ast::Item<Node::ItemKind>> + 'a {
if suffixes.is_empty() {
// Report an error for now, to avoid keeping stem for resolution and
// stability checks.
let kind = String::from(if from_glob { "glob" } else { "list" });
ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind });
}
suffixes.iter().map(move |&(ident, rename)| {
let mut path = deleg.prefix.clone();
path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None });
ast::Item {
attrs: item.attrs.clone(),
id: ast::DUMMY_NODE_ID,
span: if from_glob { item_span } else { ident.span },
vis: item.vis.clone(),
ident: rename.unwrap_or(ident),
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
id: ast::DUMMY_NODE_ID,
qself: deleg.qself.clone(),
path,
rename,
body: deleg.body.clone(),
from_glob,
})),
tokens: None,
}
})
}
struct InvocationCollector<'a, 'b> { struct InvocationCollector<'a, 'b> {
cx: &'a mut ExtCtxt<'b>, cx: &'a mut ExtCtxt<'b>,
invocations: Vec<(Invocation, Option<Lrc<SyntaxExtension>>)>, invocations: Vec<(Invocation, Option<Lrc<SyntaxExtension>>)>,
@ -1702,6 +1775,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment {
let expn_id = LocalExpnId::fresh_empty(); let expn_id = LocalExpnId::fresh_empty();
if matches!(kind, InvocationKind::GlobDelegation { .. }) {
// In resolver we need to know which invocation ids are delegations early,
// before their `ExpnData` is filled.
self.cx.resolver.register_glob_delegation(expn_id);
}
let vis = kind.placeholder_visibility(); let vis = kind.placeholder_visibility();
self.invocations.push(( self.invocations.push((
Invocation { Invocation {
@ -1734,6 +1812,14 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
self.collect(kind, InvocationKind::Attr { attr, pos, item, derives }) self.collect(kind, InvocationKind::Attr { attr, pos, item, derives })
} }
fn collect_glob_delegation(
&mut self,
item: P<ast::AssocItem>,
kind: AstFragmentKind,
) -> AstFragment {
self.collect(kind, InvocationKind::GlobDelegation { item })
}
/// If `item` is an attribute invocation, remove the attribute and return it together with /// If `item` is an attribute invocation, remove the attribute and return it together with
/// its position and derives following it. We have to collect the derives in order to resolve /// its position and derives following it. We have to collect the derives in order to resolve
/// legacy derive helpers (helpers written before derives that introduce them). /// legacy derive helpers (helpers written before derives that introduce them).
@ -1901,37 +1987,27 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
Node::post_flat_map_node_collect_bang(&mut res, add_semicolon); Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
res res
} }
None if let Some((deleg, item)) = node.delegation_list() => { None if let Some((deleg, item)) = node.delegation() => {
if deleg.suffixes.is_empty() { let Some(suffixes) = &deleg.suffixes else {
// Report an error for now, to avoid keeping stem for resolution and let item = match node.to_annotatable() {
// stability checks. Annotatable::ImplItem(item) => item,
self.cx.dcx().emit_err(EmptyDelegationList { span: item.span }); ann @ (Annotatable::Item(_)
} | Annotatable::TraitItem(_)
| Annotatable::Stmt(_)) => {
Node::flatten_outputs(deleg.suffixes.iter().map(|&(ident, rename)| { let span = ann.span();
let mut path = deleg.prefix.clone(); self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span });
path.segments.push(ast::PathSegment { return Default::default();
ident, }
id: ast::DUMMY_NODE_ID, _ => unreachable!(),
args: None, };
}); return self.collect_glob_delegation(item, Node::KIND).make_ast::<Node>();
};
let mut item = Node::from_item(ast::Item {
attrs: item.attrs.clone(),
id: ast::DUMMY_NODE_ID,
span: ident.span,
vis: item.vis.clone(),
ident: rename.unwrap_or(ident),
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
id: ast::DUMMY_NODE_ID,
qself: deleg.qself.clone(),
path,
rename,
body: deleg.body.clone(),
})),
tokens: None,
});
let single_delegations = build_single_delegations::<Node>(
self.cx, deleg, item, suffixes, item.span, false,
);
Node::flatten_outputs(single_delegations.map(|item| {
let mut item = Node::from_item(item);
assign_id!(self, item.node_id_mut(), || item.noop_flat_map(self)) assign_id!(self, item.node_id_mut(), || item.noop_flat_map(self))
})) }))
} }
@ -1983,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
self.collect_bang(mac, Node::KIND).make_ast::<Node>() self.collect_bang(mac, Node::KIND).make_ast::<Node>()
}) })
} }
None if node.delegation_list().is_some() => unreachable!(), None if node.delegation().is_some() => unreachable!(),
None => { None => {
assign_id!(self, node.node_id_mut(), || node.noop_visit(self)) assign_id!(self, node.node_id_mut(), || node.noop_visit(self))
} }

View File

@ -2699,12 +2699,13 @@ pub(crate) struct SingleColonImportPath {
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(parse_bad_item_kind)] #[diag(parse_bad_item_kind)]
#[help]
pub(crate) struct BadItemKind { pub(crate) struct BadItemKind {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
pub descr: &'static str, pub descr: &'static str,
pub ctx: &'static str, pub ctx: &'static str,
#[help]
pub help: Option<()>,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]

View File

@ -707,15 +707,25 @@ impl<'a> Parser<'a> {
}; };
let (ident, item_kind) = if self.eat(&token::PathSep) { let (ident, item_kind) = if self.eat(&token::PathSep) {
let (suffixes, _) = self.parse_delim_comma_seq(Delimiter::Brace, |p| { let suffixes = if self.eat(&token::BinOp(token::Star)) {
Ok((p.parse_path_segment_ident()?, rename(p)?)) None
})?; } else {
let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?));
Some(self.parse_delim_comma_seq(Delimiter::Brace, parse_suffix)?.0)
};
let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? }; let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? };
(Ident::empty(), ItemKind::DelegationMac(Box::new(deleg))) (Ident::empty(), ItemKind::DelegationMac(Box::new(deleg)))
} else { } else {
let rename = rename(self)?; let rename = rename(self)?;
let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident); let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, rename, body: body(self)? }; let deleg = Delegation {
id: DUMMY_NODE_ID,
qself,
path,
rename,
body: body(self)?,
from_glob: false,
};
(ident, ItemKind::Delegation(Box::new(deleg))) (ident, ItemKind::Delegation(Box::new(deleg)))
}; };
@ -1237,7 +1247,11 @@ impl<'a> Parser<'a> {
// FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`) // FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`)
let span = self.psess.source_map().guess_head_span(span); let span = self.psess.source_map().guess_head_span(span);
let descr = kind.descr(); let descr = kind.descr();
self.dcx().emit_err(errors::BadItemKind { span, descr, ctx }); let help = match kind {
ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => None,
_ => Some(()),
};
self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help });
None None
} }

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_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
use rustc_attr as attr; use rustc_attr as attr;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_expand::base::ResolverExpand;
use rustc_expand::expand::AstFragment; use rustc_expand::expand::AstFragment;
use rustc_hir::def::{self, *}; use rustc_hir::def::{self, *};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; 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); self.visit_invoc_in_module(item.id);
} }
AssocCtxt::Impl => { 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); 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); 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 { 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 parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion; let expansion = self.parent_scope.expansion;
self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, 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); 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::GlobalAsm(..) => DefKind::GlobalAsm,
ItemKind::Use(..) => return visit::walk_item(self, i), ItemKind::Use(..) => return visit::walk_item(self, i),
ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
ItemKind::DelegationMac(..) => unreachable!(), return self.visit_macro_invoc(i.id);
}
}; };
let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span); 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::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn,
AssocItemKind::Const(..) => DefKind::AssocConst, AssocItemKind::Const(..) => DefKind::AssocConst,
AssocItemKind::Type(..) => DefKind::AssocTy, AssocItemKind::Type(..) => DefKind::AssocTy,
AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
AssocItemKind::DelegationMac(..) => unreachable!(), return self.visit_macro_invoc(i.id);
}
}; };
let def = self.create_def(i.id, i.ident.name, def_kind, i.span); 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); self.visit_path(&delegation.path, delegation.id);
if let Some(body) = &delegation.body { if let Some(body) = &delegation.body {
// `PatBoundCtx` is not necessary in this context self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; // `PatBoundCtx` is not necessary in this context
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
let span = delegation.path.segments.last().unwrap().ident.span; let span = delegation.path.segments.last().unwrap().ident.span;
self.fresh_binding( this.fresh_binding(
Ident::new(kw::SelfLower, span), Ident::new(kw::SelfLower, span),
delegation.id, delegation.id,
PatternSource::FnParam, PatternSource::FnParam,
&mut bindings, &mut bindings,
); );
self.visit_block(body); this.visit_block(body);
});
} }
} }

View File

@ -1089,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> {
single_segment_macro_resolutions: single_segment_macro_resolutions:
Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>, Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
multi_segment_macro_resolutions: 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>)>, builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
/// `derive(Copy)` marks items they are applied to so they are treated specially later. /// `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 /// 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_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>, doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
all_macro_rules: FxHashMap<Symbol, Res>, 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. /// 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(), doc_link_traits_in_scope: Default::default(),
all_macro_rules: Default::default(), all_macro_rules: Default::default(),
delegation_fn_sigs: 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); 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::{self, AddAsNonDerive, CannotFindIdentInThisScope};
use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive}; use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive};
use crate::Namespace::*; 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::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc_ast::expand::StrippedCfgItem; 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); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); 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) { 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 { let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None);
InvocationKind::Attr { ref attr, ref derives, .. } => ( let (path, kind) = match invoc.kind {
&attr.get_normal_item().path, InvocationKind::Attr { ref attr, derives: ref attr_derives, .. } => {
MacroKind::Attr, derives = self.arenas.alloc_ast_paths(attr_derives);
attr.style == ast::AttrStyle::Inner, inner_attr = attr.style == ast::AttrStyle::Inner;
self.arenas.alloc_ast_paths(derives), (&attr.get_normal_item().path, MacroKind::Attr)
), }
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]), InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang),
InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]), 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. // 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, node_id,
force, force,
soft_custom_inner_attributes_gate(path, invoc), soft_custom_inner_attributes_gate(path, invoc),
deleg_impl,
)?; )?;
let span = invoc.span(); 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( invoc_id.set_expn_data(
ext.expn_data( ext.expn_data(
parent_scope.expansion, parent_scope.expansion,
@ -452,6 +464,45 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
fn registered_tools(&self) -> &RegisteredTools { fn registered_tools(&self) -> &RegisteredTools {
self.registered_tools 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> { impl<'a, 'tcx> Resolver<'a, 'tcx> {
@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
node_id: NodeId, node_id: NodeId,
force: bool, force: bool,
soft_custom_inner_attributes_gate: bool, soft_custom_inner_attributes_gate: bool,
deleg_impl: Option<LocalDefId>,
) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> { ) -> 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((Some(ext), res)) => (ext, res),
Ok((None, res)) => (self.dummy_ext(kind), res), Ok((None, res)) => (self.dummy_ext(kind), res),
Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err), Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err),
Err(Determinacy::Undetermined) => return Err(Indeterminate), 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. // Report errors for the resolved macro.
for segment in &path.segments { for segment in &path.segments {
if let Some(args) = &segment.args { if let Some(args) = &segment.args {
@ -605,12 +681,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
parent_scope: &ParentScope<'a>, parent_scope: &ParentScope<'a>,
trace: bool, trace: bool,
force: 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> { ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
let path_span = path.span; let path_span = path.span;
let mut path = Segment::from_path(path); let mut path = Segment::from_path(path);
// Possibly apply the macro helper hack // Possibly apply the macro helper hack
if kind == Some(MacroKind::Bang) if deleg_impl.is_none()
&& kind == Some(MacroKind::Bang)
&& path.len() == 1 && path.len() == 1
&& path[0].ident.span.ctxt().outer_expn_data().local_inner_macros && 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)); path.insert(0, Segment::from_ident(root));
} }
let res = if path.len() > 1 { let res = if deleg_impl.is_some() || path.len() > 1 {
let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) { 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::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res),
PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
PathResult::NonModule(..) PathResult::NonModule(..)
| PathResult::Indeterminate | PathResult::Indeterminate
| PathResult::Failed { .. } => Err(Determinacy::Determined), | PathResult::Failed { .. } => Err(Determinacy::Determined),
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
Ok(module.res().unwrap())
}
PathResult::Module(..) => unreachable!(), PathResult::Module(..) => unreachable!(),
}; };
@ -636,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
kind, kind,
*parent_scope, *parent_scope,
res.ok(), res.ok(),
ns,
)); ));
} }
@ -670,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
res 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) { 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); 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. // FIXME: Path resolution will ICE if segment IDs present.
for seg in &mut path { for seg in &mut path {
seg.id = None; seg.id = None;
} }
match self.resolve_path( match self.resolve_path(
&path, &path,
Some(MacroNS), Some(ns),
&parent_scope, &parent_scope,
Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
None, None,
@ -721,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => {
check_consistency(self, &path, path_span, kind, initial_res, 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 { .. }) => { path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => {
let mut suggestion = None; let mut suggestion = None;
let (span, label, module) = let (span, label, module) =

View File

@ -0,0 +1,32 @@
//@ check-pass
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {
fn foo(&self) {}
fn bar(&self) {}
}
impl Trait for u8 {}
struct S(u8);
mod to_import {
pub fn check(arg: &u8) -> &u8 { arg }
}
impl Trait for S {
reuse Trait::* {
use to_import::check;
let _arr = Some(self.0).map(|x| [x * 2; 3]);
check(&self.0)
}
}
fn main() {
let s = S(0);
s.foo();
s.bar();
}

View File

@ -0,0 +1,11 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {}
struct S;
impl S {
reuse Trait::*; //~ ERROR empty glob delegation is not supported
}
fn main() {}

View File

@ -0,0 +1,8 @@
error: empty glob delegation is not supported
--> $DIR/empty-glob.rs:8:5
|
LL | reuse Trait::*;
| ^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,12 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {}
struct S;
impl Trait for u8 {
reuse unresolved::*; //~ ERROR failed to resolve: use of undeclared crate or module `unresolved`
reuse S::*; //~ ERROR expected trait, found struct `S`
}
fn main() {}

View File

@ -0,0 +1,15 @@
error: expected trait, found struct `S`
--> $DIR/glob-bad-path.rs:9:11
|
LL | reuse S::*;
| ^ not a trait
error[E0433]: failed to resolve: use of undeclared crate or module `unresolved`
--> $DIR/glob-bad-path.rs:8:11
|
LL | reuse unresolved::*;
| ^^^^^^^^^^ use of undeclared crate or module `unresolved`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0433`.

View File

@ -0,0 +1,33 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait1 {
fn method(&self) -> u8;
}
trait Trait2 {
fn method(&self) -> u8;
}
trait Trait {
fn method(&self) -> u8;
}
impl Trait1 for u8 {
fn method(&self) -> u8 { 0 }
}
impl Trait1 for u16 {
fn method(&self) -> u8 { 1 }
}
impl Trait2 for u8 {
fn method(&self) -> u8 { 2 }
}
impl Trait for u8 {
reuse Trait1::*;
reuse Trait2::*; //~ ERROR duplicate definitions with name `method`
}
impl Trait for u16 {
reuse Trait1::*;
reuse Trait1::*; //~ ERROR duplicate definitions with name `method`
}
fn main() {}

View File

@ -0,0 +1,25 @@
error[E0201]: duplicate definitions with name `method`:
--> $DIR/glob-glob-conflict.rs:26:5
|
LL | fn method(&self) -> u8;
| ----------------------- item in trait
...
LL | reuse Trait1::*;
| ---------------- previous definition here
LL | reuse Trait2::*;
| ^^^^^^^^^^^^^^^^ duplicate definition
error[E0201]: duplicate definitions with name `method`:
--> $DIR/glob-glob-conflict.rs:30:5
|
LL | fn method(&self) -> u8;
| ----------------------- item in trait
...
LL | reuse Trait1::*;
| ---------------- previous definition here
LL | reuse Trait1::*;
| ^^^^^^^^^^^^^^^^ duplicate definition
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0201`.

View File

@ -0,0 +1,36 @@
//@ check-pass
#![feature(fn_delegation)]
#![allow(incomplete_features)]
mod inner {
pub trait TraitFoo {
fn foo(&self) -> u8;
}
pub trait TraitBar {
fn bar(&self) -> u8;
}
impl TraitFoo for u8 {
fn foo(&self) -> u8 { 0 }
}
impl TraitBar for u8 {
fn bar(&self) -> u8 { 1 }
}
}
trait Trait {
fn foo(&self) -> u8;
fn bar(&self) -> u8;
}
impl Trait for u8 {
reuse inner::TraitFoo::*;
reuse inner::TraitBar::*;
}
fn main() {
let u = 0u8;
u.foo();
u.bar();
}

View File

@ -0,0 +1,38 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {
fn method(&self);
const CONST: u8;
type Type;
#[allow(non_camel_case_types)]
type method;
}
impl Trait for u8 {
fn method(&self) {}
const CONST: u8 = 0;
type Type = u8;
type method = u8;
}
struct Good(u8);
impl Trait for Good {
reuse Trait::* { &self.0 }
// Explicit definitions for non-delegatable items.
const CONST: u8 = 0;
type Type = u8;
type method = u8;
}
struct Bad(u8);
impl Trait for Bad { //~ ERROR not all trait items implemented, missing: `CONST`, `Type`, `method`
reuse Trait::* { &self.0 }
//~^ ERROR item `CONST` is an associated method, which doesn't match its trait `Trait`
//~| ERROR item `Type` is an associated method, which doesn't match its trait `Trait`
//~| ERROR duplicate definitions with name `method`
//~| ERROR expected function, found associated constant `Trait::CONST`
//~| ERROR expected function, found associated type `Trait::Type`
}
fn main() {}

View File

@ -0,0 +1,62 @@
error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait`
--> $DIR/glob-non-fn.rs:30:5
|
LL | const CONST: u8;
| ---------------- item in trait
...
LL | reuse Trait::* { &self.0 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait
error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait`
--> $DIR/glob-non-fn.rs:30:5
|
LL | type Type;
| ---------- item in trait
...
LL | reuse Trait::* { &self.0 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait
error[E0201]: duplicate definitions with name `method`:
--> $DIR/glob-non-fn.rs:30:5
|
LL | fn method(&self);
| ----------------- item in trait
...
LL | reuse Trait::* { &self.0 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| duplicate definition
| previous definition here
error[E0423]: expected function, found associated constant `Trait::CONST`
--> $DIR/glob-non-fn.rs:30:11
|
LL | reuse Trait::* { &self.0 }
| ^^^^^ not a function
error[E0423]: expected function, found associated type `Trait::Type`
--> $DIR/glob-non-fn.rs:30:11
|
LL | reuse Trait::* { &self.0 }
| ^^^^^
|
= note: can't use a type alias as a constructor
error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method`
--> $DIR/glob-non-fn.rs:29:1
|
LL | const CONST: u8;
| --------------- `CONST` from trait
LL | type Type;
| --------- `Type` from trait
LL | #[allow(non_camel_case_types)]
LL | type method;
| ----------- `method` from trait
...
LL | impl Trait for Bad {
| ^^^^^^^^^^^^^^^^^^ missing `CONST`, `Type`, `method` in implementation
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0046, E0201, E0324, E0423.
For more information about an error, try `rustc --explain E0046`.

View File

@ -0,0 +1,20 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {
fn method() {}
}
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
trait OtherTrait {
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
}
extern {
reuse Trait::*; //~ ERROR delegation is not supported in `extern` blocks
}
fn main() {
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
}

View File

@ -0,0 +1,26 @@
error: delegation is not supported in `extern` blocks
--> $DIR/glob-non-impl.rs:15:5
|
LL | reuse Trait::*;
| ^^^^^^^^^^^^^^^
error: glob delegation is only supported in impls
--> $DIR/glob-non-impl.rs:8:1
|
LL | reuse Trait::*;
| ^^^^^^^^^^^^^^^
error: glob delegation is only supported in impls
--> $DIR/glob-non-impl.rs:11:5
|
LL | reuse Trait::*;
| ^^^^^^^^^^^^^^^
error: glob delegation is only supported in impls
--> $DIR/glob-non-impl.rs:19:5
|
LL | reuse Trait::*;
| ^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors

View File

@ -0,0 +1,37 @@
//@ check-pass
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {
fn foo(&self) -> u8;
fn bar(&self) -> u8;
}
impl Trait for u8 {
fn foo(&self) -> u8 { 0 }
fn bar(&self) -> u8 { 1 }
}
struct S(u8);
struct Z(u8);
impl Trait for S {
reuse Trait::* { &self.0 }
fn bar(&self) -> u8 { 2 }
}
impl Trait for Z {
reuse Trait::* { &self.0 }
reuse Trait::bar { &self.0 }
}
fn main() {
let s = S(2);
s.foo();
s.bar();
let z = Z(2);
z.foo();
z.bar();
}

View File

@ -0,0 +1,35 @@
//@ check-pass
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {
fn foo(&self) -> u8;
fn bar(&self) -> u8;
}
impl Trait for u8 {
fn foo(&self) -> u8 { 0 }
fn bar(&self) -> u8 { 1 }
}
struct S(u8);
struct Z(u8);
impl Trait for S {
reuse Trait::* { &self.0 }
}
impl Trait for Z {
reuse <u8 as Trait>::* { &self.0 }
}
fn main() {
let s = S(2);
s.foo();
s.bar();
let z = Z(3);
z.foo();
z.bar();
}

View File

@ -0,0 +1,26 @@
//@ check-pass
#![feature(fn_delegation)]
#![allow(incomplete_features)]
trait Trait {
fn foo(&self) -> u8 { 0 }
fn bar(&self) -> u8 { 1 }
}
impl Trait for u8 {}
struct S(u8);
// Macro expansion works inside delegation items.
macro_rules! u8 { () => { u8 } }
macro_rules! self_0 { ($self:ident) => { &$self.0 } }
impl Trait for S {
reuse <u8!() as Trait>::* { self_0!(self) }
}
fn main() {
let s = S(2);
s.foo();
s.bar();
}

View File

@ -14,9 +14,9 @@ struct S(u8);
// Macro expansion works inside delegation items. // Macro expansion works inside delegation items.
macro_rules! u8 { () => { u8 } } macro_rules! u8 { () => { u8 } }
macro_rules! self_0 { () => { &self.0 } } macro_rules! self_0 { ($self:ident) => { &$self.0 } }
impl Trait for S { impl Trait for S {
reuse <u8!() as Trait>::{foo, bar} { self_0!() } reuse <u8!() as Trait>::{foo, bar} { self_0!(self) }
} }
fn main() { fn main() {