|
2 | 2 | //! |
3 | 3 | //! Makes the enclosing context available to the specifications written inside an item. |
4 | 4 | //! |
5 | | -//! On a function, every `thrust_macros::invariant!(...)` in the body is rewritten into |
6 | | -//! its context-carrying counterpart, carrying the host signature and, for a method, the |
7 | | -//! enclosing `impl`/`trait` header, so an invariant may refer to generic- and |
8 | | -//! `Self`-typed variables that the standalone macro cannot see. That also extends the |
9 | | -//! function's where clause with the `Model` predicates for every in-scope type parameter |
10 | | -//! (and for `Self` when used), since each injected marker call instantiates a |
11 | | -//! `Model`-bounded formula function with the host's own generics. |
| 5 | +//! On a function, every `thrust_macros::invariant!(...)` and `thrust_macros::ghost!(...)` |
| 6 | +//! in the body is rewritten into its context-carrying counterpart, carrying the host |
| 7 | +//! signature and, for a method, the enclosing `impl`/`trait` header, so a formula may |
| 8 | +//! refer to generic- and `Self`-typed variables that the standalone macros cannot see. |
| 9 | +//! That also extends the function's where clause with the `Model` predicates for every |
| 10 | +//! in-scope type parameter (and for `Self` when used), since each injected marker call |
| 11 | +//! instantiates a `Model`-bounded formula function with the host's own generics. |
12 | 12 | //! |
13 | 13 | //! On an `impl`/`trait`, each method is stamped with the enclosing header — which is what |
14 | 14 | //! method-level `requires`/`ensures` read to recover the outer generics — and with this |
@@ -80,9 +80,9 @@ fn expand_outer(mut outer_item: FnOuterItem) -> TokenStream { |
80 | 80 | outer_item.into_token_stream().into() |
81 | 81 | } |
82 | 82 |
|
83 | | -/// Rewrites each `invariant!` in the body into its context-carrying counterpart and |
84 | | -/// extends the where clause with the `Model` predicates those calls need. A body naming |
85 | | -/// no invariant — or a trait method that has no body at all — is left as it is. |
| 83 | +/// Rewrites each spec macro in the body into its context-carrying counterpart and extends |
| 84 | +/// the where clause with the `Model` predicates those calls need. A body naming no spec |
| 85 | +/// macro — or a trait method that has no body at all — is left as it is. |
86 | 86 | fn expand_fn(mut func: FnItemWithSignature) -> TokenStream { |
87 | 87 | let outer = match crate::extract_outer_context(func.attrs()) { |
88 | 88 | Ok(outer) => outer, |
@@ -146,19 +146,25 @@ impl ContextInjector<'_> { |
146 | 146 |
|
147 | 147 | impl VisitMut for ContextInjector<'_> { |
148 | 148 | fn visit_macro_mut(&mut self, mac: &mut syn::Macro) { |
149 | | - if !is_invariant_macro(&mac.path) { |
| 149 | + let Some(with_context) = context_carrying_form(&mac.path) else { |
150 | 150 | return; |
151 | | - } |
| 151 | + }; |
152 | 152 | self.injected = true; |
153 | 153 | if crate::tokens_contain_ident(&mac.tokens, "Self") { |
154 | 154 | self.self_used = true; |
155 | 155 | } |
156 | 156 | mac.tokens = self.inject_context(&mac.tokens); |
157 | | - mac.path = syn::parse_quote!(::thrust_macros::_invariant_with_context); |
| 157 | + mac.path = with_context; |
158 | 158 | } |
159 | 159 | } |
160 | 160 |
|
161 | | -fn is_invariant_macro(path: &syn::Path) -> bool { |
| 161 | +/// The context-carrying counterpart of a spec macro that takes a formula over live |
| 162 | +/// variables, or `None` for any other macro. |
| 163 | +fn context_carrying_form(path: &syn::Path) -> Option<syn::Path> { |
162 | 164 | // TODO: identify the macro precisely |
163 | | - path.segments.last().is_some_and(|s| s.ident == "invariant") |
| 165 | + match path.segments.last()?.ident.to_string().as_str() { |
| 166 | + "invariant" => Some(syn::parse_quote!(::thrust_macros::_invariant_with_context)), |
| 167 | + "ghost" => Some(syn::parse_quote!(::thrust_macros::_ghost_with_context)), |
| 168 | + _ => None, |
| 169 | + } |
164 | 170 | } |
0 commit comments