-
Notifications
You must be signed in to change notification settings - Fork 97
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
Unwind Attribute without Python Changes #846
Changes from 1 commit
9e2e1b1
89092cf
05f83b0
da43716
5ca9856
3a2b8b0
f4cd7ee
3deac4e
d870bbe
52288e3
29b2820
4de691f
f09c7f4
e4e69e9
70f5685
192d189
08a40a3
51b86ae
5715249
ddc97dd
c661d9f
e637963
a04b462
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 |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
// RUSTFLAGS="-Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(kanitool)" | ||
|
||
// proc_macro::quote is nightly-only, so we'll cobble things together instead | ||
extern crate proc_macro; | ||
use proc_macro::TokenStream; | ||
|
||
#[cfg(all(not(kani), not(test)))] | ||
|
@@ -49,3 +50,29 @@ pub fn proof(_attr: TokenStream, item: TokenStream) -> TokenStream { | |
// $item | ||
// ) | ||
} | ||
|
||
#[cfg(not(kani))] | ||
#[proc_macro_attribute] | ||
pub fn unwind(_attr: TokenStream, _item: TokenStream) -> TokenStream { | ||
// Not-RMC, Not-Test means this code shouldn't exist, return nothing. | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
TokenStream::new() | ||
} | ||
|
||
#[cfg(kani)] | ||
#[proc_macro_attribute] | ||
pub fn unwind(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
celinval marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let mut result = TokenStream::new(); | ||
|
||
// Translate #[kani::unwind(arg)] to #[kanitool::unwind_arg_] for easier handling | ||
tedinski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let insert_string = "#[kanitool::unwind_".to_owned() + &attr.clone().to_string() + "]"; | ||
|
||
// Add the string that looks like - #[kanitool::unwind_value_] | ||
result.extend(insert_string.parse::<TokenStream>().unwrap()); | ||
// No mangle seems to be necessary as removing it prevents all the attributes in a lib from being read | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
result.extend("#[no_mangle]".parse::<TokenStream>().unwrap()); | ||
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. Do we still need this? 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. No, not anymore. let me update that. 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. Update : We do need this. Without it, all unwind annotations without 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. No mangle has a side effect of making a function public and potentially causing name conflicts (E.g.: rust-lang/rust#28179). BTW, how did you test this behavior? Can you please make sure that the function you are annotating isn't dead code. Feel free to ping me offline. 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. Talked offline. We'll remove this. |
||
|
||
result.extend(item); | ||
result | ||
|
||
// / _attr | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
|
||
//! This file contains functions related to codegenning MIR functions into gotoc | ||
|
||
use crate::context::metadata::HarnessMetadata; | ||
use crate::context::metadata::{HarnessMetadata, UnwindMetadata}; | ||
use crate::GotocCtx; | ||
use cbmc::goto_program::{Expr, Stmt, Symbol}; | ||
use cbmc::InternString; | ||
|
@@ -13,6 +13,8 @@ use rustc_middle::ty::{self, Instance, TyS}; | |
use std::collections::BTreeMap; | ||
use std::iter::FromIterator; | ||
use tracing::{debug, warn}; | ||
extern crate regex; | ||
use regex::Regex; | ||
|
||
/// Utility to skip functions that can't currently be successfully codgenned. | ||
impl<'tcx> GotocCtx<'tcx> { | ||
|
@@ -243,11 +245,33 @@ impl<'tcx> GotocCtx<'tcx> { | |
fn handle_kanitool_attributes(&mut self) { | ||
let instance = self.current_fn().instance(); | ||
|
||
// This regex can be expanded as more attributes become available | ||
// Can be thought as a menu, in expanded mode | ||
let input_re = Regex::new( | ||
r#"(?x) | ||
(proof) | | ||
(unwind_)(\d+) | ||
"#, | ||
) | ||
.unwrap(); | ||
|
||
for attr in self.tcx.get_attrs(instance.def_id()) { | ||
match kanitool_attr_name(attr).as_deref() { | ||
Some("proof") => self.handle_kanitool_proof(), | ||
_ => {} | ||
} | ||
if let Some(attribute_string) = kanitool_attr_name(attr).as_deref() { | ||
// This regex capture operation returns a vector of strings that can be matched for our purpose | ||
// In later Attributes, it will be easy to parse the regex for the attribute arguments, regardless | ||
// of the type of the argument itself if we capture all the patterns as a vector of strings of the format | ||
// [command, argument] | ||
let captures = input_re.captures(attribute_string).map(|captures| { | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
captures.iter().skip(1).flat_map(|c| c).map(|c| c.as_str()).collect::<Vec<_>>() | ||
}); | ||
|
||
// Match against the captured values as a slice | ||
match captures.as_ref().map(|c| c.as_slice()) { | ||
Some(["proof"]) => self.handle_kanitool_proof(), | ||
Some(["unwind_", x]) => self.handle_kanitool_unwind(x), | ||
_ => {} | ||
} | ||
}; | ||
} | ||
} | ||
|
||
|
@@ -267,15 +291,36 @@ impl<'tcx> GotocCtx<'tcx> { | |
|
||
self.proof_harnesses.push(harness); | ||
} | ||
|
||
/// Unwind strings of the format 'unwind_x' and the mangled names are to be parsed for the value 'x' | ||
fn handle_kanitool_unwind(&mut self, attribute_string: &str) { | ||
// Parse parameter string for value of the argument | ||
let argument_value: u32 = | ||
attribute_string.parse().expect("Unwind Argument Value is not a number"); | ||
|
||
// Make sure the value lies between 0 and u32 max | ||
assert!(argument_value < u32::MAX); | ||
|
||
let current_fn = self.current_fn(); | ||
let mangled_name = current_fn.name(); | ||
|
||
let unwind_instance = | ||
UnwindMetadata { mangled_name: mangled_name, unwind_value: argument_value }; | ||
self.unwind_metadata.push(unwind_instance); | ||
} | ||
} | ||
|
||
/// If the attribute is named `kanitool::name`, this extracts `name` | ||
fn kanitool_attr_name(attr: &ast::Attribute) -> Option<String> { | ||
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 this should return 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. Returning Option<&str> would return a dangling pointer as the original string would be destroyed once the function returns. We need to return an Owned String so I don't think we can replace the return type. 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. Indeed, I didn't notice that you changed the code to join the segments. For the original code, the return value would have the same lifetime as the argument BTW, why did you change it? This code might actually be doing the wrong thing. I.e., let's say you have the following attribute: #[kanitool::type::sub_type] I believe that you are going to return the string 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 so, but I don't see the need to modify it to potential future attributes when there's no clear consensus on what attributes we're planning on supporting. Imo we can always change this later to adapt to future annotations. Let me check about the scenario you've posted however. 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. Talked offline, this is no longer needed. Going to revert the change. |
||
match &attr.kind { | ||
ast::AttrKind::Normal(ast::AttrItem { path: ast::Path { segments, .. }, .. }, _) | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if segments.len() == 2 && segments[0].ident.as_str() == "kanitool" => | ||
if (!segments.is_empty()) && segments[0].ident.as_str() == "kanitool" => | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
Some(segments[1].ident.as_str().to_string()) | ||
let mut new_string = String::new(); | ||
for index in 1..segments.len() { | ||
new_string.push_str(segments[index].ident.as_str()); | ||
} | ||
Some(new_string) | ||
} | ||
_ => None, | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
||
[package] | ||
name = "simple-unwind-annotation" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
|
||
[kani.flags] | ||
output-format = "old" | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
line 5 assertion failed: 1 == 2: FAILURE |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
||
fn main() { | ||
assert!(1 == 2); | ||
} | ||
|
||
// rmc-flags: --no-unwinding-checks | ||
|
||
// The expected file presently looks for "1 == 2" above. | ||
// But eventually this test may start to fail as we might stop regarding 'main' | ||
// as a valid proof harness, since it isn't annotated as such. | ||
// This test should be updated if we go that route. | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#[kani::proof] | ||
#[kani::unwind(10)] | ||
fn harness() { | ||
let mut counter = 0; | ||
loop { | ||
counter += 1; | ||
assert!(counter < 10); | ||
} | ||
} | ||
|
||
#[kani::unwind(8)] | ||
fn harness_2() { | ||
let mut counter = 0; | ||
for i in 0..7 { | ||
counter += 1; | ||
assert!(counter < 5); | ||
} | ||
} | ||
|
||
#[kani::unwind(9)] | ||
fn harness_3() { | ||
let mut counter = 0; | ||
for i in 0..10 { | ||
counter += 1; | ||
assert!(counter < 8); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
||
fn main() { | ||
assert!(1 == 2); | ||
} | ||
|
||
// rmc-flags: --no-unwinding-checks | ||
|
||
// Fix me | ||
jaisnan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[kani::proof] | ||
#[kani::unwind(10)] | ||
fn harness() { | ||
let mut counter = 0; | ||
loop { | ||
counter += 1; | ||
assert!(counter < 10); | ||
} | ||
} | ||
|
||
#[kani::unwind(8)] | ||
fn harness_2() { | ||
let mut counter = 0; | ||
for i in 0..7 { | ||
counter += 1; | ||
assert!(counter < 5); | ||
} | ||
} | ||
|
||
#[kani::unwind(9)] | ||
fn harness_3() { | ||
let mut counter = 0; | ||
for i in 0..10 { | ||
counter += 1; | ||
assert!(counter < 8); | ||
} | ||
} |
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.
was this necessary?