mod hax_paths;
mod impl_fn_decoration;
mod quote;
mod rewrite_self;
mod syn_ext;
mod utils;
mod prelude {
pub use crate::hax_paths::*;
pub use crate::syn_ext::*;
pub use proc_macro as pm;
pub use proc_macro_error2::*;
pub use proc_macro2::*;
pub use quote::*;
pub use std::collections::HashSet;
pub use syn::spanned::Spanned;
pub use syn::{visit_mut::VisitMut, *};
pub use AttrPayload::Language as AttrHaxLang;
pub use hax_lib_macros_types::*;
pub type FnLike = syn::ImplItemFn;
}
use impl_fn_decoration::*;
use prelude::*;
use utils::*;
#[proc_macro_error]
#[proc_macro_attribute]
pub fn fstar_options(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: TokenStream = item.into();
let lit_str = parse_macro_input!(attr as LitStr);
let payload = format!(r#"#push-options "{}""#, lit_str.value());
let payload = LitStr::new(&payload, lit_str.span());
quote! {
#[::hax_lib::fstar::before(#payload)]
#[::hax_lib::fstar::after(r#"#pop-options"#)]
#item
}
.into()
}
#[proc_macro]
pub fn loop_invariant(predicate: pm::TokenStream) -> pm::TokenStream {
let predicate2: TokenStream = predicate.clone().into();
let predicate_expr: syn::Expr = parse_macro_input!(predicate);
let (invariant_f, predicate) = match predicate_expr {
syn::Expr::Closure(_) => (quote!(hax_lib::_internal_loop_invariant), predicate2),
_ => (
quote!(hax_lib::_internal_while_loop_invariant),
quote!(::hax_lib::Prop::from(#predicate2)),
),
};
let ts: pm::TokenStream = quote! {
#[cfg(#HaxCfgOptionName)]
{
#invariant_f({
#HaxQuantifiers
#predicate
})
}
}
.into();
ts
}
#[proc_macro]
pub fn loop_decreases(predicate: pm::TokenStream) -> pm::TokenStream {
let predicate: TokenStream = predicate.into();
let ts: pm::TokenStream = quote! {
#[cfg(#HaxCfgOptionName)]
{
hax_lib::_internal_loop_decreases({
#HaxQuantifiers
use ::hax_lib::int::ToInt;
(#predicate).to_int()
})
}
}
.into();
ts
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn fstar_verification_status(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let action = format!("{}", parse_macro_input!(attr as Ident));
match action.as_str() {
"lax" => {
let item: TokenStream = item.into();
quote! {
#[::hax_lib::fstar::options("--admit_smt_queries true")]
#item
}
}
"panic_free" => {
let mut item = parse_macro_input!(item as FnLike);
if let Some(last) = item
.block
.stmts
.iter_mut()
.rev()
.find(|stmt| matches!(stmt, syn::Stmt::Expr(_, None)))
.as_mut()
{
**last = syn::Stmt::Expr(
parse_quote! {
{let result = #last;
::hax_lib::fstar!("_hax_panic_freedom_admit_");
result}
},
None,
);
} else {
item.block.stmts.push(syn::Stmt::Expr(
parse_quote! {::hax_lib::fstar!("_hax_panic_freedom_admit_")},
None,
));
}
quote! {
#item
}
}
_ => abort_call_site!(format!("Expected `lax` or `panic_free`")),
}
.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn fstar_postprocess_with(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: TokenStream = item.into();
let payload: String = if let Ok(s) = syn::parse::<LitStr>(attr.clone()) {
s.value()
} else {
let e = parse_macro_input!(attr as Expr);
format!(" ${{ {} }} ", e.to_token_stream())
};
let payload = format!("[@@FStar.Tactics.postprocess_with ({payload})]");
let payload: Lit = Lit::Str(syn::LitStr::new(&payload, Span::call_site()));
quote! {#[::hax_lib::fstar::before(#payload)] #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn include(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: TokenStream = item.into();
let _ = parse_macro_input!(attr as parse::Nothing);
let attr = AttrPayload::ItemStatus(ItemStatus::Included { late_skip: false });
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn exclude(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: TokenStream = item.into();
let _ = parse_macro_input!(attr as parse::Nothing);
let attr = AttrPayload::ItemStatus(ItemStatus::Excluded { modeled_by: None });
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn lemma(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let mut item: syn::ItemFn = parse_macro_input!(item as ItemFn);
use syn::{GenericArgument, PathArguments, ReturnType, spanned::Spanned};
fn add_allow_unused_variables_to_args(func: &mut syn::ItemFn) {
let attr: syn::Attribute = parse_quote!(#[allow(unused_variables)]);
for input in &mut func.sig.inputs {
if let FnArg::Typed(pat_type) = input {
pat_type.attrs.push(attr.clone());
}
}
}
fn parse_proof_type(r#type: syn::Type) -> Option<syn::Expr> {
let syn::Type::Path(syn::TypePath {
qself: None,
path:
syn::Path {
leading_colon: None,
segments,
},
}) = r#type
else {
return None;
};
let ps = (segments.len() == 1).then_some(()).and(segments.first())?;
(ps.ident == "Proof").then_some(())?;
let PathArguments::AngleBracketed(args) = &ps.arguments else {
None?
};
let args = args.args.clone();
let GenericArgument::Const(e) = (args.len() == 1).then_some(()).and(args.first())? else {
None?
};
Some(e.clone())
}
let _ = parse_macro_input!(attr as parse::Nothing);
let attr = &AttrPayload::Lemma;
add_allow_unused_variables_to_args(&mut item);
if let ReturnType::Type(_, r#type) = &item.sig.output {
if let Some(ensures_clause) = parse_proof_type(*r#type.clone()) {
use AttrPayload::NeverErased;
item.sig.output = ReturnType::Default;
return ensures(
quote! {|_| #ensures_clause}.into(),
quote! { #attr #NeverErased #item }.into(),
);
}
}
abort!(
item.sig.output.span(),
"A lemma is expected to return a `Proof<{STATEMENT}>`, where {STATEMENT} is a `Prop` expression."
)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn decreases(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let phi: syn::Expr = parse_macro_input!(attr);
let item: FnLike = parse_macro_input!(item);
let (requires, attr) = make_fn_decoration(
phi,
item.sig.clone(),
FnDecorationKind::Decreases,
None,
None,
);
quote! {#requires #attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn fstar_smt_pat(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let phi: syn::Expr = parse_macro_input!(attr);
let item: FnLike = parse_macro_input!(item);
let (requires, attr) =
make_fn_decoration(phi, item.sig.clone(), FnDecorationKind::SMTPat, None, None);
quote! {#requires #attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn requires(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let phi: syn::Expr = parse_macro_input!(attr);
let item: FnLike = parse_macro_input!(item);
let (requires, attr) = make_fn_decoration(
phi.clone(),
item.sig.clone(),
FnDecorationKind::Requires,
None,
None,
);
let mut item_with_debug = item.clone();
item_with_debug
.block
.stmts
.insert(0, parse_quote! {debug_assert!(#phi);});
quote! {
#requires #attr
#item
}
.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn ensures(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let ExprClosure1 {
arg: ret_binder,
body: phi,
} = parse_macro_input!(attr);
let item: FnLike = parse_macro_input!(item);
let kind = FnDecorationKind::Ensures {
ret_binder: ret_binder.clone(),
};
let (ensures, attr) = make_fn_decoration(phi.clone(), item.sig.clone(), kind, None, None);
let mut item_with_debug = item.clone();
let body = item.block.clone();
item_with_debug.block.stmts =
parse_quote!(let #ret_binder = #body; debug_assert!(#phi); #ret_binder);
quote! {
#ensures #attr
#item
}
.into()
}
mod kw {
syn::custom_keyword!(hax_lib);
syn::custom_keyword!(decreases);
syn::custom_keyword!(ensures);
syn::custom_keyword!(requires);
syn::custom_keyword!(refine);
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn impl_fn_decoration(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let ImplFnDecoration {
kind,
phi,
generics,
self_ty,
} = parse_macro_input!(attr);
let mut item: FnLike = parse_macro_input!(item);
let (decoration, attr) =
make_fn_decoration(phi, item.sig.clone(), kind, Some(generics), Some(self_ty));
let decoration = Stmt::Item(Item::Verbatim(decoration));
item.block.stmts.insert(0, decoration);
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn trait_fn_decoration(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let ImplFnDecoration {
kind,
phi,
generics,
self_ty,
} = parse_macro_input!(attr);
let mut item: syn::TraitItemFn = parse_macro_input!(item);
let (decoration, attr) =
make_fn_decoration(phi, item.sig.clone(), kind, Some(generics), Some(self_ty));
let decoration = Stmt::Item(Item::Verbatim(decoration));
item.sig
.generics
.where_clause
.get_or_insert(parse_quote! {where})
.predicates
.push(parse_quote! {[(); {#decoration 0}]:});
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn attributes(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: Item = parse_macro_input!(item);
#[derive(Default)]
struct AttrVisitor {
extra_items: Vec<TokenStream>,
}
use syn::visit_mut;
impl VisitMut for AttrVisitor {
fn visit_item_trait_mut(&mut self, item: &mut ItemTrait) {
let span = item.span();
for ti in item.items.iter_mut() {
if let TraitItem::Fn(fun) = ti {
for attr in &mut fun.attrs {
let Meta::List(ml) = attr.meta.clone() else {
continue;
};
let Ok(Some(decoration)) = expects_path_decoration(&ml.path) else {
continue;
};
let decoration = syn::Ident::new(&decoration, ml.path.span());
let mut generics = item.generics.clone();
let predicate = WherePredicate::Type(PredicateType {
lifetimes: None,
bounded_ty: parse_quote! {Self_},
colon_token: Token,
bounds: item.supertraits.clone(),
});
let mut where_clause = generics
.where_clause
.clone()
.unwrap_or(parse_quote! {where});
where_clause.predicates.push(predicate.clone());
generics.where_clause = Some(where_clause.clone());
let self_ty: Type = parse_quote! {Self_};
let tokens = ml.tokens.clone();
let generics = merge_generics(parse_quote! {<Self_>}, generics);
let ImplFnDecoration {
kind, phi, self_ty, ..
} = parse_quote! {#decoration, #generics, where, #self_ty, #tokens};
let (decoration, relation_attr) = make_fn_decoration(
phi,
fun.sig.clone(),
kind,
Some(generics),
Some(self_ty),
);
*attr = parse_quote! {#relation_attr};
self.extra_items.push(decoration);
}
}
}
visit_mut::visit_item_trait_mut(self, item);
}
fn visit_type_mut(&mut self, _type: &mut Type) {}
fn visit_item_impl_mut(&mut self, item: &mut ItemImpl) {
for ii in item.items.iter_mut() {
if let ImplItem::Fn(fun) = ii {
for attr in fun.attrs.iter_mut() {
if let Meta::List(ml) = &mut attr.meta {
let Ok(Some(decoration)) = expects_path_decoration(&ml.path) else {
continue;
};
let decoration = syn::Ident::new(&decoration, ml.path.span());
let tokens = ml.tokens.clone();
let (generics, self_ty) = (&item.generics, &item.self_ty);
let where_clause = &generics.where_clause;
ml.tokens =
quote! {#decoration, #generics, #where_clause, #self_ty, #tokens};
ml.path = parse_quote! {::hax_lib::impl_fn_decoration};
}
}
}
}
visit_mut::visit_item_impl_mut(self, item);
}
fn visit_fields_named_mut(&mut self, fields_named: &mut FieldsNamed) {
visit_mut::visit_fields_named_mut(self, fields_named);
fn handle_reorder_attribute(attrs: &mut [Attribute], errors: &mut Vec<TokenStream>) {
let Some((attr, order)) = attrs.iter_mut().find_map(|attr| {
if let Ok(Some(_)) = expects_order(attr.path()) {
let lit: LitInt = attr.parse_args().ok()?;
Some((attr, lit))
} else {
None
}
}) else {
return;
};
let Ok(n) = order.base10_parse() else {
errors.push(parse_quote!{const _: () = {compile_error!("Expected a (base 10) i32 literal.")};});
return;
};
let payload = AttrPayload::Order(n);
*attr = parse_quote!(#payload);
}
for field in &mut fields_named.named {
handle_reorder_attribute(&mut field.attrs, &mut self.extra_items);
}
}
fn visit_item_mut(&mut self, item: &mut Item) {
visit_mut::visit_item_mut(self, item);
let mut extra: Vec<Item> = vec![];
match item {
Item::Struct(s) => {
let only_one_field = s.fields.len() == 1;
let idents: Vec<_> = s
.fields
.iter()
.enumerate()
.map(|(i, field)| {
let ident = field.ident.clone().unwrap_or(if only_one_field {
format_ident!("x")
} else {
format_ident!("x{}", i)
});
(ident, field.ty.clone())
})
.collect();
for (i, field) in s.fields.iter_mut().enumerate() {
let prev = &idents[0..=i];
let refine: Option<(&mut Attribute, Expr)> =
field.attrs.iter_mut().find_map(|attr| {
if let Ok(Some(_)) = expects_refine(attr.path()) {
let payload = attr.parse_args().ok()?;
Some((attr, payload))
} else {
None
}
});
if let Some((attr, refine)) = refine {
let binders: TokenStream = prev
.iter()
.map(|(name, ty)| quote! {#name: #ty, })
.collect();
let uid = ItemUid::fresh();
let uid_attr = AttrPayload::Uid(uid.clone());
let assoc_attr = AttrPayload::AssociatedItem {
role: AssociationRole::Refine,
item: uid,
};
*attr = syn::parse_quote! { #assoc_attr };
let status_attr =
&AttrPayload::ItemStatus(ItemStatus::Included { late_skip: true });
extra.push(syn::parse_quote! {
#[cfg(#HaxCfgOptionName)]
#status_attr
const _: () = {
#uid_attr
#status_attr
fn refinement(#binders) -> ::hax_lib::Prop { ::hax_lib::Prop::from(#refine) }
};
})
}
}
}
_ => (),
}
let extra: TokenStream = extra.iter().map(|extra| quote! {#extra}).collect();
*item = Item::Verbatim(quote! {#extra #item});
}
}
let mut v = AttrVisitor::default();
let mut item = item;
v.visit_item_mut(&mut item);
let extra_items = v.extra_items;
quote! { #item #(#extra_items)* }.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
#[deprecated(note = "Please use 'opaque' instead")]
pub fn opaque_type(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
opaque(attr, item)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn opaque(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: Item = parse_macro_input!(item);
let attr = AttrPayload::Erased;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn transparent(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: Item = parse_macro_input!(item);
let attr = AttrPayload::NeverErased;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn process_read(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProcessRead;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn process_write(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProcessWrite;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn process_init(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProcessInit;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn protocol_messages(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemEnum = parse_macro_input!(item);
let attr = AttrPayload::ProtocolMessages;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn pv_constructor(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::PVConstructor;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn pv_handwritten(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::PVHandwritten;
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro]
pub fn int(payload: pm::TokenStream) -> pm::TokenStream {
let n: LitInt = parse_macro_input!(payload);
let suffix = n.suffix();
if !suffix.is_empty() {
abort_call_site!("The literal suffix `{suffix}` was unexpected.")
}
let digits = n.base10_digits();
quote! {::hax_lib::int::Int::_unsafe_from_str(#digits)}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn lean_proof(payload: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let payload = parse_macro_input!(payload as LitStr).value();
let attr = AttrPayload::Proof(payload);
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn lean_pure_requires_proof(
payload: pm::TokenStream,
item: pm::TokenStream,
) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let payload = parse_macro_input!(payload as LitStr).value();
let attr = AttrPayload::PureRequiresProof(payload);
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn lean_pure_ensures_proof(payload: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let payload = parse_macro_input!(payload as LitStr).value();
let attr = AttrPayload::PureEnsuresProof(payload);
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn lean_proof_method_grind(_attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProofMethod(hax_lib_macros_types::ProofMethod::Grind);
quote! {#attr #item}.into()
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn lean_proof_method_bv_decide(
_attr: pm::TokenStream,
item: pm::TokenStream,
) -> pm::TokenStream {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProofMethod(hax_lib_macros_types::ProofMethod::BvDecide);
quote! {#attr #item}.into()
}
macro_rules! make_quoting_item_proc_macro {
($backend:ident, $macro_name:ident, $position:expr, $cfg_name:ident) => {
#[doc = concat!("This macro inlines verbatim ", stringify!($backend)," code before a Rust item.")]
#[proc_macro_error]
#[proc_macro_attribute]
pub fn $macro_name(payload: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let mut fstar_options = None;
let item: TokenStream = item.into();
let payload = {
let mut tokens = payload.into_iter().peekable();
if let Some(pm::TokenTree::Ident(ident)) = tokens.peek() {
let ident_str = format!("{}", ident);
fstar_options = Some(ItemQuoteFStarOpts {
intf: ident_str == "interface" || ident_str == "both",
r#impl: ident_str == "impl" || ident_str == "both",
});
if !matches!(ident_str.as_str(), "impl" | "both" | "interface") {
proc_macro_error2::abort!(
ident.span(),
"Expected `impl`, `both` or `interface`"
);
}
let _ = tokens.next();
let comma = pm::TokenStream::from_iter(tokens.next().into_iter());
let _: syn::token::Comma = parse_macro_input!(comma);
}
pm::TokenStream::from_iter(tokens)
};
let ts: TokenStream = quote::item(
ItemQuote {
position: $position,
fstar_options,
},
quote! {#[cfg($cfg_name)]},
payload,
quote! {#item}.into(),
)
.into();
ts.into()
}
};
}
macro_rules! make_quoting_proc_macro {
($backend:ident) => {
#[doc = concat!("Embed ", stringify!($backend), " expression inside a Rust expression. This macro takes only one argument: some raw ", stringify!($backend), " code as a string literal.")]
#[proc_macro]
pub fn ${concat($backend, _expr)}(payload: pm::TokenStream) -> pm::TokenStream {
let ts: TokenStream = quote::expression(quote::InlineExprType::Unit, payload).into();
quote!{{
#[cfg(${concat(hax_backend_, $backend)})]
{
#ts
}
}}.into()
}
#[doc = concat!("The `Prop` version of `", stringify!($backend), "_expr`.")]
#[proc_macro]
pub fn ${concat($backend, _prop_expr)}(payload: pm::TokenStream) -> pm::TokenStream {
let ts: TokenStream = quote::expression(quote::InlineExprType::Prop, payload).into();
quote!{{
#[cfg(${concat(hax_backend_, $backend)})]
{
#ts
}
#[cfg(not(${concat(hax_backend_, $backend)}))]
{
::hax_lib::Prop::from_bool(true)
}
}}.into()
}
#[doc = concat!("The unsafe (because polymorphic: even computationally relevant code can be inlined!) version of `", stringify!($backend), "_expr`.")]
#[proc_macro]
#[doc(hidden)]
pub fn ${concat($backend, _unsafe_expr)}(payload: pm::TokenStream) -> pm::TokenStream {
let ts: TokenStream = quote::expression(quote::InlineExprType::Anything, payload).into();
quote!{{
#[cfg(${concat(hax_backend_, $backend)})]
{
#ts
}
}}.into()
}
make_quoting_item_proc_macro!($backend, ${concat($backend, _before)}, ItemQuotePosition::Before, ${concat(hax_backend_, $backend)});
make_quoting_item_proc_macro!($backend, ${concat($backend, _after)}, ItemQuotePosition::After, ${concat(hax_backend_, $backend)});
#[doc = concat!("Replaces a Rust item with some verbatim ", stringify!($backend)," code.")]
#[proc_macro_error]
#[proc_macro_attribute]
pub fn ${concat($backend, _replace)}(payload: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let item: TokenStream = item.into();
let payload: TokenStream = payload.into();
let attr = AttrPayload::ItemStatus(ItemStatus::Included { late_skip: true });
quote! {
#[cfg(${concat(hax_backend_, $backend)})]
#[::hax_lib::$backend::before(#payload)]
#attr
#item
#[cfg(not(${concat(hax_backend_, $backend)}))]
#item
}
.into()
}
#[doc = concat!("Replaces the body of a Rust function with some verbatim ", stringify!($backend)," code.")]
#[proc_macro_error]
#[proc_macro_attribute]
pub fn ${concat($backend, _replace_body)}(payload: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let payload: TokenStream = payload.into();
let item: ItemFn = parse_macro_input!(item);
let mut hax_item = item.clone();
*hax_item.block.as_mut() = parse_quote!{
{
::hax_lib::$backend::unsafe_expr!(#payload)
}
};
quote!{
#[cfg(${concat(hax_backend_, $backend)})]
#hax_item
#[cfg(not(${concat(hax_backend_, $backend)}))]
#item
}.into()
}
};
($($backend:ident)*) => {
$(make_quoting_proc_macro!($backend);)*
}
}
make_quoting_proc_macro!(fstar coq proverif lean);
#[proc_macro_error]
#[proc_macro_attribute]
pub fn refinement_type(mut attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
let mut item = parse_macro_input!(item as syn::ItemStruct);
let syn::Fields::Unnamed(fields) = &item.fields else {
proc_macro_error2::abort!(
item.generics.span(),
"Expected a newtype (a struct with one unnamed field), got one or more named field"
);
};
let paren_token = fields.paren_token;
let fields = fields.unnamed.iter().collect::<Vec<_>>();
let [field] = &fields[..] else {
proc_macro_error2::abort!(
item.generics.span(),
"Expected a newtype (a struct with one unnamed field), got {} fields",
fields.len()
);
};
if !matches!(field.vis, syn::Visibility::Inherited) {
proc_macro_error2::abort!(field.vis.span(), "This field was expected to be private");
}
let no_debug_assert = {
let mut tokens = attr.clone().into_iter();
if let (Some(pm::TokenTree::Ident(ident)), Some(pm::TokenTree::Punct(comma))) =
(tokens.next(), tokens.next())
{
if ident.to_string() != "no_debug_runtime_check" {
proc_macro_error2::abort!(ident.span(), "Expected 'no_debug_runtime_check'");
}
if comma.as_char() != ',' {
proc_macro_error2::abort!(ident.span(), "Expected a comma");
}
attr = pm::TokenStream::from_iter(tokens);
true
} else {
false
}
};
let ExprClosure1 {
arg: ret_binder,
body: phi,
} = parse_macro_input!(attr);
let kind = FnDecorationKind::Ensures {
ret_binder: ret_binder.clone(),
};
let sig = syn::Signature {
constness: None,
asyncness: None,
unsafety: None,
abi: None,
variadic: None,
fn_token: syn::Token),
ident: parse_quote! {dummy},
generics: item.generics.clone(),
paren_token,
inputs: syn::punctuated::Punctuated::new(),
output: syn::ReturnType::Type(parse_quote! {->}, Box::new(field.ty.clone())),
};
let ident = &item.ident;
let generics = &item.generics;
let vis = item.vis.clone();
let generics_args: syn::punctuated::Punctuated<_, syn::token::Comma> = item
.generics
.params
.iter()
.map(|g| match g {
syn::GenericParam::Lifetime(p) => {
let i = &p.lifetime;
quote! { #i }
}
syn::GenericParam::Type(p) => {
let i = &p.ident;
quote! { #i }
}
syn::GenericParam::Const(p) => {
let i = &p.ident;
quote! { #i }
}
})
.collect();
let inner_ty = &field.ty;
let (refinement_item, refinement_attr) = make_fn_decoration(phi.clone(), sig, kind, None, None);
let module_ident = syn::Ident::new(
&format!("hax__autogenerated_refinement__{}", ident),
ident.span(),
);
item.vis = parse_quote! {pub};
let debug_assert =
no_debug_assert.then_some(quote! {::core::debug_assert!(Self::invariant(x.clone()));});
let newtype_as_ref_attr = AttrPayload::NewtypeAsRefinement;
quote! {
#[allow(non_snake_case)]
mod #module_ident {
#[allow(unused_imports)]
use super::*;
#refinement_item
#newtype_as_ref_attr
#refinement_attr
#item
#[::hax_lib::exclude]
impl #generics ::hax_lib::Refinement for #ident <#generics_args> {
type InnerType = #inner_ty;
fn new(x: Self::InnerType) -> Self {
#debug_assert
Self(x)
}
fn get(self) -> Self::InnerType {
self.0
}
fn get_mut(&mut self) -> &mut Self::InnerType {
&mut self.0
}
fn invariant(#ret_binder: Self::InnerType) -> ::hax_lib::Prop {
::hax_lib::Prop::from(#phi)
}
}
#[::hax_lib::exclude]
impl #generics ::std::ops::Deref for #ident <#generics_args> {
type Target = #inner_ty;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[::hax_lib::exclude]
impl #generics ::hax_lib::RefineAs<#ident <#generics_args>> for #inner_ty {
fn into_checked(self) -> #ident <#generics_args> {
use ::hax_lib::Refinement;
#ident::new(self)
}
}
}
#vis use #module_ident::#ident;
}
.into()
}