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 0b51302

Browse files
committedNov 8, 2024
checkpoint lowering of contract requires attribute to the AST form.
1 parent 3bb2bed commit 0b51302

File tree

1 file changed

+114
-8
lines changed

1 file changed

+114
-8
lines changed
 

‎compiler/rustc_builtin_macros/src/contracts.rs

+114-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#![allow(unused_imports, unused_variables)]
22

33
use rustc_ast::token;
4-
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
4+
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing};
5+
use rustc_ast::tokenstream::{TokenStream, TokenTree};
56
use rustc_errors::ErrorGuaranteed;
67
use rustc_expand::base::{AttrProcMacro, ExtCtxt};
7-
use rustc_span::symbol::{sym, Symbol};
8+
use rustc_span::symbol::{kw, Ident, sym, Symbol};
89
use rustc_span::Span;
910

1011
pub(crate) struct ExpandRequires;
@@ -19,9 +20,9 @@ impl AttrProcMacro for ExpandRequires {
1920
ecx: &'cx mut ExtCtxt<'_>,
2021
span: Span,
2122
annotation: TokenStream,
22-
annotated: TokenStream,
23+
annotated: TokenStream,
2324
) -> Result<TokenStream, ErrorGuaranteed> {
24-
todo!()
25+
expand_requires_tts(ecx, span, annotation, annotated)
2526
}
2627
}
2728

@@ -31,9 +32,9 @@ impl AttrProcMacro for ExpandCaptures {
3132
ecx: &'cx mut ExtCtxt<'_>,
3233
span: Span,
3334
annotation: TokenStream,
34-
annotated: TokenStream,
35+
annotated: TokenStream,
3536
) -> Result<TokenStream, ErrorGuaranteed> {
36-
todo!()
37+
todo!()
3738
}
3839
}
3940

@@ -43,8 +44,113 @@ impl AttrProcMacro for ExpandEnsures {
4344
ecx: &'cx mut ExtCtxt<'_>,
4445
span: Span,
4546
annotation: TokenStream,
46-
annotated: TokenStream,
47+
annotated: TokenStream,
4748
) -> Result<TokenStream, ErrorGuaranteed> {
48-
todo!()
49+
todo!()
4950
}
5051
}
52+
53+
fn expand_injecting_circa_where_clause(
54+
_ecx: &mut ExtCtxt<'_>,
55+
attr_span: Span,
56+
annotated: TokenStream,
57+
inject: impl FnOnce(&mut Vec<TokenTree>) -> Result<(), ErrorGuaranteed>,
58+
) -> Result<TokenStream, ErrorGuaranteed> {
59+
let mut new_tts = Vec::with_capacity(annotated.len());
60+
let mut cursor = annotated.into_trees();
61+
62+
// Find the `fn name<G,...>(x:X,...)` and inject the AST contract forms right after
63+
// the formal parameters (and return type if any).
64+
while let Some(tt) = cursor.next_ref() {
65+
new_tts.push(tt.clone());
66+
if let TokenTree::Token(tok, _) = tt && tok.is_ident_named(kw::Fn) {
67+
break;
68+
}
69+
}
70+
71+
// Found the `fn` keyword, now find the formal parameters.
72+
//
73+
// FIXME: can this fail if you have parentheticals in a generics list, like `fn foo<F: Fn(X) -> Y>` ?
74+
while let Some(tt) = cursor.next_ref() {
75+
new_tts.push(tt.clone());
76+
77+
if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt {
78+
break;
79+
}
80+
if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = tt {
81+
panic!("contract attribute applied to fn without parameter list.");
82+
}
83+
}
84+
85+
// There *might* be a return type declaration (and figuring out where that ends would require parsing an arbitrary type expression, e.g. `-> Foo<args ...>`
86+
//
87+
// Instead of trying to figure that out, scan ahead and look for the first occurence of a `where`, a `{ ... }`, or a `;`.
88+
//
89+
// FIXME: this might still fall into a trap for something like `-> Ctor<T, const { 0 }>`. I
90+
// *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form
91+
// prefixed by e.g. `const`, so we should still be able to filter them out without having to
92+
// parse the type expression itself. But rather than try to fix things with hacks like that,
93+
// time might be better spent extending the attribute expander to suport tt-annotation atop
94+
// ast-annotated, which would be an elegant way to sidestep all of this.
95+
let mut opt_next_tt = cursor.next_ref();
96+
while let Some(next_tt) = opt_next_tt {
97+
if let TokenTree::Token(tok, _) = next_tt && tok.is_ident_named(kw::Where) {
98+
break;
99+
}
100+
if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = next_tt {
101+
break;
102+
}
103+
if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = next_tt {
104+
break;
105+
}
106+
107+
// for anything else, transcribe the tt and keep looking.
108+
new_tts.push(next_tt.clone());
109+
opt_next_tt = cursor.next_ref();
110+
continue;
111+
}
112+
113+
// At this point, we've transcribed everything from the `fn` through the formal parameter
114+
// list and return type declaration, (if any), but `tt` itself has *not* been transcribed.
115+
//
116+
// Now inject the AST contract form.
117+
//
118+
// FIXME: this kind of manual token tree munging does not have significant precedent among rustc
119+
// builtin macros, probably because most builtin macros use direct AST manipulation to accomplish
120+
// similar goals. But since our attributes need to take arbitrary expressions, and our attribute
121+
// infrastructure does not yet support mixing a token-tree annotation with an AST annotated, we
122+
// end up doing token tree manipulation.
123+
inject(&mut new_tts)?;
124+
125+
// Above we injected the internal AST requires contruct. Now copy over all the other token trees.
126+
if let Some(tt) = opt_next_tt {
127+
new_tts.push(tt.clone());
128+
}
129+
while let Some(tt) = cursor.next_ref() {
130+
new_tts.push(tt.clone());
131+
}
132+
133+
Ok(TokenStream::new(new_tts))
134+
}
135+
136+
fn expand_requires_tts(
137+
_ecx: &mut ExtCtxt<'_>,
138+
attr_span: Span,
139+
annotation: TokenStream,
140+
annotated: TokenStream,
141+
) -> Result<TokenStream, ErrorGuaranteed> {
142+
expand_injecting_circa_where_clause(
143+
_ecx,
144+
attr_span,
145+
annotated,
146+
|new_tts| {
147+
new_tts.push(TokenTree::Token(token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)),
148+
Spacing::Joint));
149+
new_tts.push(TokenTree::Delimited(DelimSpan::from_single(attr_span),
150+
DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden),
151+
token::Delimiter::Parenthesis,
152+
annotation));
153+
Ok(())
154+
},
155+
)
156+
}

0 commit comments

Comments
 (0)
Please sign in to comment.