Skip to content

Commit

Permalink
delegation: Implement multi-item delegation
Browse files Browse the repository at this point in the history
```rust
reuse prefix::{a, b, c}
```
  • Loading branch information
petrochenkov committed Apr 3, 2024
1 parent 99c42d2 commit 886182d
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 16 deletions.
7 changes: 7 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3081,12 +3081,19 @@ pub struct Fn {
pub body: Option<P<Block>>,
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub enum DelegationKind {
Single,
List(ThinVec<Ident>),
}

#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Delegation {
/// Path resolution id.
pub id: NodeId,
pub qself: Option<P<QSelf>>,
pub path: Path,
pub kind: DelegationKind,
pub body: Option<P<Block>>,
}

Expand Down
20 changes: 18 additions & 2 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1124,10 +1124,18 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
}
ItemKind::MacCall(m) => vis.visit_mac_call(m),
ItemKind::MacroDef(def) => vis.visit_macro_def(def),
ItemKind::Delegation(box Delegation { id, qself, path, body }) => {
ItemKind::Delegation(box Delegation { id, qself, path, kind, body }) => {
vis.visit_id(id);
vis.visit_qself(qself);
vis.visit_path(path);
match kind {
DelegationKind::Single => {}
DelegationKind::List(suffixes) => {
for ident in suffixes {
vis.visit_ident(ident);
}
}
}
if let Some(body) = body {
vis.visit_block(body);
}
Expand Down Expand Up @@ -1170,10 +1178,18 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
visit_opt(ty, |ty| visitor.visit_ty(ty));
}
AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
AssocItemKind::Delegation(box Delegation { id, qself, path, body }) => {
AssocItemKind::Delegation(box Delegation { id, qself, path, kind, body }) => {
visitor.visit_id(id);
visitor.visit_qself(qself);
visitor.visit_path(path);
match kind {
DelegationKind::Single => {}
DelegationKind::List(suffixes) => {
for ident in suffixes {
visitor.visit_ident(ident);
}
}
}
if let Some(body) = body {
visitor.visit_block(body);
}
Expand Down
20 changes: 18 additions & 2 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,19 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) -> V::Resu
}
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, body }) => {
ItemKind::Delegation(box Delegation { id, qself, path, kind, body }) => {
if let Some(qself) = qself {
try_visit!(visitor.visit_ty(&qself.ty));
}
try_visit!(visitor.visit_path(path, *id));
match kind {
DelegationKind::Single => {}
DelegationKind::List(suffixes) => {
for ident in suffixes {
visitor.visit_ident(*ident);
}
}
}
visit_opt!(visitor, visit_block, body);
}
}
Expand Down Expand Up @@ -756,11 +764,19 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(
AssocItemKind::MacCall(mac) => {
try_visit!(visitor.visit_mac_call(mac));
}
AssocItemKind::Delegation(box Delegation { id, qself, path, body }) => {
AssocItemKind::Delegation(box Delegation { id, qself, path, kind, body }) => {
if let Some(qself) = qself {
try_visit!(visitor.visit_ty(&qself.ty));
}
try_visit!(visitor.visit_path(path, *id));
match kind {
DelegationKind::Single => {}
DelegationKind::List(suffixes) => {
for ident in suffixes {
visitor.visit_ident(*ident);
}
}
}
visit_opt!(visitor, visit_block, body);
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_expand/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ expand_duplicate_matcher_binding = duplicate matcher binding
.label = duplicate binding
.label2 = previous binding
expand_empty_delegation_list =
empty list delegation is not supported
expand_expected_comma_in_list =
expected token: `,`
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_expand/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,10 @@ pub struct ExpectedParenOrBrace<'a> {
pub span: Span,
pub token: Cow<'a, str>,
}

#[derive(Diagnostic)]
#[diag(expand_empty_delegation_list)]
pub(crate) struct EmptyDelegationList {
#[primary_span]
pub span: Span,
}
101 changes: 95 additions & 6 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::base::*;
use crate::config::StripUnconfigured;
use crate::errors::{
IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported,
UnsupportedKeyValue, WrongFragmentKind,
EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported,
RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
};
use crate::hygiene::SyntaxContext;
use crate::mbe::diagnostics::annotate_err_with_kind;
Expand All @@ -15,8 +15,8 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, try_visit, walk_list, AssocCtxt, Visitor, VisitorResult};
use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind};
use rustc_ast::{ForeignItemKind, HasAttrs, HasNodeId};
use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, DelegationKind};
use rustc_ast::{ExprKind, ForeignItemKind, HasAttrs, HasNodeId};
use rustc_ast::{Inline, ItemKind, MacStmtStyle, MetaItemKind, ModKind};
use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
use rustc_ast_pretty::pprust;
Expand Down Expand Up @@ -1061,7 +1061,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
fn wrap_flat_map_node_noop_flat_map(
node: Self,
collector: &mut InvocationCollector<'_, '_>,
noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
mut noop_flat_map: impl FnMut(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
) -> Result<Self::OutputTy, Self> {
Ok(noop_flat_map(node, collector))
}
Expand Down Expand Up @@ -1105,8 +1105,19 @@ impl InvocationCollectorNode for P<ast::Item> {
fn wrap_flat_map_node_noop_flat_map(
mut node: Self,
collector: &mut InvocationCollector<'_, '_>,
noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
mut noop_flat_map: impl FnMut(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
) -> Result<Self::OutputTy, Self> {
if let ItemKind::Delegation(deleg) = &node.kind
&& let DelegationKind::List(..) = deleg.kind
{
return Ok(collector.expand_delegation_list(
&node,
deleg,
ItemKind::Delegation,
|item, collector| noop_flat_map(item, collector),
));
}

if !matches!(node.kind, ItemKind::Mod(..)) {
return Ok(noop_flat_map(node, collector));
}
Expand Down Expand Up @@ -1230,6 +1241,24 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag>
_ => unreachable!(),
}
}
fn wrap_flat_map_node_noop_flat_map(
node: Self,
collector: &mut InvocationCollector<'_, '_>,
mut noop_flat_map: impl FnMut(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
) -> Result<Self::OutputTy, Self> {
if let AssocItemKind::Delegation(deleg) = &node.wrapped.kind
&& let DelegationKind::List(..) = deleg.kind
{
return Ok(collector.expand_delegation_list(
&node.wrapped,
deleg,
AssocItemKind::Delegation,
|item, collector| noop_flat_map(AstNodeWrapper::new(item, TraitItemTag), collector),
));
}

Ok(noop_flat_map(node, collector))
}
}

struct ImplItemTag;
Expand All @@ -1255,6 +1284,24 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>
_ => unreachable!(),
}
}
fn wrap_flat_map_node_noop_flat_map(
node: Self,
collector: &mut InvocationCollector<'_, '_>,
mut noop_flat_map: impl FnMut(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
) -> Result<Self::OutputTy, Self> {
if let AssocItemKind::Delegation(deleg) = &node.wrapped.kind
&& let DelegationKind::List(..) = deleg.kind
{
return Ok(collector.expand_delegation_list(
&node.wrapped,
deleg,
AssocItemKind::Delegation,
|item, collector| noop_flat_map(AstNodeWrapper::new(item, ImplItemTag), collector),
));
}

Ok(noop_flat_map(node, collector))
}
}

impl InvocationCollectorNode for P<ast::ForeignItem> {
Expand Down Expand Up @@ -1773,6 +1820,48 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
});
}

fn expand_delegation_list<K: 'static>(
&mut self,
item: &ast::Item<K>,
deleg: &ast::Delegation,
kind_delegation: impl Copy + FnOnce(Box<ast::Delegation>) -> K,
mut noop_flat_map: impl FnMut(P<ast::Item<K>>, &mut Self) -> SmallVec<[P<ast::Item<K>>; 1]>,
) -> SmallVec<[P<ast::Item<K>>; 1]> {
assert_eq!(item.id, ast::DUMMY_NODE_ID);
let DelegationKind::List(suffixes) = &deleg.kind else { unreachable!() };

if 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 });
}

suffixes
.iter()
.flat_map(|&ident| {
let mut path = deleg.path.clone();
path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None });

let item = ast::Item {
attrs: item.attrs.clone(),
id: item.id,
span: item.span,
vis: item.vis.clone(),
ident,
kind: kind_delegation(Box::new(ast::Delegation {
id: ast::DUMMY_NODE_ID,
qself: deleg.qself.clone(),
path,
kind: DelegationKind::Single,
body: deleg.body.clone(),
})),
tokens: item.tokens.clone(),
};

noop_flat_map(P(item), self)
})
.collect()
}

fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
&mut self,
mut node: Node,
Expand Down
14 changes: 10 additions & 4 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,14 @@ impl<'a> Parser<'a> {
(None, self.parse_path(PathStyle::Expr)?)
};

let kind = if self.eat(&token::ModSep) {
let (suffixes, _) =
self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_path_segment_ident())?;
DelegationKind::List(suffixes)
} else {
DelegationKind::Single
};

let body = if self.check(&token::OpenDelim(Delimiter::Brace)) {
Some(self.parse_block()?)
} else {
Expand All @@ -696,10 +704,8 @@ impl<'a> Parser<'a> {
self.psess.gated_spans.gate(sym::fn_delegation, span);

let ident = path.segments.last().map(|seg| seg.ident).unwrap_or(Ident::empty());
Ok((
ident,
ItemKind::Delegation(Box::new(Delegation { id: DUMMY_NODE_ID, qself, path, body })),
))
let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, kind, body };
Ok((ident, ItemKind::Delegation(Box::new(deleg))))
}

fn parse_item_list<T>(
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,15 @@ impl<'a> Parser<'a> {
debug!("parse_qpath: (decrement) count={:?}", self.unmatched_angle_bracket_count);
}

if !self.recover_colon_before_qpath_proj() {
let is_import_coupler = self.is_import_coupler();
if !is_import_coupler && !self.recover_colon_before_qpath_proj() {
self.expect(&token::ModSep)?;
}

let qself = P(QSelf { ty, path_span, position: path.segments.len() });
self.parse_path_segments(&mut path.segments, style, None)?;
if !is_import_coupler {
self.parse_path_segments(&mut path.segments, style, None)?;
}

Ok((
qself,
Expand Down
32 changes: 32 additions & 0 deletions tests/ui/delegation/multiple-names-body-identify.rs
Original file line number Diff line number Diff line change
@@ -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::{foo, bar} {
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();
}
8 changes: 8 additions & 0 deletions tests/ui/delegation/multiple-names-empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]

mod m {}

reuse m::{}; //~ ERROR empty list delegation is not supported

fn main() {}
8 changes: 8 additions & 0 deletions tests/ui/delegation/multiple-names-empty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: empty list delegation is not supported
--> $DIR/multiple-names-empty.rs:6:1
|
LL | reuse m::{};
| ^^^^^^^^^^^^

error: aborting due to 1 previous error

Loading

0 comments on commit 886182d

Please sign in to comment.