Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Prover] add support of abort in spec function #14939

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to be very big? Is mono_info every type in the program?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For aptos-framework, there are about 500 uninterpreted functions generated while the generated boogie code is about 300k line. So IMHO as long as the program itself is reasonable, the number of types in the program is not a big concern.

.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 @@
// 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, ..)

Check warning on line 727 in third_party/move/move-prover/boogie-backend/src/spec_translator.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/move-prover/boogie-backend/src/spec_translator.rs#L727

Added line #L727 was not covered by tests
| ExpData::Sequence(id, ..)
| ExpData::Loop(id, ..)
| ExpData::Assign(id, ..)
| ExpData::Mutate(id, ..)
| ExpData::SpecBlock(id, ..)

Check warning on line 732 in third_party/move/move-prover/boogie-backend/src/spec_translator.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/move-prover/boogie-backend/src/spec_translator.rs#L729-L732

Added lines #L729 - L732 were not covered by tests
| ExpData::LoopCont(id, ..) => {
self.env.error(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a test that illustrates this error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

&self.env.get_node_loc(*id),
"imperative expressions not supported in specs",
);
},
}
}

Expand Down Expand Up @@ -991,15 +996,31 @@
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!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we show this error, too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mean the error below. All operations should already be handled in early stages so it is hard to add a test case to generate this error.

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)
),
);

Check warning on line 1023 in third_party/move/move-prover/boogie-backend/src/spec_translator.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/move-prover/boogie-backend/src/spec_translator.rs#L1016-L1023

Added lines #L1016 - L1023 were not covered by tests
},
}
}
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 │ │ }
│ ╰─────^
Loading