From 22d0b1ee18520ba32d3bc41ad3e85d91a5c73c24 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 15 Mar 2024 14:21:03 +0300 Subject: [PATCH] delegation: Implement glob delegation --- compiler/rustc_ast/src/ast.rs | 9 +- compiler/rustc_ast/src/mut_visit.rs | 38 +++-- compiler/rustc_ast/src/visit.rs | 38 +++-- .../rustc_ast_pretty/src/pprust/state/item.rs | 51 +++--- compiler/rustc_expand/messages.ftl | 7 +- compiler/rustc_expand/src/base.rs | 47 +++++- compiler/rustc_expand/src/errors.rs | 12 +- compiler/rustc_expand/src/expand.rs | 152 +++++++++++++----- compiler/rustc_parse/src/errors.rs | 3 +- compiler/rustc_parse/src/parser/item.rs | 24 ++- .../rustc_resolve/src/build_reduced_graph.rs | 28 +++- compiler/rustc_resolve/src/def_collector.rs | 10 +- compiler/rustc_resolve/src/late.rs | 22 +-- compiler/rustc_resolve/src/lib.rs | 14 +- compiler/rustc_resolve/src/macros.rs | 152 +++++++++++++++--- tests/ui/delegation/body-identity-glob.rs | 32 ++++ tests/ui/delegation/empty-glob.rs | 11 ++ tests/ui/delegation/empty-glob.stderr | 8 + tests/ui/delegation/glob-bad-path.rs | 12 ++ tests/ui/delegation/glob-bad-path.stderr | 15 ++ tests/ui/delegation/glob-glob-conflict.rs | 33 ++++ tests/ui/delegation/glob-glob-conflict.stderr | 25 +++ tests/ui/delegation/glob-glob.rs | 36 +++++ tests/ui/delegation/glob-non-fn.rs | 38 +++++ tests/ui/delegation/glob-non-fn.stderr | 62 +++++++ tests/ui/delegation/glob-non-impl.rs | 20 +++ tests/ui/delegation/glob-non-impl.stderr | 26 +++ tests/ui/delegation/glob-override.rs | 37 +++++ tests/ui/delegation/glob.rs | 35 ++++ tests/ui/delegation/macro-inside-glob.rs | 26 +++ tests/ui/delegation/macro-inside-list.rs | 4 +- 31 files changed, 892 insertions(+), 135 deletions(-) create mode 100644 tests/ui/delegation/body-identity-glob.rs create mode 100644 tests/ui/delegation/empty-glob.rs create mode 100644 tests/ui/delegation/empty-glob.stderr create mode 100644 tests/ui/delegation/glob-bad-path.rs create mode 100644 tests/ui/delegation/glob-bad-path.stderr create mode 100644 tests/ui/delegation/glob-glob-conflict.rs create mode 100644 tests/ui/delegation/glob-glob-conflict.stderr create mode 100644 tests/ui/delegation/glob-glob.rs create mode 100644 tests/ui/delegation/glob-non-fn.rs create mode 100644 tests/ui/delegation/glob-non-fn.stderr create mode 100644 tests/ui/delegation/glob-non-impl.rs create mode 100644 tests/ui/delegation/glob-non-impl.stderr create mode 100644 tests/ui/delegation/glob-override.rs create mode 100644 tests/ui/delegation/glob.rs create mode 100644 tests/ui/delegation/macro-inside-glob.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 9cb193b4a67..b9b8d0f67ee 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3158,13 +3158,16 @@ pub struct Delegation { pub path: Path, pub rename: Option, pub body: Option>, + /// The item was expanded from a glob delegation item. + pub from_glob: bool, } #[derive(Clone, Encodable, Decodable, Debug)] pub struct DelegationMac { pub qself: Option>, pub prefix: Path, - pub suffixes: ThinVec<(Ident, Option)>, + // Some for list delegation, and None for glob delegation. + pub suffixes: Option)>>, pub body: Option>, } @@ -3291,7 +3294,7 @@ pub enum ItemKind { /// /// E.g. `reuse ::name { target_expr_template }`. Delegation(Box), - /// 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. DelegationMac(Box), } @@ -3372,7 +3375,7 @@ pub enum AssocItemKind { MacCall(P), /// An associated delegation item. Delegation(Box), - /// An associated delegation item list. + /// An associated list or glob delegation item. DelegationMac(Box), } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index cc33ce2cb56..8497be53c06 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1162,7 +1162,14 @@ impl NoopVisitItemKind for ItemKind { } ItemKind::MacCall(m) => vis.visit_mac_call(m), 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_qself(qself); vis.visit_path(path); @@ -1176,10 +1183,12 @@ impl NoopVisitItemKind for ItemKind { ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { vis.visit_qself(qself); vis.visit_path(prefix); - for (ident, rename) in suffixes { - vis.visit_ident(ident); - if let Some(rename) = rename { - vis.visit_ident(rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + vis.visit_ident(ident); + if let Some(rename) = rename { + vis.visit_ident(rename); + } } } if let Some(body) = body { @@ -1218,7 +1227,14 @@ impl NoopVisitItemKind for AssocItemKind { visit_opt(ty, |ty| visitor.visit_ty(ty)); } 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_qself(qself); visitor.visit_path(path); @@ -1232,10 +1248,12 @@ impl NoopVisitItemKind for AssocItemKind { AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { visitor.visit_qself(qself); visitor.visit_path(prefix); - for (ident, rename) in suffixes { - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(ident); + if let Some(rename) = rename { + visitor.visit_ident(rename); + } } } if let Some(body) = body { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fa97c8db326..30ab852c4aa 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -398,7 +398,14 @@ impl WalkItemKind for ItemKind { } ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)), 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 { 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_path(prefix, item.id)); - for (ident, rename) in suffixes { - visitor.visit_ident(*ident); - if let Some(rename) = rename { - visitor.visit_ident(*rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(*ident); + if let Some(rename) = rename { + visitor.visit_ident(*rename); + } } } visit_opt!(visitor, visit_block, body); @@ -828,7 +837,14 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::MacCall(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 { 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_path(prefix, item.id)); - for (ident, rename) in suffixes { - visitor.visit_ident(*ident); - if let Some(rename) = rename { - visitor.visit_ident(*rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(*ident); + if let Some(rename) = rename { + visitor.visit_ident(*rename); + } } } visit_opt!(visitor, visit_block, body); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 474741fb067..49ac5ece337 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -9,6 +9,12 @@ use rustc_ast::ptr::P; use rustc_ast::ModKind; use rustc_span::symbol::Ident; +enum DelegationKind<'a> { + Single, + List(&'a [(Ident, Option)]), + Glob, +} + fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) } @@ -387,7 +393,7 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.path, - None, + DelegationKind::Single, &deleg.body, ), ast::ItemKind::DelegationMac(deleg) => self.print_delegation( @@ -395,7 +401,7 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.prefix, - Some(&deleg.suffixes), + deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), &deleg.body, ), } @@ -579,7 +585,7 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.path, - None, + DelegationKind::Single, &deleg.body, ), ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation( @@ -587,20 +593,20 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.prefix, - Some(&deleg.suffixes), + deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), &deleg.body, ), } self.ann.post(self, AnnNode::SubItem(id)) } - pub(crate) fn print_delegation( + fn print_delegation( &mut self, attrs: &[ast::Attribute], vis: &ast::Visibility, qself: &Option>, path: &ast::Path, - suffixes: Option<&[(Ident, Option)]>, + kind: DelegationKind<'_>, body: &Option>, ) { if body.is_some() { @@ -614,21 +620,28 @@ impl<'a> State<'a> { } else { self.print_path(path, false, 0); } - if let Some(suffixes) = suffixes { - self.word("::"); - self.word("{"); - for (i, (ident, rename)) in suffixes.iter().enumerate() { - self.print_ident(*ident); - if let Some(rename) = rename { - self.nbsp(); - self.word_nbsp("as"); - self.print_ident(*rename); - } - if i != suffixes.len() - 1 { - self.word_space(","); + match kind { + DelegationKind::Single => {} + DelegationKind::List(suffixes) => { + self.word("::"); + self.word("{"); + for (i, (ident, rename)) in suffixes.iter().enumerate() { + self.print_ident(*ident); + if let Some(rename) = rename { + self.nbsp(); + self.word_nbsp("as"); + self.print_ident(*rename); + } + if i != suffixes.len() - 1 { + self.word_space(","); + } } + self.word("}"); + } + DelegationKind::Glob => { + self.word("::"); + self.word("*"); } - self.word("}"); } if let Some(body) = body { self.nbsp(); diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 2e150f7bb27..6113580491e 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -35,8 +35,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding .label = duplicate binding .label2 = previous binding -expand_empty_delegation_list = - empty list delegation is not supported +expand_empty_delegation_mac = + empty {$kind} delegation is not supported expand_expected_paren_or_brace = expected `(` or `{"{"}`, found `{$token}` @@ -58,6 +58,9 @@ expand_feature_removed = .label = feature has been removed .reason = {$reason} +expand_glob_delegation_outside_impls = + glob delegation is only supported in impls + expand_helper_attribute_name_invalid = `{$name}` cannot be a name of derive helper attribute diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b3f6a35f3a4..aab493b9db5 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -357,6 +357,10 @@ where } } +pub trait GlobDelegationExpander { + fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult)>, ()>; +} + // Use a macro because forwarding to a simple function has type system issues macro_rules! make_stmts_default { ($me:expr) => { @@ -714,6 +718,9 @@ pub enum SyntaxExtensionKind { /// The produced AST fragment is appended to the input AST fragment. Box, ), + + /// A glob delegation. + GlobDelegation(Box), } /// 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. pub fn macro_kind(&self) -> MacroKind { match self.kind { - SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang, + SyntaxExtensionKind::Bang(..) + | SyntaxExtensionKind::LegacyBang(..) + | SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang, SyntaxExtensionKind::Attr(..) | SyntaxExtensionKind::LegacyAttr(..) | SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr, @@ -922,6 +931,32 @@ impl SyntaxExtension { 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)>, ()> { + 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( &self, parent: LocalExpnId, @@ -1030,6 +1065,16 @@ pub trait ResolverExpand { /// Tools registered with `#![register_tool]` and used by tool attributes and lints. 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)>, Indeterminate>; } pub trait LintStoreExpand { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 3f8b4661e5f..c883121fb40 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -435,8 +435,16 @@ pub struct ExpectedParenOrBrace<'a> { } #[derive(Diagnostic)] -#[diag(expand_empty_delegation_list)] -pub(crate) struct EmptyDelegationList { +#[diag(expand_empty_delegation_mac)] +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] pub span: Span, } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c28a09eb57c..716bfc8c26b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,8 +1,8 @@ use crate::base::*; use crate::config::StripUnconfigured; use crate::errors::{ - EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, - RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, + EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached, + RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, }; use crate::mbe::diagnostics::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -343,6 +343,9 @@ pub enum InvocationKind { is_const: bool, item: Annotatable, }, + GlobDelegation { + item: P, + }, } impl InvocationKind { @@ -370,6 +373,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => *span, InvocationKind::Attr { attr, .. } => attr.span, InvocationKind::Derive { path, .. } => path.span, + InvocationKind::GlobDelegation { item } => item.span, } } @@ -378,6 +382,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { attr, .. } => &mut attr.span, InvocationKind::Derive { path, .. } => &mut path.span, + InvocationKind::GlobDelegation { item } => &mut item.span, } } } @@ -800,6 +805,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => 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, ImplItemTag>; + let single_delegations = build_single_delegations::( + 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, Self::AttrsTy, AddSemicolon) { unreachable!() } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { None } fn delegation_item_kind(_deleg: Box) -> Self::ItemKind { @@ -1126,7 +1161,7 @@ impl InvocationCollectorNode for P { _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.kind { ItemKind::DelegationMac(deleg) => Some((deleg, self)), _ => None, @@ -1270,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTag> _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.wrapped.kind { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1311,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.wrapped.kind { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1487,7 +1522,7 @@ impl InvocationCollectorNode for ast::Stmt { }; (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.kind { StmtKind::Item(item) => match &item.kind { ItemKind::DelegationMac(deleg) => Some((deleg, item)), @@ -1684,6 +1719,44 @@ impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> } } +fn build_single_delegations<'a, Node: InvocationCollectorNode>( + ecx: &ExtCtxt<'_>, + deleg: &'a ast::DelegationMac, + item: &'a ast::Item, + suffixes: &'a [(Ident, Option)], + item_span: Span, + from_glob: bool, +) -> impl Iterator> + '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> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -1702,6 +1775,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { 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(); self.invocations.push(( Invocation { @@ -1734,6 +1812,14 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect(kind, InvocationKind::Attr { attr, pos, item, derives }) } + fn collect_glob_delegation( + &mut self, + item: P, + kind: AstFragmentKind, + ) -> AstFragment { + self.collect(kind, InvocationKind::GlobDelegation { item }) + } + /// 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 /// 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); res } - None if let Some((deleg, item)) = node.delegation_list() => { - if deleg.suffixes.is_empty() { - // Report an error for now, to avoid keeping stem for resolution and - // stability checks. - self.cx.dcx().emit_err(EmptyDelegationList { span: item.span }); - } - - Node::flatten_outputs(deleg.suffixes.iter().map(|&(ident, rename)| { - let mut path = deleg.prefix.clone(); - path.segments.push(ast::PathSegment { - ident, - id: ast::DUMMY_NODE_ID, - args: None, - }); - - 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, - }); + None if let Some((deleg, item)) = node.delegation() => { + let Some(suffixes) = &deleg.suffixes else { + let item = match node.to_annotatable() { + Annotatable::ImplItem(item) => item, + ann @ (Annotatable::Item(_) + | Annotatable::TraitItem(_) + | Annotatable::Stmt(_)) => { + let span = ann.span(); + self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span }); + return Default::default(); + } + _ => unreachable!(), + }; + return self.collect_glob_delegation(item, Node::KIND).make_ast::(); + }; + let single_delegations = build_single_delegations::( + 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)) })) } @@ -1983,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect_bang(mac, Node::KIND).make_ast::() }) } - None if node.delegation_list().is_some() => unreachable!(), + None if node.delegation().is_some() => unreachable!(), None => { assign_id!(self, node.node_id_mut(), || node.noop_visit(self)) } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6c1fcbe06fc..314f3f17dab 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2699,12 +2699,13 @@ pub(crate) struct SingleColonImportPath { #[derive(Diagnostic)] #[diag(parse_bad_item_kind)] -#[help] pub(crate) struct BadItemKind { #[primary_span] pub span: Span, pub descr: &'static str, pub ctx: &'static str, + #[help] + pub help: Option<()>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 3f5a4afdad8..9df1431f657 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -707,15 +707,25 @@ impl<'a> Parser<'a> { }; let (ident, item_kind) = if self.eat(&token::PathSep) { - let (suffixes, _) = self.parse_delim_comma_seq(Delimiter::Brace, |p| { - Ok((p.parse_path_segment_ident()?, rename(p)?)) - })?; + let suffixes = if self.eat(&token::BinOp(token::Star)) { + 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)? }; (Ident::empty(), ItemKind::DelegationMac(Box::new(deleg))) } else { let rename = rename(self)?; 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))) }; @@ -1237,7 +1247,11 @@ impl<'a> Parser<'a> { // FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`) let span = self.psess.source_map().guess_head_span(span); 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 } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 689109b2840..e035749fc39 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -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); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index ca97d10617b..fb6e55f2b7b 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -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); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b0adc3775f6..fa711d932b6 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -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); + }); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9557b0f5ebc..3a831a7f19e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1089,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> { single_segment_macro_resolutions: Vec<(Ident, MacroKind, ParentScope<'a>, Option>)>, multi_segment_macro_resolutions: - Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option)>, + Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option, 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, doc_link_traits_in_scope: FxHashMap>, all_macro_rules: FxHashMap, + + /// Invocation ids of all glob delegations. + glob_delegation_invoc_ids: FxHashSet, + /// 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>, + /// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations. + /// Needed because glob delegations exclude explicitly defined names. + impl_binding_keys: FxHashMap>, } /// 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); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 268e7f06d04..87794d11cea 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -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)>, 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, ) -> Result<(Lrc, 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>, 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, + parent_scope: &ParentScope<'a>, + trace: bool, + force: bool, + deleg_impl: Option, ) -> Result<(Option>, 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) = diff --git a/tests/ui/delegation/body-identity-glob.rs b/tests/ui/delegation/body-identity-glob.rs new file mode 100644 index 00000000000..58b644f46d6 --- /dev/null +++ b/tests/ui/delegation/body-identity-glob.rs @@ -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(); +} diff --git a/tests/ui/delegation/empty-glob.rs b/tests/ui/delegation/empty-glob.rs new file mode 100644 index 00000000000..d98579d8972 --- /dev/null +++ b/tests/ui/delegation/empty-glob.rs @@ -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() {} diff --git a/tests/ui/delegation/empty-glob.stderr b/tests/ui/delegation/empty-glob.stderr new file mode 100644 index 00000000000..f4d282f6a0f --- /dev/null +++ b/tests/ui/delegation/empty-glob.stderr @@ -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 + diff --git a/tests/ui/delegation/glob-bad-path.rs b/tests/ui/delegation/glob-bad-path.rs new file mode 100644 index 00000000000..7bc4f0153a3 --- /dev/null +++ b/tests/ui/delegation/glob-bad-path.rs @@ -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() {} diff --git a/tests/ui/delegation/glob-bad-path.stderr b/tests/ui/delegation/glob-bad-path.stderr new file mode 100644 index 00000000000..0c06364b3f0 --- /dev/null +++ b/tests/ui/delegation/glob-bad-path.stderr @@ -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`. diff --git a/tests/ui/delegation/glob-glob-conflict.rs b/tests/ui/delegation/glob-glob-conflict.rs new file mode 100644 index 00000000000..2843bf8c493 --- /dev/null +++ b/tests/ui/delegation/glob-glob-conflict.rs @@ -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() {} diff --git a/tests/ui/delegation/glob-glob-conflict.stderr b/tests/ui/delegation/glob-glob-conflict.stderr new file mode 100644 index 00000000000..8c7e5a4b023 --- /dev/null +++ b/tests/ui/delegation/glob-glob-conflict.stderr @@ -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`. diff --git a/tests/ui/delegation/glob-glob.rs b/tests/ui/delegation/glob-glob.rs new file mode 100644 index 00000000000..ef7f9a15e19 --- /dev/null +++ b/tests/ui/delegation/glob-glob.rs @@ -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(); +} diff --git a/tests/ui/delegation/glob-non-fn.rs b/tests/ui/delegation/glob-non-fn.rs new file mode 100644 index 00000000000..ab312d51f49 --- /dev/null +++ b/tests/ui/delegation/glob-non-fn.rs @@ -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() {} diff --git a/tests/ui/delegation/glob-non-fn.stderr b/tests/ui/delegation/glob-non-fn.stderr new file mode 100644 index 00000000000..4b918c53b84 --- /dev/null +++ b/tests/ui/delegation/glob-non-fn.stderr @@ -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`. diff --git a/tests/ui/delegation/glob-non-impl.rs b/tests/ui/delegation/glob-non-impl.rs new file mode 100644 index 00000000000..d523731eeb3 --- /dev/null +++ b/tests/ui/delegation/glob-non-impl.rs @@ -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 +} diff --git a/tests/ui/delegation/glob-non-impl.stderr b/tests/ui/delegation/glob-non-impl.stderr new file mode 100644 index 00000000000..ea458fd5e90 --- /dev/null +++ b/tests/ui/delegation/glob-non-impl.stderr @@ -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 + diff --git a/tests/ui/delegation/glob-override.rs b/tests/ui/delegation/glob-override.rs new file mode 100644 index 00000000000..1d0dcf1df6e --- /dev/null +++ b/tests/ui/delegation/glob-override.rs @@ -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(); +} diff --git a/tests/ui/delegation/glob.rs b/tests/ui/delegation/glob.rs new file mode 100644 index 00000000000..5bc80c16648 --- /dev/null +++ b/tests/ui/delegation/glob.rs @@ -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 ::* { &self.0 } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(3); + z.foo(); + z.bar(); +} diff --git a/tests/ui/delegation/macro-inside-glob.rs b/tests/ui/delegation/macro-inside-glob.rs new file mode 100644 index 00000000000..1d529341c5b --- /dev/null +++ b/tests/ui/delegation/macro-inside-glob.rs @@ -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 ::* { self_0!(self) } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); +} diff --git a/tests/ui/delegation/macro-inside-list.rs b/tests/ui/delegation/macro-inside-list.rs index 16c74d396ef..d07a4e47dd4 100644 --- a/tests/ui/delegation/macro-inside-list.rs +++ b/tests/ui/delegation/macro-inside-list.rs @@ -14,9 +14,9 @@ struct S(u8); // Macro expansion works inside delegation items. macro_rules! u8 { () => { u8 } } -macro_rules! self_0 { () => { &self.0 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } impl Trait for S { - reuse ::{foo, bar} { self_0!() } + reuse ::{foo, bar} { self_0!(self) } } fn main() {