Skip to content

Commit bea9074

Browse files
authored
Merge pull request #847 from hacspec/fix-844
Fix ensures for unit-returning functions with &mut inputs
2 parents e8fbc1f + 39e78de commit bea9074

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

hax-lib-macros/src/utils.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,19 @@ fn create_future_ident(name: &str) -> syn::Ident {
201201
proc_macro2::Ident::new(&format!("{name}_future"), proc_macro2::Span::call_site())
202202
}
203203

204+
/// The engine translates functions of arity zero to functions that
205+
/// takes exactly one unit argument. The zero-arity functions we
206+
/// generate are translated correctly as well. But in the case of a
207+
/// `ensures` clause, that's an issue: we produce a function of arity
208+
/// one, whose first argument is the result of the function. Instead,
209+
/// we need a function of arity two.
210+
/// `fix_signature_arity` adds a `unit` if needed.
211+
fn add_unit_to_sig_if_needed(signature: &mut Signature) {
212+
if signature.inputs.is_empty() {
213+
signature.inputs.push(parse_quote! {_: ()})
214+
}
215+
}
216+
204217
/// Common logic when generating a function decoration
205218
pub fn make_fn_decoration(
206219
mut phi: Expr,
@@ -223,12 +236,13 @@ pub fn make_fn_decoration(
223236
};
224237
let decoration = {
225238
let decoration_sig = {
226-
let mut sig = signature;
239+
let mut sig = signature.clone();
227240
sig.ident = format_ident!("{}", kind.to_string());
228241
if let FnDecorationKind::Ensures { ret_binder } = &kind {
229-
let output = match sig.output {
230-
syn::ReturnType::Default => quote! {()},
231-
syn::ReturnType::Type(_, t) => quote! {#t},
242+
add_unit_to_sig_if_needed(&mut sig);
243+
let output_typ = match sig.output {
244+
syn::ReturnType::Default => parse_quote! {()},
245+
syn::ReturnType::Type(_, t) => t,
232246
};
233247
let mut_ref_inputs = mut_ref_inputs
234248
.iter()
@@ -240,16 +254,26 @@ pub fn make_fn_decoration(
240254
let mut rewrite_future =
241255
RewriteFuture(mut_ref_inputs.clone().map(|x| x.0).collect());
242256
rewrite_future.visit_expr_mut(&mut phi);
243-
let (pats, tys): (Vec<_>, Vec<_>) = mut_ref_inputs
257+
let (mut pats, mut tys): (Vec<_>, Vec<_>) = mut_ref_inputs
244258
.map(|(name, ty)| {
245259
(
246260
create_future_ident(&name).to_token_stream(),
247261
ty.to_token_stream(),
248262
)
249263
})
250-
.chain([(ret_binder.to_token_stream(), output)].into_iter())
251264
.unzip();
252265

266+
let is_output_typ_unit = if let syn::Type::Tuple(tuple) = &*output_typ {
267+
tuple.elems.is_empty()
268+
} else {
269+
false
270+
};
271+
272+
if !is_output_typ_unit || pats.is_empty() {
273+
pats.push(ret_binder.to_token_stream());
274+
tys.push(quote! {#output_typ});
275+
}
276+
253277
sig.inputs
254278
.push(syn::parse_quote! {(#(#pats),*): (#(#tys),*)});
255279
}

test-harness/src/snapshots/toolchain__attributes into-fstar.snap

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,28 @@ exit = 0
2626
diagnostics = []
2727

2828
[stdout.files]
29+
"Attributes.Ensures_on_arity_zero_fns.fst" = '''
30+
module Attributes.Ensures_on_arity_zero_fns
31+
#set-options "--fuel 0 --ifuel 1 --z3rlimit 15"
32+
open Core
33+
open FStar.Mul
34+
35+
let basically_a_constant (_: Prims.unit)
36+
: Prims.Pure u8
37+
(requires true)
38+
(ensures
39+
fun x ->
40+
let x:u8 = x in
41+
x >. 100uy) = 127uy
42+
43+
let doing_nothing (_: Prims.unit)
44+
: Prims.Pure Prims.unit
45+
(requires true)
46+
(ensures
47+
fun v__x ->
48+
let v__x:Prims.unit = v__x in
49+
true) = ()
50+
'''
2951
"Attributes.Inlined_code_ensures_requires.fst" = '''
3052
module Attributes.Inlined_code_ensures_requires
3153
#set-options "--fuel 0 --ifuel 1 --z3rlimit 15"
@@ -36,8 +58,8 @@ let increment_array (v: t_Array u8 (sz 4))
3658
: Prims.Pure (t_Array u8 (sz 4))
3759
(requires forall i. FStar.Seq.index v i <. 254uy)
3860
(ensures
39-
fun temp_0_ ->
40-
let vv_future, ():(t_Array u8 (sz 4) & Prims.unit) = temp_0_ in
61+
fun vv_future ->
62+
let vv_future:t_Array u8 (sz 4) = vv_future in
4163
let future_v:t_Array u8 (sz 4) = vv_future in
4264
forall i. FStar.Seq.index future_v i >. 0uy) =
4365
let v:t_Array u8 (sz 4) =
@@ -368,6 +390,14 @@ let add3_lemma (x: u32)
368390

369391
let inlined_code__V: u8 = 12uy
370392

393+
let issue_844_ (v__x: u8)
394+
: Prims.Pure u8
395+
Prims.l_True
396+
(ensures
397+
fun v__x_future ->
398+
let v__x_future:u8 = v__x_future in
399+
true) = v__x
400+
371401
let u32_max: u32 = 90000ul
372402

373403
unfold let some_function _ = "hello from F*"

tests/attributes/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ fn swap_and_mut_req_ens(x: &mut u32, y: &mut u32) -> u32 {
2020
*x + *y
2121
}
2222

23+
#[hax_lib::ensures(|_| true)]
24+
fn issue_844(_x: &mut u8) {}
25+
26+
// From issue #845
27+
mod ensures_on_arity_zero_fns {
28+
#[hax_lib::requires(true)]
29+
#[hax_lib::ensures(|_x| true)]
30+
fn doing_nothing() {}
31+
#[hax_lib::requires(true)]
32+
#[hax_lib::ensures(|x| x > 100)]
33+
fn basically_a_constant() -> u8 {
34+
127
35+
}
36+
}
37+
2338
#[hax::lemma]
2439
fn add3_lemma(x: u32) -> Proof<{ x <= 10 || x >= u32_max / 3 || add3(x, x, x) == x * 3 }> {}
2540

0 commit comments

Comments
 (0)