#![cfg_attr(hax, feature(macro_metavar_expr_concat))]
mod hax_paths;
#[cfg(hax)]
mod impl_fn_decoration;
#[cfg(hax)]
mod implementation;
#[cfg(hax)]
mod quote;
#[cfg(hax)]
mod rewrite_self;
#[cfg(hax)]
mod syn_ext;
#[cfg(hax)]
mod utils;
#[cfg(hax)]
mod prelude {
pub use crate::hax_paths::*;
pub use crate::syn_ext::*;
pub use proc_macro as pm;
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;
}
#[cfg(not(hax))]
mod dummy;
use proc_macro::TokenStream;
macro_rules! passthrough_attributes {
($($(#[$meta:meta])* $name:ident;)*) => {
$(
$(#[$meta])*
#[proc_macro_attribute]
pub fn $name(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$name(attr, item) }
#[cfg(not(hax))]
{ let _ = attr; item }
}
)*
};
}
macro_rules! attribute_macros {
($($(#[$meta:meta])* fn $name:ident($attr:ident, $item:ident) $body:block)*) => {
$(
$(#[$meta])*
#[proc_macro_attribute]
pub fn $name($attr: TokenStream, $item: TokenStream) -> TokenStream {
#[cfg(hax)]
{
#[allow(unused_imports)]
use crate::{
impl_fn_decoration::*, prelude::*, rewrite_self::SelfProjection, utils::*,
};
$body
}
#[cfg(not(hax))]
{ let _ = $attr; $item }
}
)*
};
}
passthrough_attributes! {
fstar_verification_status;
ensures;
ensures_ref;
#[deprecated(note = "Please use 'opaque' instead")]
opaque_type;
opaque;
refinement_type;
}
attribute_macros! {
fn fstar_options(attr, item) {
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()
}
fn fstar_postprocess_with(attr, item) {
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()
}
fn fstar_smt_pat(attr, item) {
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,
SelfProjection::Unknown,
);
quote! {#requires #attr #item}.into()
}
fn include(attr, item) {
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()
}
fn exclude(attr, item) {
let item: TokenStream = item.into();
let _ = parse_macro_input!(attr as parse::Nothing);
let attr = AttrPayload::ItemStatus(ItemStatus::Excluded { modeled_by: None });
let charon = charon_attr(quote! {exclude});
quote! {#attr #charon #item}.into()
}
fn decreases(attr, item) {
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,
SelfProjection::Unknown,
);
quote! {#requires #attr #item}.into()
}
fn requires(attr, item) {
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,
SelfProjection::Unknown,
);
let mut item_with_debug = item.clone();
item_with_debug
.block
.stmts
.insert(0, parse_quote! {debug_assert!(#phi);});
quote! {
#requires #attr
#item
}
.into()
}
fn transparent(_attr, item) {
let item: Item = parse_macro_input!(item);
let attr = AttrPayload::NeverErased;
quote! {#attr #item}.into()
}
fn process_read(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProcessRead;
quote! {#attr #item}.into()
}
fn process_write(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProcessWrite;
quote! {#attr #item}.into()
}
fn process_init(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProcessInit;
quote! {#attr #item}.into()
}
fn protocol_messages(_attr, item) {
let item: ItemEnum = parse_macro_input!(item);
let attr = AttrPayload::ProtocolMessages;
quote! {#attr #item}.into()
}
fn pv_constructor(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::PVConstructor;
quote! {#attr #item}.into()
}
fn pv_handwritten(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::PVHandwritten;
quote! {#attr #item}.into()
}
fn legacy_lean_proof(payload, item) {
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()
}
fn legacy_lean_pure_requires_proof(payload, item) {
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()
}
fn legacy_lean_pure_ensures_proof(payload, item) {
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()
}
fn legacy_lean_proof_method_grind(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProofMethod(hax_lib_macros_types::ProofMethod::Grind);
quote! {#attr #item}.into()
}
fn legacy_lean_proof_method_bv_decide(_attr, item) {
let item: ItemFn = parse_macro_input!(item);
let attr = AttrPayload::ProofMethod(hax_lib_macros_types::ProofMethod::BvDecide);
quote! {#attr #item}.into()
}
}
#[proc_macro_attribute]
pub fn lemma(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::lemma(attr, item)
}
#[cfg(not(hax))]
{
let _ = (attr, item);
TokenStream::new()
}
}
#[proc_macro_attribute]
pub fn attributes(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::attributes(attr, item)
}
#[cfg(not(hax))]
{
let _ = attr;
dummy::attributes(item)
}
}
#[proc_macro]
pub fn int(payload: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::int(payload)
}
#[cfg(not(hax))]
{
dummy::int(payload)
}
}
#[proc_macro]
pub fn loop_invariant(predicate: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::loop_invariant(predicate)
}
#[cfg(not(hax))]
{
let _ = predicate;
TokenStream::new()
}
}
#[proc_macro]
pub fn loop_decreases(predicate: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::loop_decreases(predicate)
}
#[cfg(not(hax))]
{
let _ = predicate;
TokenStream::new()
}
}
#[proc_macro_attribute]
pub fn impl_fn_decoration(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::impl_fn_decoration(attr, item)
}
#[cfg(not(hax))]
{
let _ = (attr, item);
dummy::internal_macro_misuse("impl_fn_decoration")
}
}
#[proc_macro_attribute]
pub fn trait_fn_decoration(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{
implementation::trait_fn_decoration(attr, item)
}
#[cfg(not(hax))]
{
let _ = (attr, item);
dummy::internal_macro_misuse("trait_fn_decoration")
}
}
macro_rules! item_quoting_proc_macros {
($backend:ident, $(($name:ident, $position:literal)),*) => {$(
#[doc = concat!("This macro inlines verbatim ", stringify!($backend), " code ", $position, " a Rust item.")]
#[proc_macro_attribute]
pub fn $name(payload: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$name(payload, item) }
#[cfg(not(hax))]
{ let _ = payload; item }
}
)*};
}
macro_rules! quoting_proc_macros {
($backend:ident, $expr:ident, $prop_expr:ident, $unsafe_expr:ident,
$before:ident, $after:ident, $replace:ident, $replace_body: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 $expr(payload: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$expr(payload) }
#[cfg(not(hax))]
{ let _ = payload; dummy::unit_expr() }
}
#[doc = concat!("The `Prop` version of `", stringify!($backend), "_expr`.")]
#[proc_macro]
pub fn $prop_expr(payload: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$prop_expr(payload) }
#[cfg(not(hax))]
{ let _ = payload; dummy::prop_expr() }
}
#[doc = concat!("The unsafe (because polymorphic: even computationally relevant code can be inlined!) version of `", stringify!($backend), "_expr`.")]
#[proc_macro]
#[doc(hidden)]
pub fn $unsafe_expr(payload: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$unsafe_expr(payload) }
#[cfg(not(hax))]
{ let _ = payload; dummy::unsafe_expr() }
}
item_quoting_proc_macros!($backend, ($before, "before"), ($after, "after"));
#[doc = concat!("Replaces a Rust item with some verbatim ", stringify!($backend)," code.")]
#[proc_macro_attribute]
pub fn $replace(payload: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$replace(payload, item) }
#[cfg(not(hax))]
{ let _ = payload; item }
}
#[doc = concat!("Replaces the body of a Rust function with some verbatim ", stringify!($backend)," code.")]
#[proc_macro_attribute]
pub fn $replace_body(payload: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(hax)]
{ implementation::$replace_body(payload, item) }
#[cfg(not(hax))]
{ let _ = payload; item }
}
};
}
quoting_proc_macros!(
fstar,
fstar_expr,
fstar_prop_expr,
fstar_unsafe_expr,
fstar_before,
fstar_after,
fstar_replace,
fstar_replace_body
);
quoting_proc_macros!(
coq,
coq_expr,
coq_prop_expr,
coq_unsafe_expr,
coq_before,
coq_after,
coq_replace,
coq_replace_body
);
quoting_proc_macros!(
proverif,
proverif_expr,
proverif_prop_expr,
proverif_unsafe_expr,
proverif_before,
proverif_after,
proverif_replace,
proverif_replace_body
);
quoting_proc_macros!(
legacy_lean,
legacy_lean_expr,
legacy_lean_prop_expr,
legacy_lean_unsafe_expr,
legacy_lean_before,
legacy_lean_after,
legacy_lean_replace,
legacy_lean_replace_body
);