Skip to content

Commit

Permalink
[Prover] add support of abort in spec function (#14939)
Browse files Browse the repository at this point in the history
* [single-node-performance] Add runner information in the output (#14932)

* [single-node-performance] Add runner information in the output

* adding skip move e2e

* recalibration

* add support of abort when in spec

---------

Co-authored-by: igor-aptos <[email protected]>
  • Loading branch information
rahxephon89 and igor-aptos authored Oct 16, 2024
1 parent 8b7aee1 commit d2eaddb
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl<'a> ExpRewriterFunctions for SpecConverter<'a> {
.into_exp()
},
// Deal with removing various occurrences of Abort and spec blocks
Call(id, Abort, _) | SpecBlock(id, ..) => {
SpecBlock(id, ..) => {
// Replace direct call by unit
Call(*id, Tuple, vec![]).into_exp()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module 0x42::move_function_in_spec {
}
}
spec fun $type_of<T>(): TypeInfo {
Tuple()
Abort(1)
}
} // end 0x42::move_function_in_spec

Expand Down
21 changes: 21 additions & 0 deletions third_party/move/move-prover/boogie-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,27 @@ pub fn add_prelude(
sh_instances = vec![];
bv_instances = vec![];
}

let mut all_types = mono_info
.all_types
.iter()
.filter(|ty| ty.can_be_type_argument())
.map(|ty| TypeInfo::new(env, options, ty, false))
.collect::<BTreeSet<_>>()
.into_iter()
.collect_vec();
let mut bv_all_types = mono_info
.all_types
.iter()
.filter(|ty| ty.can_be_type_argument())
.map(|ty| TypeInfo::new(env, options, ty, true))
.filter(|ty_info| !all_types.contains(ty_info))
.collect::<BTreeSet<_>>()
.into_iter()
.collect_vec();
all_types.append(&mut bv_all_types);
context.insert("uninterpreted_instances", &all_types);

context.insert("sh_instances", &sh_instances);
context.insert("bv_instances", &bv_instances);
let mut vec_instances = mono_info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ options provided to the prover.
{% include "custom-natives" %}
{%- endif %}


// Uninterpreted function for all types

{% for instance in uninterpreted_instances %}

{%- set S = "'" ~ instance.suffix ~ "'" -%}
{%- set T = instance.name -%}

function $Arbitrary_value_of{{S}}(): {{T}};

{% endfor %}

// ============================================================================================
// Primitive Types

Expand Down
39 changes: 30 additions & 9 deletions third_party/move/move-prover/boogie-backend/src/spec_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,13 +724,18 @@ impl<'env> SpecTranslator<'env> {
// Single-element sequence is just a wrapped value.
self.translate_exp(exp_vec.first().expect("list has an element"));
},
ExpData::Return(..)
| ExpData::Sequence(..)
| ExpData::Loop(..)
| ExpData::Assign(..)
| ExpData::Mutate(..)
| ExpData::SpecBlock(..)
| ExpData::LoopCont(..) => panic!("imperative expressions not supported"),
ExpData::Return(id, ..)
| ExpData::Sequence(id, ..)
| ExpData::Loop(id, ..)
| ExpData::Assign(id, ..)
| ExpData::Mutate(id, ..)
| ExpData::SpecBlock(id, ..)
| ExpData::LoopCont(id, ..) => {
self.env.error(
&self.env.get_node_loc(*id),
"imperative expressions not supported in specs",
);
},
}
}

Expand Down Expand Up @@ -991,15 +996,31 @@ impl<'env> SpecTranslator<'env> {
self.translate_call(node_id, oper, &[args[args.len() - 1].clone()]);
emit!(self.writer, &")".repeat(count));
},
Operation::Abort => {
let exp_bv_flag = global_state.get_node_num_oper(node_id) == Bitwise;
emit!(
self.writer,
&format!(
"$Arbitrary_value_of'{}'()",
boogie_type_suffix_bv(self.env, &self.get_node_type(node_id), exp_bv_flag)
)
);
},
Operation::MoveFunction(_, _)
| Operation::BorrowGlobal(_)
| Operation::Borrow(..)
| Operation::Deref
| Operation::MoveTo
| Operation::MoveFrom
| Operation::Abort
| Operation::Old => {
panic!("operation unexpected: {}", oper.display(self.env, node_id))
self.env.error(
&self.env.get_node_loc(node_id),
&format!(
"bug: operation {} is not supported \
in the current context",
oper.display(self.env, node_id)
),
);
},
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
module 0x42::TestAbortInFunction {

fun aborts_with(x: u64, y: u64): u64 {
if (x == 1) {
abort 2
} else if (y == 2) {
abort 3
} else {
x
}
}
spec aborts_with {
aborts_if x == 1 with 2;
aborts_if y == 2 with 3;
ensures result == x;
}

fun call_aborts_with(): u64 {
aborts_with(2, 3)
}

spec call_aborts_with {
ensures result == aborts_with(2, 3);
}

fun abort_generic<Element: copy + drop>(x: Element, y: Element): Element {
if (x == y) {
abort 0
} else {
x
}
}

fun call_aborts_generic(): u64 {
abort_generic(2, 3)
}

spec call_aborts_generic {
ensures result == abort_generic(2, 3);
}

struct S<Element: copy + drop> has copy, drop {
value: Element
}

fun abort_generic_struct<Element: copy + drop>(x: S<Element>, y: S<Element>): S<Element> {
if (x == y) {
abort 0
} else {
x
}
}

fun spec_abort_generic_struct<Element: copy + drop>(x: S<Element>, y: S<Element>): S<Element> {
if (x == y) {
abort 0
} else {
x
}
}

fun call_abort_generic_struct<Element: copy + drop>(x: Element, y: Element): Element {
let sx = S {
value: x
};
let sy = S {
value: y
};
abort_generic_struct(sx, sy).value
}

spec call_abort_generic_struct {
aborts_if x == y;
ensures result == call_abort_generic_struct(x, y);
}

struct T has copy, drop {
v: u64
}

spec T {
pragma bv=b"0";
}

fun call_abort_generic_struct_concrete(x: u64, y: u64, test_assert1: bool): T {
let sx = S {
value: T {
v: x
}
};
let sy = S {
value: T {
v: y
}
};
assert!(test_assert1, 0);
abort_generic_struct(sx, sy).value
}

spec call_abort_generic_struct_concrete {
aborts_if x == y;
aborts_if !test_assert1;
ensures result == call_abort_generic_struct_concrete(x, y, test_assert1);
ensures result == spec_call_abort_generic_struct_concrete(x, y);
}

spec fun spec_call_abort_generic_struct_concrete(x: u64, y: u64): T {
let sx = S {
value: T {
v: x
}
};
let sy = S {
value: T {
v: y
}
};
spec_abort_generic_struct(sx, sy).value
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ error: induction case of the loop invariant does not hold
= at tests/sources/functional/loops_with_memory_ops.move:80: nested_loop2
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= b = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= a = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:85: nested_loop2
= i = <redacted>
Expand Down Expand Up @@ -104,8 +105,8 @@ error: unknown assertion failed
= at tests/sources/functional/loops_with_memory_ops.move:80: nested_loop2
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= b = <redacted>
= a = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= a = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:85: nested_loop2
= i = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:86: nested_loop2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ error: induction case of the loop invariant does not hold
= at tests/sources/functional/loops_with_memory_ops.move:80: nested_loop2
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= a = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= b = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:85: nested_loop2
= <redacted> = <redacted>
= i = <redacted>
Expand Down Expand Up @@ -99,9 +99,8 @@ error: unknown assertion failed
= at tests/sources/functional/loops_with_memory_ops.move:75: nested_loop2
= at tests/sources/functional/loops_with_memory_ops.move:80: nested_loop2
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= a = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= b = <redacted>
= at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
= at tests/sources/functional/loops_with_memory_ops.move:85: nested_loop2
= <redacted> = <redacted>
= i = <redacted>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Move prover returns: exiting with condition generation errors
error: imperative expressions not supported in specs
┌─ tests/sources/functional/spec_fun_imperative_expression_err.move:2:27
2 │ fun sequential(): u64 {
│ ╭───────────────────────────^
3 │ │ let _x = 2;
4 │ │ let _y = 3;
5 │ │ while(_y > 0) {
· │
16 │ │ _x
17 │ │ }
│ ╰─────^
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module 0x42::M {
fun sequential(): u64 {
let _x = 2;
let _y = 3;
while(_y > 0) {
break
};
if (_x > 0) {
abort(0)
};
let _z = if (_x > 5) {
_x
} else {
_y
};
_x
}

fun m() {
let _z = 2;
spec {
assert _z == sequential();
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Move prover returns: exiting with condition generation errors
error: imperative expressions not supported in specs
┌─ tests/sources/functional/spec_fun_imperative_expression_err.move:2:27
2 │ fun sequential(): u64 {
│ ╭───────────────────────────^
3 │ │ let _x = 2;
4 │ │ let _y = 3;
5 │ │ while(_y > 0) {
· │
16 │ │ _x
17 │ │ }
│ ╰─────^

0 comments on commit d2eaddb

Please sign in to comment.