Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7f68acf
Run CI workflow on pull_request
coord-e Jan 14, 2026
9a0ee9b
add: test for raw_define attribute
coeff-aij Jan 9, 2026
984c159
add: RawDefinition for System
coeff-aij Jan 10, 2026
209dfed
add: formatting for RawDefinition
coeff-aij Jan 10, 2026
8c2716f
add: raw_define path
coeff-aij Jan 11, 2026
e19c5bc
add: parse raw definitions
coeff-aij Jan 11, 2026
aab6bdc
fix: invalid SMT-LIB2 format
coeff-aij Jan 11, 2026
79813f2
add: analyze inner-attribute #[raw_define()] for the given crate
coeff-aij Jan 11, 2026
f18c14d
fix: error relate to new raw_definitions field of System
coeff-aij Jan 11, 2026
1b6658a
remove: unused code
coeff-aij Jan 11, 2026
bdf5b4d
add: positiive test for multiple raw_define annotations
coeff-aij Jan 12, 2026
393e7e6
add: negative tests for raw_define
coeff-aij Jan 12, 2026
55bc858
fix: tests for raw_define(removing unused code, add more description)
coeff-aij Jan 14, 2026
5f8e80e
change: rename raw_define -> raw_command, RawDefine -> RawCommand, etc.
coeff-aij Jan 14, 2026
d9e4a93
add: negative tests for raw_define
coeff-aij Jan 12, 2026
51f1afd
remove: FormatContext::raw_commands
coeff-aij Jan 14, 2026
c1458e6
fix: wrong description about the error on negative test
coeff-aij Jan 14, 2026
ace4d2a
fix: undo accidental renames
coeff-aij Jan 15, 2026
bc79866
change: delete annot::AnnotParser::parse_raw_definition() and procces…
coeff-aij Jan 15, 2026
4937071
fix: run `cargo fmt`
coeff-aij Jan 16, 2026
49f5691
remove: accidentally duplicated negative tests
coeff-aij Jan 16, 2026
cac76db
Update src/analyze/crate_.rs
coeff-aij Jan 20, 2026
f354b5d
Update src/chc.rs
coeff-aij Jan 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/analyze/annot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ pub fn extern_spec_fn_path() -> [Symbol; 2] {
[Symbol::intern("thrust"), Symbol::intern("extern_spec_fn")]
}

pub fn raw_command_path() -> [Symbol; 2] {
[Symbol::intern("thrust"), Symbol::intern("raw_command")]
}

/// A [`annot::Resolver`] implementation for resolving function parameters.
///
/// The parameter names and their sorts needs to be configured via
Expand Down
33 changes: 33 additions & 0 deletions src/analyze/crate_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::collections::HashSet;

use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_middle::ty::{self as mir_ty, TyCtxt};
use rustc_span::def_id::{DefId, LocalDefId};

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

impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> {
fn analyze_raw_command_annot(&mut self) {
for attrs in self.tcx.get_attrs_by_path(
CRATE_DEF_ID.to_def_id(),
&analyze::annot::raw_command_path(),
) {
use rustc_ast::token::{LitKind, Token, TokenKind};
use rustc_ast::tokenstream::TokenTree;

let ts = analyze::annot::extract_annot_tokens(attrs.clone());
let tt = ts.trees().next().expect("string literal");

let raw_command = match tt {
TokenTree::Token(
Token {
kind: TokenKind::Literal(lit),
..
},
_,
) if lit.kind == LitKind::Str => lit.symbol.to_string(),
_ => panic!("invalid raw_command annotation"),
};

self.ctx
.system
.borrow_mut()
.push_raw_command(chc::RawCommand {
command: raw_command,
});
}
}

fn refine_local_defs(&mut self) {
for local_def_id in self.tcx.mir_keys(()) {
if self.tcx.def_kind(*local_def_id).is_fn_like() {
Expand Down Expand Up @@ -187,6 +219,7 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> {
let span = tracing::debug_span!("crate", krate = %self.tcx.crate_name(rustc_span::def_id::LOCAL_CRATE));
let _guard = span.enter();

self.analyze_raw_command_annot();
self.refine_local_defs();
self.analyze_local_defs();
self.assert_callable_entry();
Expand Down
13 changes: 13 additions & 0 deletions src/chc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,14 @@ impl Clause {
}
}

/// A command specified using `thrust::raw_command` attribute
///
/// Those will be directly inserted into the generated SMT-LIB2 file.
#[derive(Debug, Clone)]
pub struct RawCommand {
pub command: String,
}

/// A selector for a datatype constructor.
///
/// A selector is a function that extracts a field from a datatype value.
Expand Down Expand Up @@ -1655,6 +1663,7 @@ pub struct PredVarDef {
/// A CHC system.
#[derive(Debug, Clone, Default)]
pub struct System {
pub raw_commands: Vec<RawCommand>,
pub datatypes: Vec<Datatype>,
pub clauses: IndexVec<ClauseId, Clause>,
pub pred_vars: IndexVec<PredVarId, PredVarDef>,
Expand All @@ -1665,6 +1674,10 @@ impl System {
self.pred_vars.push(PredVarDef { sig, debug_info })
}

pub fn push_raw_command(&mut self, raw_command: RawCommand) {
self.raw_commands.push(raw_command)
}

pub fn push_clause(&mut self, clause: Clause) -> Option<ClauseId> {
if clause.is_nop() {
return None;
Expand Down
23 changes: 23 additions & 0 deletions src/chc/smtlib2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,24 @@ impl<'ctx, 'a> Clause<'ctx, 'a> {
}
}

/// A wrapper around a [`chc::RawCommand`] that provides a [`std::fmt::Display`] implementation in SMT-LIB2 format.
#[derive(Debug, Clone)]
pub struct RawCommand<'a> {
inner: &'a chc::RawCommand,
}

impl<'a> std::fmt::Display for RawCommand<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner.command,)
}
}

impl<'a> RawCommand<'a> {
pub fn new(inner: &'a chc::RawCommand) -> Self {
Self { inner }
}
}

/// A wrapper around a [`chc::DatatypeSelector`] that provides a [`std::fmt::Display`] implementation in SMT-LIB2 format.
#[derive(Debug, Clone)]
pub struct DatatypeSelector<'ctx, 'a> {
Expand Down Expand Up @@ -555,6 +573,11 @@ impl<'a> std::fmt::Display for System<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "(set-logic HORN)\n")?;

// insert command from #![thrust::raw_command()] here
for raw_command in &self.inner.raw_commands {
writeln!(f, "{}\n", RawCommand::new(raw_command))?;
}

writeln!(f, "{}\n", Datatypes::new(&self.ctx, self.ctx.datatypes()))?;
for datatype in self.ctx.datatypes() {
writeln!(f, "{}", DatatypeDiscrFun::new(&self.ctx, datatype))?;
Expand Down
2 changes: 2 additions & 0 deletions src/chc/unbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ pub fn unbox(system: System) -> System {
clauses,
pred_vars,
datatypes,
raw_commands,
} = system;
let datatypes = datatypes.into_iter().map(unbox_datatype).collect();
let clauses = clauses.into_iter().map(unbox_clause).collect();
Expand All @@ -169,5 +170,6 @@ pub fn unbox(system: System) -> System {
clauses,
pred_vars,
datatypes,
raw_commands,
}
}
23 changes: 23 additions & 0 deletions tests/ui/pass/annot_raw_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@check-pass
//@compile-flags: -Adead_code -C debug-assertions=off

// Insert commands written in SMT-LIB2 format into .smt2 file directly.
// This feature is intended for debug or experiment purpose.
#![feature(custom_inner_attributes)]
#![thrust::raw_command("(define-fun is_double ((x Int) (doubled_x Int)) Bool
(=
(* x 2)
doubled_x
)
)")]

#[thrust::requires(true)]
#[thrust::ensures(result == 2 * x)]
fn double(x: i64) -> i64 {
x + x
}

fn main() {
let a = 3;
assert!(double(a) == 6);
}
31 changes: 31 additions & 0 deletions tests/ui/pass/annot_raw_command_multi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//@check-pass
//@compile-flags: -Adead_code -C debug-assertions=off

// Insert commands written in SMT-LIB2 format into .smt2 file directly.
// This feature is intended for debug or experiment purpose.
#![feature(custom_inner_attributes)]
#![thrust::raw_command("(define-fun is_double ((x Int) (doubled_x Int)) Bool
(=
(* x 2)
doubled_x
)
)")]

// multiple raw commands can be inserted.
#![thrust::raw_command("(define-fun is_triple ((x Int) (tripled_x Int)) Bool
(=
(* x 3)
tripled_x
)
)")]

#[thrust::requires(true)]
#[thrust::ensures(result == 2 * x)]
fn double(x: i64) -> i64 {
x + x
}

fn main() {
let a = 3;
assert!(double(a) == 6);
}