-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, ..) | ||
| ExpData::Sequence(id, ..) | ||
| ExpData::Loop(id, ..) | ||
| ExpData::Assign(id, ..) | ||
| ExpData::Mutate(id, ..) | ||
| ExpData::SpecBlock(id, ..) | ||
| ExpData::LoopCont(id, ..) => { | ||
self.env.error( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a test that illustrates this error? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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", | ||
); | ||
}, | ||
} | ||
} | ||
|
||
|
@@ -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!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we show this error, too? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
), | ||
); | ||
}, | ||
} | ||
} | ||
|
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 |
---|---|---|
@@ -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 │ │ } | ||
│ ╰─────^ |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.