Skip to content

Commit f651b43

Browse files
committed
Auto merge of #117050 - c410-f3r:here-we-go-again, r=petrochenkov
[`RFC 3086`] Attempt to try to resolve blocking concerns Implements what is described at #83527 (comment) to hopefully make some progress. It is unknown if such approach is or isn't desired due to the lack of further feedback, as such, it is probably best to nominate this PR to the official entities. `@rustbot` labels +I-compiler-nominated
2 parents 9f1bfe5 + 0278505 commit f651b43

16 files changed

+676
-242
lines changed

compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs

+144
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
/// E::A,
8282
/// }
8383
/// ```
84+
#[cfg(bootstrap)]
8485
#[macro_export]
8586
macro_rules! impl_tag {
8687
(
@@ -140,5 +141,148 @@ macro_rules! impl_tag {
140141
};
141142
}
142143

144+
/// Implements [`Tag`] for a given type.
145+
///
146+
/// You can use `impl_tag` on structs and enums.
147+
/// You need to specify the type and all its possible values,
148+
/// which can only be paths with optional fields.
149+
///
150+
/// [`Tag`]: crate::tagged_ptr::Tag
151+
///
152+
/// # Examples
153+
///
154+
/// Basic usage:
155+
///
156+
/// ```
157+
/// #![feature(macro_metavar_expr)]
158+
/// use rustc_data_structures::{impl_tag, tagged_ptr::Tag};
159+
///
160+
/// #[derive(Copy, Clone, PartialEq, Debug)]
161+
/// enum SomeTag {
162+
/// A,
163+
/// B,
164+
/// X { v: bool },
165+
/// Y(bool, bool),
166+
/// }
167+
///
168+
/// impl_tag! {
169+
/// // The type for which the `Tag` will be implemented
170+
/// impl Tag for SomeTag;
171+
/// // You need to specify all possible tag values:
172+
/// SomeTag::A, // 0
173+
/// SomeTag::B, // 1
174+
/// // For variants with fields, you need to specify the fields:
175+
/// SomeTag::X { v: true }, // 2
176+
/// SomeTag::X { v: false }, // 3
177+
/// // For tuple variants use named syntax:
178+
/// SomeTag::Y { 0: true, 1: true }, // 4
179+
/// SomeTag::Y { 0: false, 1: true }, // 5
180+
/// SomeTag::Y { 0: true, 1: false }, // 6
181+
/// SomeTag::Y { 0: false, 1: false }, // 7
182+
/// }
183+
///
184+
/// // Tag values are assigned in order:
185+
/// assert_eq!(SomeTag::A.into_usize(), 0);
186+
/// assert_eq!(SomeTag::X { v: false }.into_usize(), 3);
187+
/// assert_eq!(SomeTag::Y(false, true).into_usize(), 5);
188+
///
189+
/// assert_eq!(unsafe { SomeTag::from_usize(1) }, SomeTag::B);
190+
/// assert_eq!(unsafe { SomeTag::from_usize(2) }, SomeTag::X { v: true });
191+
/// assert_eq!(unsafe { SomeTag::from_usize(7) }, SomeTag::Y(false, false));
192+
/// ```
193+
///
194+
/// Structs are supported:
195+
///
196+
/// ```
197+
/// #![feature(macro_metavar_expr)]
198+
/// # use rustc_data_structures::impl_tag;
199+
/// #[derive(Copy, Clone)]
200+
/// struct Flags { a: bool, b: bool }
201+
///
202+
/// impl_tag! {
203+
/// impl Tag for Flags;
204+
/// Flags { a: true, b: true },
205+
/// Flags { a: false, b: true },
206+
/// Flags { a: true, b: false },
207+
/// Flags { a: false, b: false },
208+
/// }
209+
/// ```
210+
///
211+
/// Not specifying all values results in a compile error:
212+
///
213+
/// ```compile_fail,E0004
214+
/// #![feature(macro_metavar_expr)]
215+
/// # use rustc_data_structures::impl_tag;
216+
/// #[derive(Copy, Clone)]
217+
/// enum E {
218+
/// A,
219+
/// B,
220+
/// }
221+
///
222+
/// impl_tag! {
223+
/// impl Tag for E;
224+
/// E::A,
225+
/// }
226+
/// ```
227+
#[cfg(not(bootstrap))]
228+
#[macro_export]
229+
macro_rules! impl_tag {
230+
(
231+
impl Tag for $Self:ty;
232+
$(
233+
$($path:ident)::* $( { $( $fields:tt )* })?,
234+
)*
235+
) => {
236+
// Safety:
237+
// `bits_for_tags` is called on the same `${index()}`-es as
238+
// `into_usize` returns, thus `BITS` constant is correct.
239+
unsafe impl $crate::tagged_ptr::Tag for $Self {
240+
const BITS: u32 = $crate::tagged_ptr::bits_for_tags(&[
241+
$(
242+
${index()},
243+
$( ${ignore($path)} )*
244+
)*
245+
]);
246+
247+
#[inline]
248+
fn into_usize(self) -> usize {
249+
// This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc)
250+
// (or at least it should, see <https://github.com/rust-lang/rust/issues/110613>)
251+
#[forbid(unreachable_patterns)]
252+
match self {
253+
// `match` is doing heavy lifting here, by requiring exhaustiveness
254+
$(
255+
$($path)::* $( { $( $fields )* } )? => ${index()},
256+
)*
257+
}
258+
}
259+
260+
#[inline]
261+
unsafe fn from_usize(tag: usize) -> Self {
262+
match tag {
263+
$(
264+
${index()} => $($path)::* $( { $( $fields )* } )?,
265+
)*
266+
267+
// Safety:
268+
// `into_usize` only returns `${index()}` of the same
269+
// repetition as we are filtering above, thus if this is
270+
// reached, the safety contract of this function was
271+
// already breached.
272+
_ => unsafe {
273+
debug_assert!(
274+
false,
275+
"invalid tag: {tag}\
276+
(this is a bug in the caller of `from_usize`)"
277+
);
278+
std::hint::unreachable_unchecked()
279+
},
280+
}
281+
}
282+
283+
}
284+
};
285+
}
286+
143287
#[cfg(test)]
144288
mod tests;

compiler/rustc_expand/src/expand.rs

+126
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use std::path::PathBuf;
4141
use std::rc::Rc;
4242
use std::{iter, mem};
4343

44+
#[cfg(bootstrap)]
4445
macro_rules! ast_fragments {
4546
(
4647
$($Kind:ident($AstTy:ty) {
@@ -165,6 +166,131 @@ macro_rules! ast_fragments {
165166
}
166167
}
167168

169+
#[cfg(not(bootstrap))]
170+
macro_rules! ast_fragments {
171+
(
172+
$($Kind:ident($AstTy:ty) {
173+
$kind_name:expr;
174+
$(one fn $mut_visit_ast:ident; fn $visit_ast:ident;)?
175+
$(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*);)?
176+
fn $make_ast:ident;
177+
})*
178+
) => {
179+
/// A fragment of AST that can be produced by a single macro expansion.
180+
/// Can also serve as an input and intermediate result for macro expansion operations.
181+
pub enum AstFragment {
182+
OptExpr(Option<P<ast::Expr>>),
183+
MethodReceiverExpr(P<ast::Expr>),
184+
$($Kind($AstTy),)*
185+
}
186+
187+
/// "Discriminant" of an AST fragment.
188+
#[derive(Copy, Clone, PartialEq, Eq)]
189+
pub enum AstFragmentKind {
190+
OptExpr,
191+
MethodReceiverExpr,
192+
$($Kind,)*
193+
}
194+
195+
impl AstFragmentKind {
196+
pub fn name(self) -> &'static str {
197+
match self {
198+
AstFragmentKind::OptExpr => "expression",
199+
AstFragmentKind::MethodReceiverExpr => "expression",
200+
$(AstFragmentKind::$Kind => $kind_name,)*
201+
}
202+
}
203+
204+
fn make_from<'a>(self, result: Box<dyn MacResult + 'a>) -> Option<AstFragment> {
205+
match self {
206+
AstFragmentKind::OptExpr =>
207+
result.make_expr().map(Some).map(AstFragment::OptExpr),
208+
AstFragmentKind::MethodReceiverExpr =>
209+
result.make_expr().map(AstFragment::MethodReceiverExpr),
210+
$(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)*
211+
}
212+
}
213+
}
214+
215+
impl AstFragment {
216+
pub fn add_placeholders(&mut self, placeholders: &[NodeId]) {
217+
if placeholders.is_empty() {
218+
return;
219+
}
220+
match self {
221+
$($(AstFragment::$Kind(ast) => ast.extend(placeholders.iter().flat_map(|id| {
222+
${ignore($flat_map_ast_elt)}
223+
placeholder(AstFragmentKind::$Kind, *id, None).$make_ast()
224+
})),)?)*
225+
_ => panic!("unexpected AST fragment kind")
226+
}
227+
}
228+
229+
pub fn make_opt_expr(self) -> Option<P<ast::Expr>> {
230+
match self {
231+
AstFragment::OptExpr(expr) => expr,
232+
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
233+
}
234+
}
235+
236+
pub fn make_method_receiver_expr(self) -> P<ast::Expr> {
237+
match self {
238+
AstFragment::MethodReceiverExpr(expr) => expr,
239+
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
240+
}
241+
}
242+
243+
$(pub fn $make_ast(self) -> $AstTy {
244+
match self {
245+
AstFragment::$Kind(ast) => ast,
246+
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
247+
}
248+
})*
249+
250+
fn make_ast<T: InvocationCollectorNode>(self) -> T::OutputTy {
251+
T::fragment_to_output(self)
252+
}
253+
254+
pub fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) {
255+
match self {
256+
AstFragment::OptExpr(opt_expr) => {
257+
visit_clobber(opt_expr, |opt_expr| {
258+
if let Some(expr) = opt_expr {
259+
vis.filter_map_expr(expr)
260+
} else {
261+
None
262+
}
263+
});
264+
}
265+
AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr),
266+
$($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)*
267+
$($(AstFragment::$Kind(ast) =>
268+
ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)*
269+
}
270+
}
271+
272+
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
273+
match self {
274+
AstFragment::OptExpr(Some(expr)) => visitor.visit_expr(expr),
275+
AstFragment::OptExpr(None) => {}
276+
AstFragment::MethodReceiverExpr(expr) => visitor.visit_method_receiver_expr(expr),
277+
$($(AstFragment::$Kind(ast) => visitor.$visit_ast(ast),)?)*
278+
$($(AstFragment::$Kind(ast) => for ast_elt in &ast[..] {
279+
visitor.$visit_ast_elt(ast_elt, $($args)*);
280+
})?)*
281+
}
282+
}
283+
}
284+
285+
impl<'a> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a> {
286+
$(fn $make_ast(self: Box<crate::mbe::macro_rules::ParserAnyMacro<'a>>)
287+
-> Option<$AstTy> {
288+
Some(self.make(AstFragmentKind::$Kind).$make_ast())
289+
})*
290+
}
291+
}
292+
}
293+
168294
ast_fragments! {
169295
Expr(P<ast::Expr>) { "expression"; one fn visit_expr; fn visit_expr; fn make_expr; }
170296
Pat(P<ast::Pat>) { "pattern"; one fn visit_pat; fn visit_pat; fn make_pat; }

compiler/rustc_expand/src/mbe/metavar_expr.rs

+26-6
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ use rustc_span::Span;
1010
/// A meta-variable expression, for expansions based on properties of meta-variables.
1111
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
1212
pub(crate) enum MetaVarExpr {
13-
/// The number of repetitions of an identifier, optionally limited to a number
14-
/// of outer-most repetition depths. If the depth limit is `None` then the depth is unlimited.
15-
Count(Ident, Option<usize>),
13+
/// The number of repetitions of an identifier.
14+
Count(Ident, usize),
1615

1716
/// Ignore a meta-variable for repetition without expansion.
1817
Ignore(Ident),
@@ -43,7 +42,10 @@ impl MetaVarExpr {
4342
let mut iter = args.trees();
4443
let rslt = match ident.as_str() {
4544
"count" => parse_count(&mut iter, sess, ident.span)?,
46-
"ignore" => MetaVarExpr::Ignore(parse_ident(&mut iter, sess, ident.span)?),
45+
"ignore" => {
46+
eat_dollar(&mut iter, sess, ident.span)?;
47+
MetaVarExpr::Ignore(parse_ident(&mut iter, sess, ident.span)?)
48+
}
4749
"index" => MetaVarExpr::Index(parse_depth(&mut iter, sess, ident.span)?),
4850
"length" => MetaVarExpr::Length(parse_depth(&mut iter, sess, ident.span)?),
4951
_ => {
@@ -92,6 +94,7 @@ fn parse_count<'sess>(
9294
sess: &'sess ParseSess,
9395
span: Span,
9496
) -> PResult<'sess, MetaVarExpr> {
97+
eat_dollar(iter, sess, span)?;
9598
let ident = parse_ident(iter, sess, span)?;
9699
let depth = if try_eat_comma(iter) {
97100
if iter.look_ahead(0).is_none() {
@@ -100,9 +103,9 @@ fn parse_count<'sess>(
100103
"`count` followed by a comma must have an associated index indicating its depth",
101104
));
102105
}
103-
Some(parse_depth(iter, sess, span)?)
106+
parse_depth(iter, sess, span)?
104107
} else {
105-
None
108+
0
106109
};
107110
Ok(MetaVarExpr::Count(ident, depth))
108111
}
@@ -166,3 +169,20 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
166169
}
167170
false
168171
}
172+
173+
/// Expects that the next item is a dollar sign.
174+
fn eat_dollar<'sess>(
175+
iter: &mut RefTokenTreeCursor<'_>,
176+
sess: &'sess ParseSess,
177+
span: Span,
178+
) -> PResult<'sess, ()> {
179+
if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
180+
{
181+
let _ = iter.next();
182+
return Ok(());
183+
}
184+
Err(sess.span_diagnostic.struct_span_err(
185+
span,
186+
"meta-variables within meta-variable expressions must be referenced using a dollar sign",
187+
))
188+
}

0 commit comments

Comments
 (0)