diff options
| author | 许杰友 Jieyou Xu (Joe) <39484203+jieyouxu@users.noreply.github.com> | 2024-06-19 01:51:36 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-19 01:51:36 +0100 |
| commit | f8ce1cfbf5faaaa2456aed6eef6a9c9651df1985 (patch) | |
| tree | 436863dd8808976484c99d4d727429669061c4bd | |
| parent | 8fcd4dd08e2ba3e922d917d819ba0be066bdb005 (diff) | |
| parent | 22d0b1ee18520ba32d3bc41ad3e85d91a5c73c24 (diff) | |
| download | rust-f8ce1cfbf5faaaa2456aed6eef6a9c9651df1985.tar.gz rust-f8ce1cfbf5faaaa2456aed6eef6a9c9651df1985.zip | |
Rollup merge of #124135 - petrochenkov:deleglob, r=fmease
delegation: Implement glob delegation Support delegating to all trait methods in one go. Overriding globs with explicit definitions is also supported. The implementation is generally based on the design from https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2020869823, but unlike with list delegation in https://github.com/rust-lang/rust/pull/123413 we cannot expand glob delegation eagerly. We have to enqueue it into the queue of unexpanded macros (most other macros are processed this way too), and then a glob delegation waits in that queue until its trait path is resolved, and enough code expands to generate the identifier list produced from the glob. Glob delegation is only allowed in impls, and can only point to traits. Supporting it in other places gives very little practical benefit, but significantly raises the implementation complexity. Part of https://github.com/rust-lang/rust/issues/118212.
29 files changed, 878 insertions, 123 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fe888d2004f..7f766e63e06 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3161,13 +3161,16 @@ pub struct Delegation { pub path: Path, pub rename: Option<Ident>, pub body: Option<P<Block>>, + /// The item was expanded from a glob delegation item. + pub from_glob: bool, } #[derive(Clone, Encodable, Decodable, Debug)] pub struct DelegationMac { pub qself: Option<P<QSelf>>, 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>>, } @@ -3294,7 +3297,7 @@ pub enum ItemKind { /// /// E.g. `reuse <Type as Trait>::name { target_expr_template }`. 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. DelegationMac(Box<DelegationMac>), } @@ -3375,7 +3378,7 @@ pub enum AssocItemKind { MacCall(P<MacCall>), /// An associated delegation item. Delegation(Box<Delegation>), - /// An associated delegation item list. + /// An associated list or glob delegation item. DelegationMac(Box<DelegationMac>), } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 182ad7359af..35aa53e978c 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 104a401cd53..ed34a44db67 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -408,7 +408,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)); } @@ -421,10 +428,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); @@ -837,7 +846,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)); } @@ -850,10 +866,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<Ident>)]), + 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<P<ast::QSelf>>, path: &ast::Path, - suffixes: Option<&[(Ident, Option<Ident>)]>, + kind: DelegationKind<'_>, body: &Option<P<ast::Block>>, ) { 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 ddc6490ac0c..d8e6d3525da 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<Vec<(Ident, Option<Ident>)>, ()>; +} + // 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<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. @@ -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<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( &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<Vec<(Ident, Option<Ident>)>, 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<ast::AssocItem>, + }, } 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<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) { unreachable!() } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { None } fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind { @@ -1126,7 +1161,7 @@ impl InvocationCollectorNode for P<ast::Item> { _ => 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 { ItemKind::DelegationMac(deleg) => Some((deleg, self)), _ => None, @@ -1270,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag> _ => 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 { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1311,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag> _ => 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 { 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<Self::ItemKind>)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { match &self.kind { StmtKind::Item(item) => match &item.kind { 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> { cx: &'a mut ExtCtxt<'b>, 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 { 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<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 /// 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::<Node>(); + }; + 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)) })) } @@ -1983,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { 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 => { 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 7566a4d5066..936fc9d6b77 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 42f8c6e38b9..2db777a9f70 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/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<NameBinding<'a>>)>, multi_segment_macro_resolutions: - Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>, + Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>, Namespace)>, builtin_attrs: Vec<(Ident, ParentScope<'a>)>, /// `derive(Copy)` marks items they are applied to so they are treated specially later. /// Derive macros cannot modify the item themselves and have to store the markers in the global @@ -1163,6 +1163,15 @@ pub struct Resolver<'a, 'tcx> { doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>, doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>, all_macro_rules: FxHashMap<Symbol, Res>, + + /// Invocation ids of all glob delegations. + glob_delegation_invoc_ids: FxHashSet<LocalExpnId>, + /// Analogue of module `unexpanded_invocations` but in trait impls, excluding glob delegations. + /// Needed because glob delegations wait for all other neighboring macros to expand. + impl_unexpanded_invocations: FxHashMap<LocalDefId, FxHashSet<LocalExpnId>>, + /// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations. + /// Needed because glob delegations exclude explicitly defined names. + impl_binding_keys: FxHashMap<LocalDefId, FxHashSet<BindingKey>>, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1504,6 +1513,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { doc_link_traits_in_scope: Default::default(), all_macro_rules: Default::default(), delegation_fn_sigs: Default::default(), + glob_delegation_invoc_ids: Default::default(), + impl_unexpanded_invocations: Default::default(), + impl_binding_keys: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); 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<Vec<(Ident, Option<Ident>)>, Indeterminate> { + let target_trait = self.expect_module(trait_def_id); + if !target_trait.unexpanded_invocations.borrow().is_empty() { + return Err(Indeterminate); + } + // FIXME: Instead of waiting try generating all trait methods, and pruning + // the shadowed ones a bit later, e.g. when all macro expansion completes. + // Pros: expansion will be stuck less (but only in exotic cases), the implementation may be + // less hacky. + // Cons: More code is generated just to be deleted later, deleting already created `DefId`s + // may be nontrivial. + if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get(&impl_def_id) + && !unexpanded_invocations.is_empty() + { + return Err(Indeterminate); + } + + let mut idents = Vec::new(); + target_trait.for_each_child(self, |this, ident, ns, _binding| { + // FIXME: Adjust hygiene for idents from globs, like for glob imports. + if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) + && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns)) + { + // The name is overridden, do not produce it from the glob delegation. + } else { + idents.push((ident, None)); + } + }); + Ok(idents) + } } impl<'a, 'tcx> Resolver<'a, 'tcx> { @@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { node_id: NodeId, force: bool, soft_custom_inner_attributes_gate: bool, + deleg_impl: Option<LocalDefId>, ) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> { - let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force) - { + let (ext, res) = match self.resolve_macro_or_delegation_path( + path, + Some(kind), + parent_scope, + true, + force, + deleg_impl, + ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err), Err(Determinacy::Undetermined) => return Err(Indeterminate), }; + // Everything below is irrelevant to glob delegation, take a shortcut. + if deleg_impl.is_some() { + if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) { + self.dcx().emit_err(MacroExpectedFound { + span: path.span, + expected: "trait", + article: "a", + found: res.descr(), + macro_path: &pprust::path_to_string(path), + remove_surrounding_derive: None, + add_as_non_derive: None, + }); + return Ok((self.dummy_ext(kind), Res::Err)); + } + + return Ok((ext, res)); + } + // Report errors for the resolved macro. for segment in &path.segments { if let Some(args) = &segment.args { @@ -606,11 +682,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { trace: bool, force: bool, ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> { + self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None) + } + + fn resolve_macro_or_delegation_path( + &mut self, + path: &ast::Path, + kind: Option<MacroKind>, + parent_scope: &ParentScope<'a>, + trace: bool, + force: bool, + deleg_impl: Option<LocalDefId>, + ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> { let path_span = path.span; let mut path = Segment::from_path(path); // Possibly apply the macro helper hack - if kind == Some(MacroKind::Bang) + if deleg_impl.is_none() + && kind == Some(MacroKind::Bang) && path.len() == 1 && path[0].ident.span.ctxt().outer_expn_data().local_inner_macros { @@ -618,13 +707,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path.insert(0, Segment::from_ident(root)); } - let res = if path.len() > 1 { - let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) { + let res = if deleg_impl.is_some() || path.len() > 1 { + let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS }; + let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) | PathResult::Indeterminate | PathResult::Failed { .. } => Err(Determinacy::Determined), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => { + Ok(module.res().unwrap()) + } PathResult::Module(..) => unreachable!(), }; @@ -636,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind, *parent_scope, res.ok(), + ns, )); } @@ -670,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { res }; - res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res)) + let res = res?; + let ext = match deleg_impl { + Some(impl_def_id) => match res { + def::Res::Def(DefKind::Trait, def_id) => { + let edition = self.tcx.sess.edition(); + Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + } + _ => None, + }, + None => self.get_macro(res).map(|macro_data| macro_data.ext.clone()), + }; + Ok((ext, res)) } pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { @@ -706,14 +811,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions); - for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions { + for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions { // FIXME: Path resolution will ICE if segment IDs present. for seg in &mut path { seg.id = None; } match self.resolve_path( &path, - Some(MacroNS), + Some(ns), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, @@ -721,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { check_consistency(self, &path, path_span, kind, initial_res, res) } + // This may be a trait for glob delegation expansions. + PathResult::Module(ModuleOrUniformRoot::Module(module)) => check_consistency( + self, + &path, + path_span, + kind, + initial_res, + module.res().unwrap(), + ), path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; let (span, label, module) = 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 <u8 as Trait>::* { &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 <u8!() as Trait>::* { self_0!(self) } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); +} |
