Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0278505

Browse files
committedDec 2, 2023
Attempt to try to resolve blocking concerns
1 parent 64d7e0d commit 0278505

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+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.