Skip to content

Commit b8d4e4d

Browse files
committed
Allow concat in repetitions
1 parent 52f3c71 commit b8d4e4d

File tree

4 files changed

+90
-45
lines changed

4 files changed

+90
-45
lines changed

compiler/rustc_expand/src/mbe/macro_check.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,10 @@ fn check_occurrences(
352352
check_ops_is_prefix(psess, node_id, macros, binders, ops, span, name);
353353
}
354354
TokenTree::MetaVarExpr(dl, ref mve) => {
355-
let Some(name) = mve.ident().map(MacroRulesNormalizedIdent::new) else {
356-
return;
357-
};
358-
check_ops_is_prefix(psess, node_id, macros, binders, ops, dl.entire(), name);
355+
mve.for_each_metavar((), |_, ident| {
356+
let name = MacroRulesNormalizedIdent::new(*ident);
357+
check_ops_is_prefix(psess, node_id, macros, binders, ops, dl.entire(), name);
358+
});
359359
}
360360
TokenTree::Delimited(.., ref del) => {
361361
check_nested_occurrences(psess, node_id, &del.tts, macros, binders, ops, guar);

compiler/rustc_expand/src/mbe/metavar_expr.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,18 @@ impl MetaVarExpr {
111111
Ok(rslt)
112112
}
113113

114-
pub(crate) fn ident(&self) -> Option<Ident> {
115-
match *self {
116-
MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident),
117-
MetaVarExpr::Concat { .. } | MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => None,
114+
pub(crate) fn for_each_metavar<A>(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A {
115+
match self {
116+
MetaVarExpr::Concat(elems) => {
117+
for elem in elems {
118+
if let MetaVarExprConcatElem::Var(ident) = elem {
119+
aux = cb(aux, ident)
120+
}
121+
}
122+
aux
123+
}
124+
MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => cb(aux, ident),
125+
MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => aux,
118126
}
119127
}
120128
}

compiler/rustc_expand/src/mbe/transcribe.rs

+56-37
Original file line numberDiff line numberDiff line change
@@ -557,17 +557,13 @@ fn lockstep_iter_size(
557557
}
558558
}
559559
TokenTree::MetaVarExpr(_, expr) => {
560-
let default_rslt = LockstepIterSize::Unconstrained;
561-
let Some(ident) = expr.ident() else {
562-
return default_rslt;
563-
};
564-
let name = MacroRulesNormalizedIdent::new(ident);
565-
match lookup_cur_matched(name, interpolations, repeats) {
566-
Some(MatchedSeq(ads)) => {
567-
default_rslt.with(LockstepIterSize::Constraint(ads.len(), name))
568-
}
569-
_ => default_rslt,
570-
}
560+
expr.for_each_metavar(LockstepIterSize::Unconstrained, |lis, ident| {
561+
lis.with(lockstep_iter_size(
562+
&TokenTree::MetaVar(ident.span, *ident),
563+
interpolations,
564+
repeats,
565+
))
566+
})
571567
}
572568
TokenTree::Token(..) => LockstepIterSize::Unconstrained,
573569
}
@@ -695,7 +691,23 @@ fn transcribe_metavar_expr<'a>(
695691
let symbol = match element {
696692
MetaVarExprConcatElem::Ident(elem) => elem.name,
697693
MetaVarExprConcatElem::Literal(elem) => *elem,
698-
MetaVarExprConcatElem::Var(elem) => extract_var_symbol(dcx, *elem, interp)?,
694+
MetaVarExprConcatElem::Var(ident) => {
695+
match matched_from_ident(dcx, *ident, interp)? {
696+
NamedMatch::MatchedSeq(named_matches) => {
697+
let curr_idx = repeats.last().unwrap().0;
698+
match &named_matches[curr_idx] {
699+
// FIXME(c410-f3r) Nested repetitions are unimplemented
700+
MatchedSeq(_) => unimplemented!(),
701+
MatchedSingle(pnr) => {
702+
extract_symbol_from_pnr(dcx, pnr, ident.span)?
703+
}
704+
}
705+
}
706+
NamedMatch::MatchedSingle(pnr) => {
707+
extract_symbol_from_pnr(dcx, pnr, ident.span)?
708+
}
709+
}
710+
}
699711
};
700712
concatenated.push_str(symbol.as_str());
701713
}
@@ -752,41 +764,48 @@ fn transcribe_metavar_expr<'a>(
752764
}
753765

754766
/// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
755-
fn extract_var_symbol<'a>(
767+
fn extract_symbol_from_pnr<'a>(
756768
dcx: DiagCtxtHandle<'a>,
757-
ident: Ident,
758-
interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
769+
pnr: &ParseNtResult,
770+
span_err: Span,
759771
) -> PResult<'a, Symbol> {
760-
if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? {
761-
if let ParseNtResult::Ident(nt_ident, is_raw) = pnr {
772+
match pnr {
773+
ParseNtResult::Ident(nt_ident, is_raw) => {
762774
if let IdentIsRaw::Yes = is_raw {
763-
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
775+
return Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR));
764776
}
765777
return Ok(nt_ident.name);
766778
}
767-
768-
if let ParseNtResult::Tt(TokenTree::Token(Token { kind, .. }, _)) = pnr {
769-
if let TokenKind::Ident(symbol, is_raw) = kind {
770-
if let IdentIsRaw::Yes = is_raw {
771-
return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
772-
}
773-
return Ok(*symbol);
774-
}
775-
776-
if let TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }) = kind {
777-
return Ok(*symbol);
779+
ParseNtResult::Tt(TokenTree::Token(
780+
Token { kind: TokenKind::Ident(symbol, is_raw), .. },
781+
_,
782+
)) => {
783+
if let IdentIsRaw::Yes = is_raw {
784+
return Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR));
778785
}
786+
return Ok(*symbol);
779787
}
780-
781-
if let ParseNtResult::Nt(nt) = pnr
782-
&& let Nonterminal::NtLiteral(expr) = &**nt
783-
&& let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind
788+
ParseNtResult::Tt(TokenTree::Token(
789+
Token {
790+
kind: TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }),
791+
..
792+
},
793+
_,
794+
)) => {
795+
return Ok(*symbol);
796+
}
797+
ParseNtResult::Nt(nt)
798+
if let Nonterminal::NtLiteral(expr) = &**nt
799+
&& let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) =
800+
&expr.kind =>
784801
{
785802
return Ok(*symbol);
786803
}
804+
_ => Err(dcx
805+
.struct_err(
806+
"metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`",
807+
)
808+
.with_note("currently only string literals are supported")
809+
.with_span(span_err)),
787810
}
788-
Err(dcx
789-
.struct_err("metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")
790-
.with_note("currently only string literals are supported")
791-
.with_span(ident.span))
792811
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ run-pass
2+
3+
#![feature(macro_metavar_expr_concat)]
4+
5+
macro_rules! one_rep {
6+
( $($a:ident)* ) => {
7+
$(
8+
const ${concat($a, Z)}: i32 = 3;
9+
)*
10+
};
11+
}
12+
13+
fn main() {
14+
one_rep!(A B C);
15+
assert_eq!(AZ, 3);
16+
assert_eq!(BZ, 3);
17+
assert_eq!(CZ, 3);
18+
}

0 commit comments

Comments
 (0)