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

Lines changed: 676 additions & 242 deletions

‎compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs‎

Lines changed: 144 additions & 0 deletions
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+
implTagfor $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+
unsafeimpl $crate::tagged_ptr::Tagfor $Self {
240+
constBITS: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+
matchself{
253+
// `match` is doing heavy lifting here, by requiring exhaustiveness
254+
$(
255+
$($path)::* $({ $( $fields )*})? => ${index()},
256+
)*
257+
}
258+
}
259+
260+
#[inline]
261+
unsafefn 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‎

Lines changed: 126 additions & 0 deletions
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+
pubenumAstFragment{
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+
pubenumAstFragmentKind{
190+
OptExpr,
191+
MethodReceiverExpr,
192+
$($Kind,)*
193+
}
194+
195+
implAstFragmentKind{
196+
pubfn name(self) -> &'staticstr{
197+
matchself{
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+
matchself{
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+
implAstFragment{
216+
pubfn add_placeholders(&mutself, placeholders:&[NodeId]){
217+
if placeholders.is_empty(){
218+
return;
219+
}
220+
matchself{
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+
pubfn make_opt_expr(self) -> Option<P<ast::Expr>> {
230+
matchself{
231+
AstFragment::OptExpr(expr) => expr,
232+
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
233+
}
234+
}
235+
236+
pubfn make_method_receiver_expr(self) -> P<ast::Expr> {
237+
matchself{
238+
AstFragment::MethodReceiverExpr(expr) => expr,
239+
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
240+
}
241+
}
242+
243+
$(pubfn $make_ast(self) -> $AstTy {
244+
matchself{
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+
pubfn mut_visit_with<F:MutVisitor>(&mutself, vis:&mutF){
255+
matchself{
256+
AstFragment::OptExpr(opt_expr) => {
257+
visit_clobber(opt_expr, |opt_expr| {
258+
ifletSome(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+
pubfn visit_with<'a,V:Visitor<'a>>(&'a self, visitor:&mutV){
273+
matchself{
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> MacResultforcrate::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‎

Lines changed: 26 additions & 6 deletions
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)enumMetaVarExpr{
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
letmut 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:&'sessParseSess,
9395
span:Span,
9496
) -> PResult<'sess,MetaVarExpr>{
97+
eat_dollar(iter, sess, span)?;
9598
let ident = parse_ident(iter, sess, span)?;
9699
let depth = iftry_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+
fneat_dollar<'sess>(
175+
iter:&mutRefTokenTreeCursor<'_>,
176+
sess:&'sessParseSess,
177+
span:Span,
178+
) -> PResult<'sess,()>{
179+
ifletSome(TokenTree::Token(token::Token{kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
180+
{
181+
let _ = iter.next();
182+
returnOk(());
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)