Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ pub enum PatKind {
Never,

/// A guard pattern (e.g., `x if guard(x)`).
Guard(Box<Pat>, Box<Expr>),
Guard(Box<Pat>, Box<Guard>),

/// Parentheses in patterns used for grouping (i.e., `(PAT)`).
Paren(Box<Pat>),
Expand Down Expand Up @@ -1346,7 +1346,7 @@ pub struct Arm {
/// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`.
pub pat: Box<Pat>,
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`.
pub guard: Option<Box<Expr>>,
pub guard: Option<Box<Guard>>,
/// Match arm body. Omitted if the pattern is a never pattern.
pub body: Option<Box<Expr>>,
pub span: Span,
Expand Down Expand Up @@ -3954,6 +3954,18 @@ impl ConstBlockItem {
pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP };
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct Guard {
pub cond: Expr,
pub span_with_leading_if: Span,
}

impl Guard {
pub fn span(&self) -> Span {
self.cond.span
}
}

// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum ItemKind {
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub enum MetaVarKind {
},
Path,
Vis,
Guard,
TT,
}

Expand All @@ -114,6 +115,7 @@ impl fmt::Display for MetaVarKind {
MetaVarKind::Meta { .. } => sym::meta,
MetaVarKind::Path => sym::path,
MetaVarKind::Vis => sym::vis,
MetaVarKind::Guard => sym::guard,
MetaVarKind::TT => sym::tt,
};
write!(f, "{sym}")
Expand Down Expand Up @@ -1124,6 +1126,7 @@ pub enum NonterminalKind {
Meta,
Path,
Vis,
Guard,
TT,
}

Expand Down Expand Up @@ -1161,6 +1164,7 @@ impl NonterminalKind {
sym::meta => NonterminalKind::Meta,
sym::path => NonterminalKind::Path,
sym::vis => NonterminalKind::Vis,
sym::guard => NonterminalKind::Guard,
sym::tt => NonterminalKind::TT,
_ => return None,
})
Expand All @@ -1182,6 +1186,7 @@ impl NonterminalKind {
NonterminalKind::Meta => sym::meta,
NonterminalKind::Path => sym::path,
NonterminalKind::Vis => sym::vis,
NonterminalKind::Guard => sym::guard,
NonterminalKind::TT => sym::tt,
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ macro_rules! common_visitor_and_walkers {
FormatArguments,
FormatPlaceholder,
GenericParamKind,
Guard,
Impl,
ImplPolarity,
Inline,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ impl<'hir> LoweringContext<'_, 'hir> {

fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
let pat = self.lower_pat(&arm.pat);
let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
let guard = arm.guard.as_ref().map(|guard| self.lower_expr(&guard.cond));
let hir_id = self.next_id();
let span = self.lower_span(arm.span);
self.lower_attrs(hir_id, &arm.attrs, arm.span, Target::Arm);
Expand All @@ -662,7 +662,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
} else if let Some(body) = &arm.body {
self.dcx().emit_err(NeverPatternWithBody { span: body.span });
} else if let Some(g) = &arm.guard {
self.dcx().emit_err(NeverPatternWithGuard { span: g.span });
self.dcx().emit_err(NeverPatternWithGuard { span: g.span() });
}

// We add a fake `loop {}` arm body so that it typecks to `!`. The mir lowering of never
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_range_end(end, e2.is_some()),
);
}
PatKind::Guard(inner, cond) => {
break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond));
PatKind::Guard(inner, guard) => {
break hir::PatKind::Guard(
self.lower_pat(inner),
self.lower_expr(&guard.cond),
);
}
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
PatKind::Rest => {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1889,12 +1889,12 @@ impl<'a> State<'a> {
self.print_expr(e, FixupContext::default());
}
}
PatKind::Guard(subpat, condition) => {
PatKind::Guard(subpat, guard) => {
self.popen();
self.print_pat(subpat);
self.space();
self.word_space("if");
self.print_expr(condition, FixupContext::default());
self.print_expr(&guard.cond, FixupContext::default());
self.pclose();
}
PatKind::Slice(elts) => {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -880,9 +880,9 @@ impl<'a> State<'a> {
self.print_outer_attributes(&arm.attrs);
self.print_pat(&arm.pat);
self.space();
if let Some(e) = &arm.guard {
if let Some(guard) = &arm.guard {
self.word_space("if");
self.print_expr(e, FixupContext::default());
self.print_expr(&guard.cond, FixupContext::default());
self.space();
}

Expand Down
58 changes: 48 additions & 10 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ pub fn compile_declarative_macro(
let args = p.parse_token_tree();
check_args_parens(sess, sym::attr, &args);
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
check_emission(check_lhs(sess, node_id, &args));
check_emission(check_lhs(sess, features, node_id, &args));
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
return dummy_syn_ext(guar);
}
Expand Down Expand Up @@ -773,7 +773,7 @@ pub fn compile_declarative_macro(
};
let lhs_tt = p.parse_token_tree();
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
check_emission(check_lhs(sess, node_id, &lhs_tt));
check_emission(check_lhs(sess, features, node_id, &lhs_tt));
if let Err(e) = p.expect(exp!(FatArrow)) {
return dummy_syn_ext(e.emit());
}
Expand Down Expand Up @@ -870,21 +870,27 @@ fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(),
}
}

fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
fn check_lhs(
sess: &Session,
features: &Features,
node_id: NodeId,
lhs: &mbe::TokenTree,
) -> Result<(), ErrorGuaranteed> {
let e1 = check_lhs_nt_follows(sess, features, node_id, lhs);
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
e1.and(e2)
}

fn check_lhs_nt_follows(
sess: &Session,
features: &Features,
node_id: NodeId,
lhs: &mbe::TokenTree,
) -> Result<(), ErrorGuaranteed> {
// lhs is going to be like TokenTree::Delimited(...), where the
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
if let mbe::TokenTree::Delimited(.., delimited) = lhs {
check_matcher(sess, node_id, &delimited.tts)
check_matcher(sess, features, node_id, &delimited.tts)
} else {
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
Err(sess.dcx().span_err(lhs.span(), msg))
Expand Down Expand Up @@ -989,12 +995,13 @@ fn check_rhs(sess: &Session, rhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed

fn check_matcher(
sess: &Session,
features: &Features,
node_id: NodeId,
matcher: &[mbe::TokenTree],
) -> Result<(), ErrorGuaranteed> {
let first_sets = FirstSets::new(matcher);
let empty_suffix = TokenSet::empty();
check_matcher_core(sess, node_id, &first_sets, matcher, &empty_suffix)?;
check_matcher_core(sess, features, node_id, &first_sets, matcher, &empty_suffix)?;
Ok(())
}

Expand Down Expand Up @@ -1331,6 +1338,7 @@ impl<'tt> TokenSet<'tt> {
// see `FirstSets::new`.
fn check_matcher_core<'tt>(
sess: &Session,
features: &Features,
node_id: NodeId,
first_sets: &FirstSets<'tt>,
matcher: &'tt [mbe::TokenTree],
Expand Down Expand Up @@ -1369,6 +1377,17 @@ fn check_matcher_core<'tt>(
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl { .. }
| TokenTree::MetaVarExpr(..) => {
if let TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } = token
&& !features.macro_guard_matcher()
{
feature_err(
sess,
sym::macro_guard_matcher,
token.span(),
"`guard` fragments in macro are unstable",
)
.emit();
}
if token_can_be_followed_by_any(token) {
// don't need to track tokens that work with any,
last.replace_with_irrelevant();
Expand All @@ -1385,7 +1404,7 @@ fn check_matcher_core<'tt>(
d.delim.as_close_token_kind(),
span.close,
));
check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?;
check_matcher_core(sess, features, node_id, first_sets, &d.tts, &my_suffix)?;
// don't track non NT tokens
last.replace_with_irrelevant();

Expand Down Expand Up @@ -1417,7 +1436,14 @@ fn check_matcher_core<'tt>(
// At this point, `suffix_first` is built, and
// `my_suffix` is some TokenSet that we can use
// for checking the interior of `seq_rep`.
let next = check_matcher_core(sess, node_id, first_sets, &seq_rep.tts, my_suffix)?;
let next = check_matcher_core(
sess,
features,
node_id,
first_sets,
&seq_rep.tts,
my_suffix,
)?;
if next.maybe_empty {
last.add_all(&next);
} else {
Expand Down Expand Up @@ -1609,7 +1635,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
}
}
NonterminalKind::Pat(PatParam { .. }) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`if let`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Eq | Or => IsInFollow::Yes,
Expand All @@ -1618,11 +1644,12 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
}
_ => IsInFollow::No(TOKENS),
},
TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::Pat(PatWithOr) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`if let`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Eq => IsInFollow::Yes,
Expand All @@ -1631,6 +1658,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
}
_ => IsInFollow::No(TOKENS),
},
TokenTree::MetaVarDecl { kind: NonterminalKind::Guard, .. } => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
}
}
NonterminalKind::Guard => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`{`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | OpenBrace => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
},
_ => IsInFollow::No(TOKENS),
}
}
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use rustc_parse::parser::ParseNtResult;
use rustc_session::parse::ParseSess;
use rustc_span::hygiene::{LocalExpnId, Transparency};
use rustc_span::{
Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
BytePos, Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, kw, sym,
with_metavar_spans,
};
use smallvec::{SmallVec, smallvec};

Expand Down Expand Up @@ -556,6 +557,19 @@ fn transcribe_pnr<'tx>(
ParseNtResult::Vis(vis) => {
mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
}
ParseNtResult::Guard(guard) => {
// FIXME(macro_guard_matcher):
// Perhaps it would be better to treat the leading `if` as part of `ast::Guard` during parsing?
// Currently they are separate, but in macros we match and emit the leading `if` for `:guard` matchers, which creates some inconsistency.

let leading_if_span =
guard.span_with_leading_if.with_hi(guard.span_with_leading_if.lo() + BytePos(2));
let mut ts =
TokenStream::token_alone(token::Ident(kw::If, IdentIsRaw::No), leading_if_span);
ts.push_stream(TokenStream::from_ast(&guard.cond));

mk_delimited(guard.span_with_leading_if, MetaVarKind::Guard, ts)
}
};

tscx.result.push(tt);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,8 @@ declare_features! (
(unstable, macro_attr, "1.91.0", Some(143547)),
/// Allow `macro_rules!` derive rules
(unstable, macro_derive, "1.91.0", Some(143549)),
/// Allow `$x:guard` matcher in macros
(unstable, macro_guard_matcher, "CURRENT_RUSTC_VERSION", Some(153104)),
/// Give access to additional metadata about declarative macro meta-variables.
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
/// Provides a way to concatenate identifiers using metavariable expressions.
Expand Down
Loading
Loading