Skip to content

Commit cd26f00

Browse files
coeff-aijcoord-e
andauthored
Insert raw commands into .smt2 file with #![raw_command()] attribute (#21)
* Run CI workflow on pull_request * add: test for raw_define attribute * add: RawDefinition for System * add: formatting for RawDefinition * add: raw_define path * add: parse raw definitions * fix: invalid SMT-LIB2 format * add: analyze inner-attribute #[raw_define()] for the given crate * fix: error relate to new raw_definitions field of System * remove: unused code * add: positiive test for multiple raw_define annotations * add: negative tests for raw_define * fix: tests for raw_define(removing unused code, add more description) * change: rename raw_define -> raw_command, RawDefine -> RawCommand, etc. * add: negative tests for raw_define * remove: FormatContext::raw_commands * fix: wrong description about the error on negative test * fix: undo accidental renames * change: delete annot::AnnotParser::parse_raw_definition() and proccess TokenTrees directly * fix: run `cargo fmt` * remove: accidentally duplicated negative tests * Update src/analyze/crate_.rs Co-authored-by: Hiromi Ogawa <me@coord-e.com> * Update src/chc.rs Co-authored-by: Hiromi Ogawa <me@coord-e.com> --------- Co-authored-by: coord_e <me@coord-e.com>
1 parent 0951406 commit cd26f00

7 files changed

Lines changed: 129 additions & 0 deletions

File tree

src/analyze/annot.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ pub fn extern_spec_fn_path() -> [Symbol; 2] {
3737
[Symbol::intern("thrust"), Symbol::intern("extern_spec_fn")]
3838
}
3939

40+
pub fn raw_command_path() -> [Symbol; 2] {
41+
[Symbol::intern("thrust"), Symbol::intern("raw_command")]
42+
}
43+
4044
/// A [`annot::Resolver`] implementation for resolving function parameters.
4145
///
4246
/// The parameter names and their sorts needs to be configured via

src/analyze/crate_.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::collections::HashSet;
44

5+
use rustc_hir::def_id::CRATE_DEF_ID;
56
use rustc_middle::ty::{self as mir_ty, TyCtxt};
67
use rustc_span::def_id::{DefId, LocalDefId};
78

@@ -26,6 +27,37 @@ pub struct Analyzer<'tcx, 'ctx> {
2627
}
2728

2829
impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> {
30+
fn analyze_raw_command_annot(&mut self) {
31+
for attrs in self.tcx.get_attrs_by_path(
32+
CRATE_DEF_ID.to_def_id(),
33+
&analyze::annot::raw_command_path(),
34+
) {
35+
use rustc_ast::token::{LitKind, Token, TokenKind};
36+
use rustc_ast::tokenstream::TokenTree;
37+
38+
let ts = analyze::annot::extract_annot_tokens(attrs.clone());
39+
let tt = ts.trees().next().expect("string literal");
40+
41+
let raw_command = match tt {
42+
TokenTree::Token(
43+
Token {
44+
kind: TokenKind::Literal(lit),
45+
..
46+
},
47+
_,
48+
) if lit.kind == LitKind::Str => lit.symbol.to_string(),
49+
_ => panic!("invalid raw_command annotation"),
50+
};
51+
52+
self.ctx
53+
.system
54+
.borrow_mut()
55+
.push_raw_command(chc::RawCommand {
56+
command: raw_command,
57+
});
58+
}
59+
}
60+
2961
fn refine_local_defs(&mut self) {
3062
for local_def_id in self.tcx.mir_keys(()) {
3163
if self.tcx.def_kind(*local_def_id).is_fn_like() {
@@ -187,6 +219,7 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> {
187219
let span = tracing::debug_span!("crate", krate = %self.tcx.crate_name(rustc_span::def_id::LOCAL_CRATE));
188220
let _guard = span.enter();
189221

222+
self.analyze_raw_command_annot();
190223
self.refine_local_defs();
191224
self.analyze_local_defs();
192225
self.assert_callable_entry();

src/chc.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,6 +1606,14 @@ impl Clause {
16061606
}
16071607
}
16081608

1609+
/// A command specified using `thrust::raw_command` attribute
1610+
///
1611+
/// Those will be directly inserted into the generated SMT-LIB2 file.
1612+
#[derive(Debug, Clone)]
1613+
pub struct RawCommand {
1614+
pub command: String,
1615+
}
1616+
16091617
/// A selector for a datatype constructor.
16101618
///
16111619
/// A selector is a function that extracts a field from a datatype value.
@@ -1655,6 +1663,7 @@ pub struct PredVarDef {
16551663
/// A CHC system.
16561664
#[derive(Debug, Clone, Default)]
16571665
pub struct System {
1666+
pub raw_commands: Vec<RawCommand>,
16581667
pub datatypes: Vec<Datatype>,
16591668
pub clauses: IndexVec<ClauseId, Clause>,
16601669
pub pred_vars: IndexVec<PredVarId, PredVarDef>,
@@ -1665,6 +1674,10 @@ impl System {
16651674
self.pred_vars.push(PredVarDef { sig, debug_info })
16661675
}
16671676

1677+
pub fn push_raw_command(&mut self, raw_command: RawCommand) {
1678+
self.raw_commands.push(raw_command)
1679+
}
1680+
16681681
pub fn push_clause(&mut self, clause: Clause) -> Option<ClauseId> {
16691682
if clause.is_nop() {
16701683
return None;

src/chc/smtlib2.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,24 @@ impl<'ctx, 'a> Clause<'ctx, 'a> {
370370
}
371371
}
372372

373+
/// A wrapper around a [`chc::RawCommand`] that provides a [`std::fmt::Display`] implementation in SMT-LIB2 format.
374+
#[derive(Debug, Clone)]
375+
pub struct RawCommand<'a> {
376+
inner: &'a chc::RawCommand,
377+
}
378+
379+
impl<'a> std::fmt::Display for RawCommand<'a> {
380+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381+
write!(f, "{}", self.inner.command,)
382+
}
383+
}
384+
385+
impl<'a> RawCommand<'a> {
386+
pub fn new(inner: &'a chc::RawCommand) -> Self {
387+
Self { inner }
388+
}
389+
}
390+
373391
/// A wrapper around a [`chc::DatatypeSelector`] that provides a [`std::fmt::Display`] implementation in SMT-LIB2 format.
374392
#[derive(Debug, Clone)]
375393
pub struct DatatypeSelector<'ctx, 'a> {
@@ -555,6 +573,11 @@ impl<'a> std::fmt::Display for System<'a> {
555573
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
556574
writeln!(f, "(set-logic HORN)\n")?;
557575

576+
// insert command from #![thrust::raw_command()] here
577+
for raw_command in &self.inner.raw_commands {
578+
writeln!(f, "{}\n", RawCommand::new(raw_command))?;
579+
}
580+
558581
writeln!(f, "{}\n", Datatypes::new(&self.ctx, self.ctx.datatypes()))?;
559582
for datatype in self.ctx.datatypes() {
560583
writeln!(f, "{}", DatatypeDiscrFun::new(&self.ctx, datatype))?;

src/chc/unbox.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub fn unbox(system: System) -> System {
161161
clauses,
162162
pred_vars,
163163
datatypes,
164+
raw_commands,
164165
} = system;
165166
let datatypes = datatypes.into_iter().map(unbox_datatype).collect();
166167
let clauses = clauses.into_iter().map(unbox_clause).collect();
@@ -169,5 +170,6 @@ pub fn unbox(system: System) -> System {
169170
clauses,
170171
pred_vars,
171172
datatypes,
173+
raw_commands,
172174
}
173175
}

tests/ui/pass/annot_raw_command.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@check-pass
2+
//@compile-flags: -Adead_code -C debug-assertions=off
3+
4+
// Insert commands written in SMT-LIB2 format into .smt2 file directly.
5+
// This feature is intended for debug or experiment purpose.
6+
#![feature(custom_inner_attributes)]
7+
#![thrust::raw_command("(define-fun is_double ((x Int) (doubled_x Int)) Bool
8+
(=
9+
(* x 2)
10+
doubled_x
11+
)
12+
)")]
13+
14+
#[thrust::requires(true)]
15+
#[thrust::ensures(result == 2 * x)]
16+
fn double(x: i64) -> i64 {
17+
x + x
18+
}
19+
20+
fn main() {
21+
let a = 3;
22+
assert!(double(a) == 6);
23+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@check-pass
2+
//@compile-flags: -Adead_code -C debug-assertions=off
3+
4+
// Insert commands written in SMT-LIB2 format into .smt2 file directly.
5+
// This feature is intended for debug or experiment purpose.
6+
#![feature(custom_inner_attributes)]
7+
#![thrust::raw_command("(define-fun is_double ((x Int) (doubled_x Int)) Bool
8+
(=
9+
(* x 2)
10+
doubled_x
11+
)
12+
)")]
13+
14+
// multiple raw commands can be inserted.
15+
#![thrust::raw_command("(define-fun is_triple ((x Int) (tripled_x Int)) Bool
16+
(=
17+
(* x 3)
18+
tripled_x
19+
)
20+
)")]
21+
22+
#[thrust::requires(true)]
23+
#[thrust::ensures(result == 2 * x)]
24+
fn double(x: i64) -> i64 {
25+
x + x
26+
}
27+
28+
fn main() {
29+
let a = 3;
30+
assert!(double(a) == 6);
31+
}

0 commit comments

Comments
 (0)