Skip to content

Commit

Permalink
feat(hax-lib): intro. hax feature
Browse files Browse the repository at this point in the history
  • Loading branch information
W95Psp committed Oct 10, 2024
1 parent 0ab5ebc commit a375b7c
Show file tree
Hide file tree
Showing 14 changed files with 1,467 additions and 1,282 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ annotate-snippets = "0.11"
hax-frontend-exporter = { path = "frontend/exporter", version = "=0.1.0-alpha.1", default-features = false }
hax-adt-into = { path = "frontend/exporter/adt-into", version = "=0.1.0-alpha.1" }
hax-frontend-exporter-options = { path = "frontend/exporter/options", version = "=0.1.0-alpha.1" }
hax-lib-macros-types = { path = "hax-lib/macros/types", version = "=0.1.0-alpha.1" }
hax-lib-macros = { path = "hax-lib/macros", version = "=0.1.0-alpha.1" }
hax-lib-macros-types = { path = "hax-lib/macros/types", version = "=0.1.0-alpha.1" }
hax-lib = { path = "hax-lib", version = "=0.1.0-alpha.1" }
hax-engine-names = { path = "engine/names", version = "=0.1.0-alpha.1" }
hax-types = { path = "hax-types", version = "=0.1.0-alpha.1" }
3 changes: 2 additions & 1 deletion hax-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ num-bigint = { version = "0.4.3", default-features = false }
num-traits = { version = "0.2.15", default-features = false }

[features]
default = ["macros"]
default = ["macros", "hax"]
macros = ["dep:hax-lib-macros"]
hax = ["hax-lib-macros/hax"]

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(hax)'] }
23 changes: 11 additions & 12 deletions hax-lib/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@ description = "Hax-specific proc-macros for Rust programs"
proc-macro = true

[dependencies]
proc-macro-error = "1.0.4"
proc-macro2.workspace = true
quote.workspace = true
syn = { version = "2.0", features = [
"full",
"visit-mut",
"visit",
"extra-traits",
"parsing",
] }
hax-lib-macros-types.workspace = true
paste = "1.0.15"
proc-macro-error = { version = "1.0.4", optional = true }
proc-macro2 = { workspace = true, optional = true }
quote = { workspace = true }
syn = { version = "2.0", features = ["full", "visit-mut"] }
hax-lib-macros-types = { workspace = true, optional = true }
paste = { version = "1.0.15", optional = true }

[features]
hax = [ "dep:paste", "dep:hax-lib-macros-types", "dep:proc-macro-error", "dep:proc-macro2",
"syn/visit", "syn/extra-traits", "syn/parsing"
]

[dev-dependencies]
hax-lib = { path = ".." }
139 changes: 139 additions & 0 deletions hax-lib/macros/src/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
mod hax_paths;

use hax_paths::*;
use proc_macro::TokenStream;
use quote::quote;
use syn::{visit_mut::VisitMut, *};

macro_rules! identity_proc_macro_attribute {
($($name:ident,)*) => {
$(
#[proc_macro_attribute]
pub fn $name(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
)*
}
}

identity_proc_macro_attribute!(
fstar_options,
fstar_verification_status,
include,
exclude,
requires,
ensures,
pv_handwritten,
pv_constructor,
protocol_messages,
process_init,
process_write,
process_read,
opaque_type,
refinement_type,
fstar_replace,
coq_replace,
proverif_replace,
fstar_before,
coq_before,
proverif_before,
fstar_after,
coq_after,
proverif_after,
);

#[proc_macro]
pub fn fstar_expr(_payload: TokenStream) -> TokenStream {
quote! { () }.into()
}
#[proc_macro]
pub fn coq_expr(_payload: TokenStream) -> TokenStream {
quote! { () }.into()
}
#[proc_macro]
pub fn proverif_expr(_payload: TokenStream) -> TokenStream {
quote! { () }.into()
}

#[proc_macro_attribute]
pub fn lemma(_attr: TokenStream, _item: TokenStream) -> TokenStream {
quote! {}.into()
}

fn unsafe_expr() -> TokenStream {
quote! { ::std::compile_error!("`hax_lib::unsafe_expr` has no meaning outside of hax extraction, please use it solely on hax only places.") }.into()
}

#[proc_macro]
pub fn fstar_unsafe_expr(_payload: TokenStream) -> TokenStream {
unsafe_expr()
}
#[proc_macro]
pub fn coq_unsafe_expr(_payload: TokenStream) -> TokenStream {
unsafe_expr()
}
#[proc_macro]
pub fn proverif_unsafe_expr(_payload: TokenStream) -> TokenStream {
unsafe_expr()
}

fn not_hax_attribute(attr: &syn::Attribute) -> bool {
if let Meta::List(ml) = &attr.meta {
!matches!(expects_path_decoration(&ml.path), Ok(Some(_)))
} else {
true
}
}

fn not_refine_attribute(attr: &syn::Attribute) -> bool {
if let Meta::List(ml) = &attr.meta {
!matches!(expects_refine(&ml.path), Ok(Some(_)))
} else {
true
}
}

#[proc_macro_attribute]
pub fn attributes(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item: Item = parse_macro_input!(item);

struct AttrVisitor;

use syn::visit_mut;
impl VisitMut for AttrVisitor {
fn visit_item_trait_mut(&mut self, item: &mut ItemTrait) {
for ti in item.items.iter_mut() {
if let TraitItem::Fn(fun) = ti {
fun.attrs.retain(not_hax_attribute)
}
}
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 {
fun.attrs.retain(not_hax_attribute)
}
}
visit_mut::visit_item_impl_mut(self, item);
}
fn visit_item_mut(&mut self, item: &mut Item) {
visit_mut::visit_item_mut(self, item);

match item {
Item::Struct(s) => {
for field in s.fields.iter_mut() {
field.attrs.retain(not_refine_attribute)
}
}
_ => (),
}
}
}

let mut item = item;
AttrVisitor.visit_item_mut(&mut item);

quote! { #item }.into()
}
55 changes: 55 additions & 0 deletions hax-lib/macros/src/hax_paths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! This module defines the `ImplFnDecoration` structure and utils
//! around it.
use syn::spanned::Spanned;
use syn::*;

fn expect_simple_path(path: &Path) -> Option<Vec<String>> {
let mut chunks = vec![];
if path.leading_colon.is_some() {
chunks.push(String::new())
}
for segment in &path.segments {
chunks.push(format!("{}", segment.ident));
if !matches!(segment.arguments, PathArguments::None) {
return None;
}
}
Some(chunks)
}

/// The various strings allowed as decoration kinds.
pub const DECORATION_KINDS: &[&str] = &["decreases", "ensures", "requires"];

/// Expects a `Path` to be a decoration kind: `::hax_lib::<KIND>`,
/// `hax_lib::<KIND>` or `<KIND>` in (with `KIND` in
/// `DECORATION_KINDS`).
pub fn expects_path_decoration(path: &Path) -> Result<Option<String>> {
expects_hax_path(DECORATION_KINDS, path)
}

/// Expects a path to be `[[::]hax_lib]::refine`
pub fn expects_refine(path: &Path) -> Result<Option<String>> {
expects_hax_path(&["refine"], path)
}

/// Expects a `Path` to be a hax path: `::hax_lib::<KW>`,
/// `hax_lib::<KW>` or `<KW>` in (with `KW` in `allowlist`).
pub fn expects_hax_path(allowlist: &[&str], path: &Path) -> Result<Option<String>> {
let path_span = path.span();
let path = expect_simple_path(path)
.ok_or_else(|| Error::new(path_span, "Expected a simple path, with no `<...>`."))?;
Ok(
match path
.iter()
.map(|x| x.as_str())
.collect::<Vec<_>>()
.as_slice()
{
[kw] | ["", "hax_lib", kw] | ["hax_lib", kw] if allowlist.contains(kw) => {
Some(kw.to_string())
}
_ => None,
},
)
}
26 changes: 0 additions & 26 deletions hax-lib/macros/src/impl_fn_decoration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,6 @@ pub struct ImplFnDecoration {
pub self_ty: Type,
}

/// The various strings allowed as decoration kinds.
pub const DECORATION_KINDS: &[&str] = &["decreases", "ensures", "requires"];

/// Expects a `Path` to be a decoration kind: `::hax_lib::<KIND>`,
/// `hax_lib::<KIND>` or `<KIND>` in (with `KIND` in
/// `DECORATION_KINDS`).
pub fn expects_path_decoration(path: &Path) -> Result<Option<String>> {
let path_span = path.span();
let path = path
.expect_simple_path()
.ok_or_else(|| Error::new(path_span, "Expected a simple path, with no `<...>`."))?;
Ok(
match path
.iter()
.map(|x| x.as_str())
.collect::<Vec<_>>()
.as_slice()
{
[kw] | ["", "hax_lib", kw] | ["hax_lib", kw] if DECORATION_KINDS.contains(kw) => {
Some(kw.to_string())
}
_ => None,
},
)
}

impl parse::Parse for ImplFnDecoration {
fn parse(input: parse::ParseStream) -> Result<Self> {
let parse_next = || -> Result<_> {
Expand Down
Loading

0 comments on commit a375b7c

Please sign in to comment.