1#![cfg_attr(feature = "nightly", feature(rustc_private))]
5
6#[cfg(feature = "nightly")]
7extern crate rustc_macros;
8#[cfg(feature = "nightly")]
9extern crate rustc_serialize;
10#[cfg(feature = "nightly")]
11extern crate rustc_span;
12
13mod constraint;
14#[cfg(feature = "rust-fixpoint")]
15mod constraint_fragments;
16#[cfg(feature = "rust-fixpoint")]
17mod constraint_solving;
18mod constraint_with_env;
19#[cfg(feature = "rust-fixpoint")]
20mod cstr2smt2;
21mod format;
22#[cfg(feature = "rust-fixpoint")]
23mod graph;
24pub mod parser;
25pub mod sexp;
26
27use std::{
28 collections::{HashMap, hash_map::DefaultHasher},
29 fmt::{self, Debug},
30 hash::{Hash, Hasher},
31 io,
32 str::FromStr,
33};
34#[cfg(not(feature = "rust-fixpoint"))]
35use std::{
36 io::{BufWriter, Write as IOWrite},
37 process::{Command, Stdio},
38};
39
40pub use constraint::{
41 BinOp, BinRel, Bind, BoundVar, Constant, Constraint, DataCtor, DataDecl, DataField, Expr, Pred,
42 Qualifier, Sort, SortCtor, SortDecl,
43};
44use derive_where::derive_where;
45#[cfg(feature = "nightly")]
46use rustc_macros::{Decodable, Encodable};
47use serde::{Deserialize, Serialize, de};
48
49pub type Assignments<'a, T> = HashMap<<T as Types>::KVar, Vec<(&'a Qualifier<T>, Vec<usize>)>>;
51
52#[cfg(feature = "rust-fixpoint")]
53use crate::constraint_with_env::ConstraintWithEnv;
54
55pub trait Types {
56 type Sort: Identifier + Hash + Clone + Debug + Eq;
57 type KVar: Identifier + Hash + Clone + Debug + Eq;
58 type Var: Identifier + Hash + Clone + Debug + Eq;
59 type String: FixpointFmt + Hash + Clone + Debug;
60 type Tag: fmt::Display + FromStr + Hash + Clone + Debug;
61}
62
63pub trait FixpointFmt: Sized {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
65
66 fn display(&self) -> impl fmt::Display {
68 struct DisplayAdapter<T>(T);
69 impl<T: FixpointFmt> std::fmt::Display for DisplayAdapter<&T> {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 FixpointFmt::fmt(self.0, f)
72 }
73 }
74 DisplayAdapter(self)
75 }
76}
77
78pub trait Identifier: Sized {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
80
81 fn display(&self) -> impl fmt::Display {
83 struct DisplayAdapter<T>(T);
84 impl<T: Identifier> fmt::Display for DisplayAdapter<&T> {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 Identifier::fmt(self.0, f)
87 }
88 }
89 DisplayAdapter(self)
90 }
91}
92
93impl Identifier for &str {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "{self}")
96 }
97}
98
99impl FixpointFmt for u32 {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "{self}")
102 }
103}
104
105impl FixpointFmt for String {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 write!(f, "\"{self}\"")
108 }
109}
110
111#[macro_export]
112macro_rules! declare_types {
113 ( type Sort = $sort:ty;
114 type KVar = $kvar:ty;
115 type Var = $var:ty;
116 type String = $str:ty;
117 type Tag = $tag:ty;
118 ) => {
119 pub mod fixpoint_generated {
120 pub struct FixpointTypes;
121 pub type Expr = $crate::Expr<FixpointTypes>;
122 pub type Pred = $crate::Pred<FixpointTypes>;
123 pub type Constraint = $crate::Constraint<FixpointTypes>;
124 pub type KVarDecl = $crate::KVarDecl<FixpointTypes>;
125 pub type ConstDecl = $crate::ConstDecl<FixpointTypes>;
126 pub type FunDef = $crate::FunDef<FixpointTypes>;
127 pub type Task = $crate::Task<FixpointTypes>;
128 pub type Qualifier = $crate::Qualifier<FixpointTypes>;
129 pub type Sort = $crate::Sort<FixpointTypes>;
130 pub type SortCtor = $crate::SortCtor<FixpointTypes>;
131 pub type SortDecl = $crate::SortDecl<FixpointTypes>;
132 pub type DataDecl = $crate::DataDecl<FixpointTypes>;
133 pub type DataCtor = $crate::DataCtor<FixpointTypes>;
134 pub type DataField = $crate::DataField<FixpointTypes>;
135 pub type Bind = $crate::Bind<FixpointTypes>;
136 pub type Constant = $crate::Constant<FixpointTypes>;
137 pub use $crate::{BinOp, BinRel, BoundVar, ThyFunc};
138 }
139
140 impl $crate::Types for fixpoint_generated::FixpointTypes {
141 type Sort = $sort;
142 type KVar = $kvar;
143 type Var = $var;
144 type String = $str;
145 type Tag = $tag;
146 }
147 };
148}
149
150#[derive_where(Hash, Clone, Debug)]
151pub struct ConstDecl<T: Types> {
152 pub name: T::Var,
153 pub sort: Sort<T>,
154 #[derive_where(skip)]
155 pub comment: Option<String>,
156}
157
158#[derive_where(Hash)]
159pub struct FunDef<T: Types> {
160 pub name: T::Var,
161 pub args: Vec<(T::Var, Sort<T>)>,
162 pub out: Sort<T>,
163 pub body: Expr<T>,
164 #[derive_where(skip)]
165 pub comment: Option<String>,
166}
167
168#[derive_where(Hash)]
169pub struct Task<T: Types> {
170 #[derive_where(skip)]
171 pub comments: Vec<String>,
172 pub constants: Vec<ConstDecl<T>>,
173 pub data_decls: Vec<DataDecl<T>>,
174 pub define_funs: Vec<FunDef<T>>,
175 pub kvars: Vec<KVarDecl<T>>,
176 pub constraint: Constraint<T>,
177 pub qualifiers: Vec<Qualifier<T>>,
178 pub scrape_quals: bool,
179 pub solver: SmtSolver,
180}
181
182#[derive(Clone, Copy, Hash)]
183pub enum SmtSolver {
184 Z3,
185 CVC5,
186}
187
188impl fmt::Display for SmtSolver {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 match self {
191 SmtSolver::Z3 => write!(f, "z3"),
192 SmtSolver::CVC5 => write!(f, "cvc5"),
193 }
194 }
195}
196
197#[derive(Serialize, Deserialize, Debug, Clone)]
198#[serde(
199 tag = "tag",
200 content = "contents",
201 bound(deserialize = "Tag: FromStr", serialize = "Tag: ToString")
202)]
203pub enum FixpointStatus<Tag> {
204 Safe(Stats),
205 Unsafe(Stats, Vec<Error<Tag>>),
206 Crash(CrashInfo),
207}
208
209#[derive(Serialize, Deserialize, Debug, Clone)]
210#[serde(bound(deserialize = "Tag: FromStr", serialize = "Tag: ToString"))]
211pub struct FixpointResult<Tag> {
212 pub status: FixpointStatus<Tag>,
213 pub solution: Vec<KVarBind>,
214 #[serde(rename = "nonCutsSolution")]
215 pub non_cuts_solution: Vec<KVarBind>,
216}
217
218#[derive(Serialize, Deserialize, Debug, Clone)]
219pub struct KVarBind {
220 pub kvar: String,
221 pub val: String,
222}
223
224impl KVarBind {
225 pub fn dump(&self) -> String {
226 format!("{} := {}", self.kvar, self.val)
227 }
228}
229
230impl<Tag> FixpointStatus<Tag> {
231 pub fn is_safe(&self) -> bool {
232 matches!(self, FixpointStatus::Safe(_))
233 }
234
235 pub fn merge(self, other: FixpointStatus<Tag>) -> Self {
236 use FixpointStatus as FR;
237 match (self, other) {
238 (FR::Safe(stats1), FR::Safe(stats2)) => FR::Safe(stats1.merge(&stats2)),
239 (FR::Safe(stats1), FR::Unsafe(stats2, errors)) => {
240 FR::Unsafe(stats1.merge(&stats2), errors)
241 }
242 (FR::Unsafe(stats1, mut errors1), FR::Unsafe(stats2, errors2)) => {
243 errors1.extend(errors2);
244 FR::Unsafe(stats1.merge(&stats2), errors1)
245 }
246 (FR::Unsafe(stats1, errors), FR::Safe(stats2)) => {
247 FR::Unsafe(stats1.merge(&stats2), errors)
248 }
249 (FR::Crash(info1), FR::Crash(info2)) => FR::Crash(info1.merge(info2)),
250 (FR::Crash(info), _) => FR::Crash(info),
251 (_, FR::Crash(info)) => FR::Crash(info),
252 }
253 }
254}
255
256#[derive(Debug, Clone)]
257pub struct Error<Tag> {
258 pub id: i32,
259 pub tag: Tag,
260}
261
262#[derive(Debug, Serialize, Deserialize, Default, Clone)]
263#[serde(rename_all = "camelCase")]
264pub struct Stats {
265 pub num_cstr: i32,
266 pub num_iter: i32,
267 pub num_chck: i32,
268 pub num_vald: i32,
269}
270
271impl Stats {
272 pub fn merge(&self, other: &Stats) -> Self {
273 Stats {
274 num_cstr: self.num_cstr + other.num_cstr,
275 num_iter: self.num_iter + other.num_iter,
276 num_chck: self.num_chck + other.num_chck,
277 num_vald: self.num_vald + other.num_vald,
278 }
279 }
280}
281
282#[derive(Serialize, Deserialize, Debug, Clone)]
283pub struct CrashInfo(Vec<serde_json::Value>);
284
285impl CrashInfo {
286 pub fn merge(self, other: CrashInfo) -> Self {
287 let mut v = self.0;
288 v.extend(other.0);
289 CrashInfo(v)
290 }
291}
292
293#[derive_where(Debug, Clone, Hash)]
294pub struct KVarDecl<T: Types> {
295 pub kvid: T::KVar,
296 pub sorts: Vec<Sort<T>>,
297 #[derive_where(skip)]
298 pub comment: String,
299}
300
301impl<T: Types> Task<T> {
302 pub fn hash_with_default(&self) -> u64 {
303 let mut hasher = DefaultHasher::new();
304 self.hash(&mut hasher);
305 hasher.finish()
306 }
307
308 #[cfg(feature = "rust-fixpoint")]
309 pub fn run(&self) -> io::Result<FixpointResult<T::Tag>> {
310 let mut cstr_with_env = ConstraintWithEnv::new(
311 self.data_decls.clone(),
312 self.kvars.clone(),
313 self.qualifiers.clone(),
314 self.constants.clone(),
315 self.constraint.clone(),
316 );
317 Ok(FixpointResult {
318 status: cstr_with_env.is_satisfiable(),
319 solution: vec![],
320 non_cuts_solution: vec![],
321 })
322 }
323
324 #[cfg(not(feature = "rust-fixpoint"))]
325 pub fn run(&self) -> io::Result<FixpointResult<T::Tag>> {
326 let mut child = Command::new("fixpoint")
327 .arg("-q")
328 .arg("--stdin")
329 .arg("--json")
330 .arg("--allowho")
331 .arg("--allowhoqs")
332 .arg(format!("--solver={}", self.solver))
333 .stdin(Stdio::piped())
334 .stdout(Stdio::piped())
335 .stderr(Stdio::piped())
336 .spawn()?;
337 let mut stdin = None;
338 std::mem::swap(&mut stdin, &mut child.stdin);
339 {
340 let mut w = BufWriter::new(stdin.unwrap());
341 writeln!(w, "{self}")?;
342 }
343 let out = child.wait_with_output()?;
344
345 serde_json::from_slice(&out.stdout).map_err(|err| {
346 if !out.stderr.is_empty() {
349 let stderr = std::str::from_utf8(&out.stderr)
350 .unwrap_or("fixpoint exited with a non-zero return code");
351 io::Error::other(stderr)
352 } else {
353 err.into()
354 }
355 })
356 }
357}
358
359impl<T: Types> KVarDecl<T> {
360 pub fn new(kvid: T::KVar, sorts: Vec<Sort<T>>, comment: String) -> Self {
361 Self { kvid, sorts, comment }
362 }
363}
364
365#[derive(Serialize, Deserialize)]
366struct ErrorInner(i32, String);
367
368impl<Tag: ToString> Serialize for Error<Tag> {
369 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
370 where
371 S: serde::Serializer,
372 {
373 ErrorInner(self.id, self.tag.to_string()).serialize(serializer)
374 }
375}
376
377impl<'de, Tag: FromStr> Deserialize<'de> for Error<Tag> {
378 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
379 where
380 D: serde::Deserializer<'de>,
381 {
382 let ErrorInner(id, tag) = Deserialize::deserialize(deserializer)?;
383 let tag = tag
384 .parse()
385 .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(&tag), &"valid tag"))?;
386 Ok(Error { id, tag })
387 }
388}
389
390#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
391#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
392pub enum ThyFunc {
393 StrLen,
395
396 BvZeroExtend(u8),
398 BvSignExtend(u8),
399 IntToBv8,
400 Bv8ToInt,
401 IntToBv32,
402 Bv32ToInt,
403 IntToBv64,
404 Bv64ToInt,
405 BvUle,
406 BvSle,
407 BvUge,
408 BvSge,
409 BvUdiv,
410 BvSdiv,
411 BvSrem,
412 BvUrem,
413 BvLshr,
414 BvAshr,
415 BvAnd,
416 BvOr,
417 BvXor,
418 BvNot,
419 BvAdd,
420 BvNeg,
421 BvSub,
422 BvMul,
423 BvShl,
424 BvUgt,
425 BvSgt,
426 BvUlt,
427 BvSlt,
428
429 SetEmpty,
432 SetSng,
434 SetCup,
436 SetCap,
438 SetDif,
440 SetSub,
442 SetMem,
444
445 MapDefault,
448 MapSelect,
450 MapStore,
452}
453
454impl ThyFunc {
455 pub const ALL: [ThyFunc; 40] = [
456 ThyFunc::StrLen,
457 ThyFunc::IntToBv8,
458 ThyFunc::Bv8ToInt,
459 ThyFunc::IntToBv32,
460 ThyFunc::Bv32ToInt,
461 ThyFunc::IntToBv64,
462 ThyFunc::Bv64ToInt,
463 ThyFunc::BvAdd,
464 ThyFunc::BvNeg,
465 ThyFunc::BvSub,
466 ThyFunc::BvShl,
467 ThyFunc::BvLshr,
468 ThyFunc::BvAshr,
469 ThyFunc::BvMul,
470 ThyFunc::BvUdiv,
471 ThyFunc::BvSdiv,
472 ThyFunc::BvUrem,
473 ThyFunc::BvSrem,
474 ThyFunc::BvAnd,
475 ThyFunc::BvOr,
476 ThyFunc::BvXor,
477 ThyFunc::BvNot,
478 ThyFunc::BvUle,
479 ThyFunc::BvSle,
480 ThyFunc::BvUge,
481 ThyFunc::BvSge,
482 ThyFunc::BvUgt,
483 ThyFunc::BvSgt,
484 ThyFunc::BvUlt,
485 ThyFunc::BvSlt,
486 ThyFunc::SetEmpty,
487 ThyFunc::SetSng,
488 ThyFunc::SetCup,
489 ThyFunc::SetMem,
490 ThyFunc::SetCap,
491 ThyFunc::SetDif,
492 ThyFunc::SetSub,
493 ThyFunc::MapDefault,
494 ThyFunc::MapSelect,
495 ThyFunc::MapStore,
496 ];
497}
498
499impl fmt::Display for ThyFunc {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 match self {
502 ThyFunc::StrLen => write!(f, "strLen"),
503 ThyFunc::BvZeroExtend(size) => {
504 write!(f, "app (_ zero_extend {size})")
506 }
507 ThyFunc::BvSignExtend(size) => write!(f, "app (_ sign_extend {size})"),
508 ThyFunc::IntToBv32 => write!(f, "int_to_bv32"),
509 ThyFunc::Bv32ToInt => write!(f, "bv32_to_int"),
510 ThyFunc::IntToBv8 => write!(f, "int_to_bv8"),
511 ThyFunc::Bv8ToInt => write!(f, "bv8_to_int"),
512 ThyFunc::IntToBv64 => write!(f, "int_to_bv64"),
513 ThyFunc::Bv64ToInt => write!(f, "bv64_to_int"),
514 ThyFunc::BvUle => write!(f, "bvule"),
515 ThyFunc::BvSle => write!(f, "bvsle"),
516 ThyFunc::BvUge => write!(f, "bvuge"),
517 ThyFunc::BvSge => write!(f, "bvsge"),
518 ThyFunc::BvUdiv => write!(f, "bvudiv"),
519 ThyFunc::BvSdiv => write!(f, "bvsdiv"),
520 ThyFunc::BvUrem => write!(f, "bvurem"),
521 ThyFunc::BvSrem => write!(f, "bvsrem"),
522 ThyFunc::BvLshr => write!(f, "bvlshr"),
523 ThyFunc::BvAshr => write!(f, "bvashr"),
524 ThyFunc::BvAnd => write!(f, "bvand"),
525 ThyFunc::BvOr => write!(f, "bvor"),
526 ThyFunc::BvXor => write!(f, "bvxor"),
527 ThyFunc::BvNot => write!(f, "bvnot"),
528 ThyFunc::BvAdd => write!(f, "bvadd"),
529 ThyFunc::BvNeg => write!(f, "bvneg"),
530 ThyFunc::BvSub => write!(f, "bvsub"),
531 ThyFunc::BvMul => write!(f, "bvmul"),
532 ThyFunc::BvShl => write!(f, "bvshl"),
533 ThyFunc::BvUgt => write!(f, "bvugt"),
534 ThyFunc::BvSgt => write!(f, "bvsgt"),
535 ThyFunc::BvUlt => write!(f, "bvult"),
536 ThyFunc::BvSlt => write!(f, "bvslt"),
537 ThyFunc::SetEmpty => write!(f, "Set_empty"),
538 ThyFunc::SetSng => write!(f, "Set_sng"),
539 ThyFunc::SetCup => write!(f, "Set_cup"),
540 ThyFunc::SetCap => write!(f, "Set_cap"),
541 ThyFunc::SetDif => write!(f, "Set_dif"),
542 ThyFunc::SetMem => write!(f, "Set_mem"),
543 ThyFunc::SetSub => write!(f, "Set_sub"),
544 ThyFunc::MapDefault => write!(f, "Map_default"),
545 ThyFunc::MapSelect => write!(f, "Map_select"),
546 ThyFunc::MapStore => write!(f, "Map_store"),
547 }
548 }
549}