-
Notifications
You must be signed in to change notification settings - Fork 129
Introduce rmc::proof function attribute #668
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
Changes from 1 commit
1502f02
ea2be69
ee317ea
e4de683
37d6aa2
1446d36
76081aa
00788d2
9709a0b
2377945
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 |
|---|---|---|
|
|
@@ -3,8 +3,10 @@ | |
|
|
||
| //! This file contains functions related to codegenning MIR functions into gotoc | ||
|
|
||
| use crate::context::metadata::HarnessMetadata; | ||
| use crate::GotocCtx; | ||
| use cbmc::goto_program::{Expr, Stmt, Symbol}; | ||
| use rustc_ast::ast; | ||
| use rustc_middle::mir::{HasLocalDecls, Local}; | ||
| use rustc_middle::ty::{self, Instance, TyS}; | ||
| use tracing::{debug, warn}; | ||
|
|
@@ -98,6 +100,8 @@ impl<'tcx> GotocCtx<'tcx> { | |
| let stmts = self.current_fn_mut().extract_block(); | ||
| let body = Stmt::block(stmts, loc); | ||
| self.symbol_table.update_fn_declaration_with_definition(&name, body); | ||
|
|
||
| self.codegen_rmctool_attributes(); | ||
| } | ||
| self.reset_current_fn(); | ||
| } | ||
|
|
@@ -199,4 +203,41 @@ impl<'tcx> GotocCtx<'tcx> { | |
| }); | ||
| self.reset_current_fn(); | ||
| } | ||
|
|
||
| /// This updates the goto context with any information that should be accumulated from a function's | ||
| /// attributes. | ||
| /// | ||
| /// Currently, this is only proof harness annotations. | ||
| /// i.e. `#[rmc::proof]` (which rmc-annotations translates to `#[rmctool::proof]` for us to handle here) | ||
| fn codegen_rmctool_attributes(&mut self) { | ||
|
||
| let instance = self.current_fn().instance(); | ||
|
|
||
| for attr in self.tcx.get_attrs(instance.def_id()) { | ||
|
Contributor
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 you please add a debug statement? |
||
| if matches_rmctool_attr(attr, "proof") { | ||
|
||
| let current_fn = self.current_fn(); | ||
| let pretty_name = current_fn.readable_name().to_owned(); | ||
| let mangled_name = current_fn.name(); | ||
| let loc = self.codegen_span(¤t_fn.mir().span); | ||
|
|
||
| let harness = HarnessMetadata { | ||
| pretty_name, | ||
| mangled_name, | ||
| original_file: loc.filename().unwrap(), | ||
tedinski marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| self.proof_harnesses.push(harness); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn matches_rmctool_attr(attr: &ast::Attribute, name: &str) -> bool { | ||
|
||
| match &attr.kind { | ||
| ast::AttrKind::Normal(ast::AttrItem { path: ast::Path { segments, .. }, .. }, _) => { | ||
| segments.len() == 2 | ||
| && segments[0].ident.as_str() == "rmctool" | ||
| && segments[1].ident.as_str() == name | ||
| } | ||
| _ => false, | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,8 @@ | |
|
|
||
| //! This file contains the code necessary to interface with the compiler backend | ||
|
|
||
| use crate::context::metadata::RmcMetadata; | ||
| use crate::GotocCtx; | ||
|
|
||
| use bitflags::_core::any::Any; | ||
| use cbmc::goto_program::symtab_transformer; | ||
| use cbmc::goto_program::SymbolTable; | ||
|
|
@@ -27,9 +27,10 @@ use tracing::{debug, warn}; | |
|
|
||
| // #[derive(RustcEncodable, RustcDecodable)] | ||
| pub struct GotocCodegenResult { | ||
| pub type_map: BTreeMap<String, String>, | ||
| pub symtab: SymbolTable, | ||
| pub crate_name: rustc_span::Symbol, | ||
| pub metadata: RmcMetadata, | ||
| pub symtab: SymbolTable, | ||
| pub type_map: BTreeMap<String, String>, | ||
| } | ||
|
|
||
| #[derive(Clone)] | ||
|
|
@@ -125,17 +126,20 @@ impl CodegenBackend for GotocCodegenBackend { | |
| } | ||
|
|
||
| // perform post-processing symbol table passes | ||
| let symbol_table = symtab_transformer::do_passes( | ||
| let symtab = symtab_transformer::do_passes( | ||
| c.symbol_table, | ||
| &tcx.sess.opts.debugging_opts.symbol_table_passes, | ||
| ); | ||
|
|
||
| let type_map = BTreeMap::from_iter(c.type_map.into_iter().map(|(k, v)| (k, v.to_string()))); | ||
|
|
||
| let metadata = RmcMetadata { proof_harnesses: c.proof_harnesses }; | ||
|
Contributor
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. Why do we need this?
Contributor
Author
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'm not sure what you mean by 'this'. The RmcMetadata type?) I'm planning on the
Contributor
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. Sorry, I mean the copy statement.
Contributor
Author
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. Still not sure what the "copy statement" is. |
||
|
|
||
| Box::new(GotocCodegenResult { | ||
| type_map, | ||
| symtab: symbol_table, | ||
| crate_name: tcx.crate_name(LOCAL_CRATE) as rustc_span::Symbol, | ||
| metadata, | ||
| symtab, | ||
| type_map, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -163,6 +167,7 @@ impl CodegenBackend for GotocCodegenBackend { | |
| let base_filename = outputs.path(OutputType::Object); | ||
| write_file(&base_filename, "symtab.json", &result.symtab); | ||
| write_file(&base_filename, "type_map.json", &result.type_map); | ||
| write_file(&base_filename, "rmc-metadata.json", &result.metadata); | ||
| } | ||
|
|
||
| Ok(()) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
|
||
| //! This module should be factored out into its own separate crate eventually, | ||
| //! but leaving it here for now... | ||
|
|
||
| use serde::Serialize; | ||
|
|
||
| /// We emit this structure for each annotated proof harness we find | ||
| #[derive(Serialize)] | ||
| pub struct HarnessMetadata { | ||
| /// The name the user gave to the function | ||
| pub pretty_name: String, | ||
| /// The name of the function in the CBMC symbol table | ||
| pub mangled_name: String, | ||
| /// The (currently full-) path to the file this proof harness was declared within | ||
| pub original_file: String, | ||
| } | ||
|
|
||
| /// The structure of `.rmc-metadata.json` files, which are emitted for each crate | ||
| #[derive(Serialize)] | ||
| pub struct RmcMetadata { | ||
| pub proof_harnesses: Vec<HarnessMetadata>, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,5 +8,6 @@ | |
|
|
||
| mod current_fn; | ||
| mod goto_ctx; | ||
| pub mod metadata; | ||
|
|
||
| pub use goto_ctx::GotocCtx; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
|
||
| [package] | ||
| name = "rmc-annotations" | ||
| version = "0.1.0" | ||
| edition = "2018" | ||
| license = "MIT OR Apache-2.0" | ||
|
|
||
| [lib] | ||
| proc-macro = true | ||
|
|
||
| [dependencies] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
|
||
| // #![feature(register_tool)] | ||
| // #![register_tool(rmctool)] | ||
| // Frustratingly, it's not enough for our crate to enable these features, because we need all | ||
tedinski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // downstream crates to enable these features as well. | ||
| // So we have to enable this on the commandline (see rmc-rustc) with: | ||
| // RUSTFLAGS="-Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(rmctool)" | ||
tedinski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // proc_macro::quote is nightly-only, so we'll cobble things together instead | ||
| use proc_macro::TokenStream; | ||
|
|
||
| #[cfg(not(rmc))] | ||
| #[proc_macro_attribute] | ||
| pub fn proof(_attr: TokenStream, item: TokenStream) -> TokenStream { | ||
| // Leave the code intact, so it can be easily be edited in an IDE, | ||
| // but outside RMC, this code is likely never called. | ||
| let mut result = TokenStream::new(); | ||
|
|
||
| result.extend("#[allow(dead_code)]".parse::<TokenStream>().unwrap()); | ||
|
Contributor
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 wouldn't add this. We don't want proof methods to inadvertently make it to the final library / binary. We can either extend with a
Contributor
Author
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 thought about additionally branching into having a debug/release version of the macro, with the release removing the function entirely. Would that work for you? My goal here was to ensure But come to think of it, I think this won't work as-is anyway. If the goal is to let users add
Contributor
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. Gotcha. I'm OK with the idea of users adding |
||
| result.extend(item); | ||
| result | ||
| // quote!( | ||
| // #[allow(dead_code)] | ||
| // $item | ||
| // ) | ||
| } | ||
|
|
||
| #[cfg(rmc)] | ||
| #[proc_macro_attribute] | ||
| pub fn proof(_attr: TokenStream, item: TokenStream) -> TokenStream { | ||
tedinski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| let mut result = TokenStream::new(); | ||
|
|
||
| result.extend("#[rmctool::proof]".parse::<TokenStream>().unwrap()); | ||
|
Contributor
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 you please add the
Contributor
Author
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 thought the need for Is there some other reason for
Contributor
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 was thinking about addressing that in a follow up PR so this PR doesn't get too big and we can merge it sooner rather than later. If you do add that to this PR, then it's OK to keep it without the no_mangle. There are two reasons why we rely on the |
||
| result.extend(item); | ||
| result | ||
| // quote!( | ||
| // #[rmctool::proof] | ||
| // $item | ||
| // ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,3 +8,4 @@ edition = "2018" | |
| license = "MIT OR Apache-2.0" | ||
|
|
||
| [dependencies] | ||
| rmc-annotations = { path = "../rmc-annotations" } | ||
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.
Just a suggestion. This could be a one liner:
if let Location::Loc { file, .. } = self { Some(file.to_string) } else { None }