Skip to content

Commit c3024c6

Browse files
authored
Merge pull request #7511 from NlightNFotis/rust_api_exceptions_to_results
Translate exceptions from C++ API into Rust `Result` types.
2 parents c0bcc71 + f9da9dc commit c3024c6

File tree

4 files changed

+110
-20
lines changed

4 files changed

+110
-20
lines changed

src/libcprover-rust/include/c_api.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#pragma once
44

5+
#include <util/exception_utils.h>
6+
57
#include <memory>
68

79
// NOLINTNEXTLINE(build/include)
@@ -16,3 +18,30 @@ translate_vector_of_string(rust::Vec<rust::String> elements);
1618
// Exposure of the C++ object oriented API through free-standing functions.
1719
std::unique_ptr<api_sessiont> new_api_session();
1820
std::vector<std::string> const &get_messages();
21+
22+
// NOLINTNEXTLINE(readability/namespace)
23+
namespace rust
24+
{
25+
// NOLINTNEXTLINE(readability/namespace)
26+
namespace behavior
27+
{
28+
template <typename Try, typename Fail>
29+
static void trycatch(Try &&func, Fail &&fail) noexcept
30+
{
31+
try
32+
{
33+
func();
34+
}
35+
catch(const cprover_exception_baset &e)
36+
{
37+
fail(e.what());
38+
}
39+
catch(const invariant_failedt &i)
40+
{
41+
fail(i.what());
42+
}
43+
}
44+
45+
} // namespace behavior
46+
47+
} // namespace rust
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
util

src/libcprover-rust/src/c_api.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
std::vector<std::string> output;
1919

20+
extern bool cbmc_invariants_should_throw;
21+
2022
std::vector<std::string> const &
2123
translate_vector_of_string(rust::Vec<rust::String> elements)
2224
{
@@ -35,6 +37,9 @@ std::unique_ptr<api_sessiont> new_api_session()
3537
{
3638
// Create a new API session and register the default API callback for that.
3739
api_sessiont *api{new api_sessiont()};
40+
// We need to configure invariants to be throwing exceptions instead of
41+
// reporting to stderr and calling abort()
42+
cbmc_invariants_should_throw = true;
3843

3944
// This lambda needs to be non-capturing in order for it to be convertible
4045
// to a function pointer, to pass to the API.

src/libcprover-rust/src/lib.rs

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ pub mod ffi {
1111
// API Functions
1212
fn new_api_session() -> UniquePtr<api_sessiont>;
1313
fn get_api_version(&self) -> UniquePtr<CxxString>;
14-
fn load_model_from_files(&self, files: &CxxVector<CxxString>);
15-
fn verify_model(&self);
16-
fn validate_goto_model(&self);
17-
fn drop_unused_functions(&self);
14+
fn load_model_from_files(&self, files: &CxxVector<CxxString>) -> Result<()>;
15+
fn verify_model(&self) -> Result<()>;
16+
fn validate_goto_model(&self) -> Result<()>;
17+
fn drop_unused_functions(&self) -> Result<()>;
1818

1919
// Helper/Utility functions
2020
fn translate_vector_of_string(elements: Vec<String>) -> &'static CxxVector<CxxString>;
@@ -23,25 +23,37 @@ pub mod ffi {
2323

2424
extern "Rust" {
2525
fn print_response(vec: &CxxVector<CxxString>);
26+
fn translate_response_buffer(vec: &CxxVector<CxxString>) -> Vec<String>;
2627
}
2728
}
2829

29-
fn print_response(vec: &CxxVector<CxxString>) {
30-
let vec: Vec<String> = vec
31-
.iter()
30+
/// This is a utility function, whose job is to translate the responses from the C++
31+
/// API (which we get in the form of a C++ std::vector<std:string>) into a form that
32+
/// can be easily consumed by other Rust programs.
33+
pub fn translate_response_buffer(vec: &CxxVector<CxxString>) -> Vec<String> {
34+
vec.iter()
3235
.map(|s| s.to_string_lossy().into_owned())
33-
.collect();
36+
.collect()
37+
}
38+
39+
/// This is a utility function, whose aim is to simplify direct printing of the messages
40+
/// that we get from CBMC's C++ API. Underneath, it's using translate_response_buffer
41+
/// to translate the C++ types into Rust types and then prints out the strings contained
42+
/// in the resultant rust vector.
43+
pub fn print_response(vec: &CxxVector<CxxString>) {
44+
let vec: Vec<String> = translate_response_buffer(vec);
3445

3546
for s in vec {
3647
println!("{}", s);
3748
}
3849
}
3950

40-
// To test run "CBMC_LIB_DIR=<path_to_build/libs> cargo test -- --test-threads=1 --nocapture"
51+
// To test run "CBMC_LIB_DIR=<path_to_build/libs> SAT_IMPL=minisat2 cargo test -- --test-threads=1 --nocapture"
4152
#[cfg(test)]
4253
mod tests {
4354
use super::*;
4455
use cxx::let_cxx_string;
56+
use std::process;
4557

4658
#[test]
4759
fn it_works() {
@@ -75,12 +87,28 @@ mod tests {
7587

7688
// Invoke load_model_from_files and see if the model
7789
// has been loaded.
78-
client.load_model_from_files(vect);
79-
// Validate integrity of passed goto-model.
80-
client.validate_goto_model();
90+
if let Err(_) = client.load_model_from_files(vect) {
91+
eprintln!("Failed to load model from files: {:?}", vect);
92+
process::exit(1);
93+
}
8194

95+
// Validate integrity of passed goto-model.
96+
if let Err(_) = client.validate_goto_model() {
97+
eprintln!("Failed to validate goto model from files: {:?}", vect);
98+
process::exit(1);
99+
}
100+
101+
// ATTENTION: The following `.clone()` is unneeded - I keep it here in order to
102+
// make potential printing of the message buffer easier (because of borrowing).
103+
// This is also why a print instruction is commented out (as a guide for someone
104+
// else in case they want to inspect the output).
105+
let validation_msg = "Validating consistency of goto-model supplied to API session";
82106
let msgs = ffi::get_messages();
83-
print_response(msgs);
107+
let msgs_assert = translate_response_buffer(msgs).clone();
108+
109+
assert!(msgs_assert.contains(&String::from(validation_msg)));
110+
111+
// print_response(msgs);
84112
}
85113

86114
#[test]
@@ -90,15 +118,29 @@ mod tests {
90118
let vec: Vec<String> = vec!["other/example.c".to_owned()];
91119

92120
let vect = ffi::translate_vector_of_string(vec);
93-
client.load_model_from_files(vect);
121+
122+
if let Err(_) = client.load_model_from_files(vect) {
123+
eprintln!("Failed to load model from files: {:?}", vect);
124+
process::exit(1);
125+
}
94126

95127
// Validate integrity of goto-model
96-
client.validate_goto_model();
128+
if let Err(_) = client.validate_goto_model() {
129+
eprintln!("Failed to validate goto model from files: {:?}", vect);
130+
process::exit(1);
131+
}
132+
133+
if let Err(_) = client.verify_model() {
134+
eprintln!("Failed to verify model from files: {:?}", vect);
135+
process::exit(1);
136+
}
97137

98-
client.verify_model();
138+
let verification_msg = "VERIFICATION FAILED";
99139

100140
let msgs = ffi::get_messages();
101-
print_response(msgs);
141+
let msgs_assert = translate_response_buffer(msgs).clone();
142+
143+
assert!(msgs_assert.contains(&String::from(verification_msg)));
102144
}
103145

104146
#[test]
@@ -114,11 +156,24 @@ mod tests {
114156
let vect = ffi::translate_vector_of_string(vec);
115157
assert_eq!(vect.len(), 1);
116158

117-
client.load_model_from_files(vect);
159+
if let Err(_) = client.load_model_from_files(vect) {
160+
eprintln!("Failed to load model from files: {:?}", vect);
161+
process::exit(1);
162+
}
163+
118164
// Perform a drop of any unused functions.
119-
client.drop_unused_functions();
165+
if let Err(err) = client.drop_unused_functions() {
166+
eprintln!("Error during client call: {:?}", err);
167+
process::exit(1);
168+
}
169+
170+
let instrumentation_msg = "Performing instrumentation pass: dropping unused functions";
171+
let instrumentation_msg2 = "Dropping 8 of 11 functions (3 used)";
120172

121173
let msgs = ffi::get_messages();
122-
print_response(msgs);
174+
let msgs_assert = translate_response_buffer(msgs).clone();
175+
176+
assert!(msgs_assert.contains(&String::from(instrumentation_msg)));
177+
assert!(msgs_assert.contains(&String::from(instrumentation_msg2)));
123178
}
124179
}

0 commit comments

Comments
 (0)