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,
42 FunSort, Pred, 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 FunSort = $crate::FunSort<FixpointTypes>;
128 pub type FunBody = $crate::FunBody<FixpointTypes>;
129 pub type Task = $crate::Task<FixpointTypes>;
130 pub type Qualifier = $crate::Qualifier<FixpointTypes>;
131 pub type Sort = $crate::Sort<FixpointTypes>;
132 pub type SortCtor = $crate::SortCtor<FixpointTypes>;
133 pub type SortDecl = $crate::SortDecl<FixpointTypes>;
134 pub type DataDecl = $crate::DataDecl<FixpointTypes>;
135 pub type DataCtor = $crate::DataCtor<FixpointTypes>;
136 pub type DataField = $crate::DataField<FixpointTypes>;
137 pub type Bind = $crate::Bind<FixpointTypes>;
138 pub type Constant = $crate::Constant<FixpointTypes>;
139 pub use $crate::{BinOp, BinRel, BoundVar, ThyFunc};
140 }
141
142 impl $crate::Types for fixpoint_generated::FixpointTypes {
143 type Sort = $sort;
144 type KVar = $kvar;
145 type Var = $var;
146 type String = $str;
147 type Tag = $tag;
148 }
149 };
150}
151
152#[derive_where(Hash, Clone, Debug)]
153pub struct ConstDecl<T: Types> {
154 pub name: T::Var,
155 pub sort: Sort<T>,
156 #[derive_where(skip)]
157 pub comment: Option<String>,
158}
159
160#[derive_where(Hash, Debug)]
161pub struct FunDef<T: Types> {
162 pub name: T::Var,
163 pub sort: FunSort<T>,
164 pub body: Option<FunBody<T>>,
165 #[derive_where(skip)]
166 pub comment: Option<String>,
167}
168
169#[derive_where(Hash, Debug)]
170pub struct FunBody<T: Types> {
171 pub args: Vec<T::Var>,
172 pub expr: Expr<T>,
173}
174
175#[derive_where(Hash)]
176pub struct Task<T: Types> {
177 #[derive_where(skip)]
178 pub comments: Vec<String>,
179 pub constants: Vec<ConstDecl<T>>,
180 pub data_decls: Vec<DataDecl<T>>,
181 pub define_funs: Vec<FunDef<T>>,
182 pub kvars: Vec<KVarDecl<T>>,
183 pub constraint: Constraint<T>,
184 pub qualifiers: Vec<Qualifier<T>>,
185 pub scrape_quals: bool,
186 pub solver: SmtSolver,
187}
188
189#[derive(Clone, Copy, Hash)]
190pub enum SmtSolver {
191 Z3,
192 CVC5,
193}
194
195impl fmt::Display for SmtSolver {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 match self {
198 SmtSolver::Z3 => write!(f, "z3"),
199 SmtSolver::CVC5 => write!(f, "cvc5"),
200 }
201 }
202}
203
204#[derive(Serialize, Deserialize, Debug, Clone)]
205#[serde(
206 tag = "tag",
207 content = "contents",
208 bound(deserialize = "Tag: FromStr", serialize = "Tag: ToString")
209)]
210pub enum FixpointStatus<Tag> {
211 Safe(Stats),
212 Unsafe(Stats, Vec<Error<Tag>>),
213 Crash(CrashInfo),
214}
215
216#[derive(Serialize, Deserialize, Debug, Clone)]
217#[serde(bound(deserialize = "Tag: FromStr", serialize = "Tag: ToString"))]
218pub struct FixpointResult<Tag> {
219 pub status: FixpointStatus<Tag>,
220 pub solution: Vec<KVarBind>,
221 #[serde(rename = "nonCutsSolution")]
222 pub non_cuts_solution: Vec<KVarBind>,
223}
224
225#[derive(Serialize, Deserialize, Debug, Clone)]
226pub struct KVarBind {
227 pub kvar: String,
228 pub val: String,
229}
230
231impl KVarBind {
232 pub fn dump(&self) -> String {
233 format!("{} := {}", self.kvar, self.val)
234 }
235}
236
237impl<Tag> FixpointStatus<Tag> {
238 pub fn is_safe(&self) -> bool {
239 matches!(self, FixpointStatus::Safe(_))
240 }
241
242 pub fn merge(self, other: FixpointStatus<Tag>) -> Self {
243 use FixpointStatus as FR;
244 match (self, other) {
245 (FR::Safe(stats1), FR::Safe(stats2)) => FR::Safe(stats1.merge(&stats2)),
246 (FR::Safe(stats1), FR::Unsafe(stats2, errors)) => {
247 FR::Unsafe(stats1.merge(&stats2), errors)
248 }
249 (FR::Unsafe(stats1, mut errors1), FR::Unsafe(stats2, errors2)) => {
250 errors1.extend(errors2);
251 FR::Unsafe(stats1.merge(&stats2), errors1)
252 }
253 (FR::Unsafe(stats1, errors), FR::Safe(stats2)) => {
254 FR::Unsafe(stats1.merge(&stats2), errors)
255 }
256 (FR::Crash(info1), FR::Crash(info2)) => FR::Crash(info1.merge(info2)),
257 (FR::Crash(info), _) => FR::Crash(info),
258 (_, FR::Crash(info)) => FR::Crash(info),
259 }
260 }
261}
262
263#[derive(Debug, Clone)]
264pub struct Error<Tag> {
265 pub id: i32,
266 pub tag: Tag,
267}
268
269#[derive(Debug, Serialize, Deserialize, Default, Clone)]
270#[serde(rename_all = "camelCase")]
271pub struct Stats {
272 pub num_cstr: i32,
273 pub num_iter: i32,
274 pub num_chck: i32,
275 pub num_vald: i32,
276}
277
278impl Stats {
279 pub fn merge(&self, other: &Stats) -> Self {
280 Stats {
281 num_cstr: self.num_cstr + other.num_cstr,
282 num_iter: self.num_iter + other.num_iter,
283 num_chck: self.num_chck + other.num_chck,
284 num_vald: self.num_vald + other.num_vald,
285 }
286 }
287}
288
289#[derive(Serialize, Deserialize, Debug, Clone)]
290pub struct CrashInfo(Vec<serde_json::Value>);
291
292impl CrashInfo {
293 pub fn merge(self, other: CrashInfo) -> Self {
294 let mut v = self.0;
295 v.extend(other.0);
296 CrashInfo(v)
297 }
298}
299
300#[derive_where(Debug, Clone, Hash)]
301pub struct KVarDecl<T: Types> {
302 pub kvid: T::KVar,
303 pub sorts: Vec<Sort<T>>,
304 #[derive_where(skip)]
305 pub comment: String,
306}
307
308impl<T: Types> Task<T> {
309 pub fn hash_with_default(&self) -> u64 {
310 let mut hasher = DefaultHasher::new();
311 self.hash(&mut hasher);
312 hasher.finish()
313 }
314
315 #[cfg(feature = "rust-fixpoint")]
316 pub fn run(&self) -> io::Result<FixpointResult<T::Tag>> {
317 let mut cstr_with_env = ConstraintWithEnv::new(
318 self.data_decls.clone(),
319 self.kvars.clone(),
320 self.qualifiers.clone(),
321 self.constants.clone(),
322 self.constraint.clone(),
323 );
324 Ok(FixpointResult {
325 status: cstr_with_env.is_satisfiable(),
326 solution: vec![],
327 non_cuts_solution: vec![],
328 })
329 }
330
331 #[cfg(not(feature = "rust-fixpoint"))]
332 pub fn run(&self) -> io::Result<FixpointResult<T::Tag>> {
333 let mut child = Command::new("fixpoint")
334 .arg("-q")
335 .arg("--stdin")
336 .arg("--json")
337 .arg("--allowho")
338 .arg("--allowhoqs")
339 .arg(format!("--solver={}", self.solver))
340 .stdin(Stdio::piped())
341 .stdout(Stdio::piped())
342 .stderr(Stdio::piped())
343 .spawn()?;
344 let mut stdin = None;
345 std::mem::swap(&mut stdin, &mut child.stdin);
346 {
347 let mut w = BufWriter::new(stdin.unwrap());
348 writeln!(w, "{self}")?;
349 }
350 let out = child.wait_with_output()?;
351
352 serde_json::from_slice(&out.stdout).map_err(|err| {
353 if !out.stderr.is_empty() {
356 let stderr = std::str::from_utf8(&out.stderr)
357 .unwrap_or("fixpoint exited with a non-zero return code");
358 io::Error::other(stderr)
359 } else {
360 err.into()
361 }
362 })
363 }
364}
365
366impl<T: Types> KVarDecl<T> {
367 pub fn new(kvid: T::KVar, sorts: Vec<Sort<T>>, comment: String) -> Self {
368 Self { kvid, sorts, comment }
369 }
370}
371
372#[derive(Serialize, Deserialize)]
373struct ErrorInner(i32, String);
374
375impl<Tag: ToString> Serialize for Error<Tag> {
376 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
377 where
378 S: serde::Serializer,
379 {
380 ErrorInner(self.id, self.tag.to_string()).serialize(serializer)
381 }
382}
383
384impl<'de, Tag: FromStr> Deserialize<'de> for Error<Tag> {
385 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
386 where
387 D: serde::Deserializer<'de>,
388 {
389 let ErrorInner(id, tag) = Deserialize::deserialize(deserializer)?;
390 let tag = tag
391 .parse()
392 .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(&tag), &"valid tag"))?;
393 Ok(Error { id, tag })
394 }
395}
396
397#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
398#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
399pub enum ThyFunc {
400 StrLen,
402 StrConcat,
403 StrPrefixOf,
404 StrSuffixOf,
405 StrContains,
406
407 BvZeroExtend(u8),
409 BvSignExtend(u8),
410 IntToBv8,
411 Bv8ToInt,
412 IntToBv32,
413 Bv32ToInt,
414 IntToBv64,
415 Bv64ToInt,
416 BvUle,
417 BvSle,
418 BvUge,
419 BvSge,
420 BvUdiv,
421 BvSdiv,
422 BvSrem,
423 BvUrem,
424 BvLshr,
425 BvAshr,
426 BvAnd,
427 BvOr,
428 BvXor,
429 BvNot,
430 BvAdd,
431 BvNeg,
432 BvSub,
433 BvMul,
434 BvShl,
435 BvUgt,
436 BvSgt,
437 BvUlt,
438 BvSlt,
439
440 SetEmpty,
443 SetSng,
445 SetCup,
447 SetCap,
449 SetDif,
451 SetSub,
453 SetMem,
455
456 MapDefault,
459 MapSelect,
461 MapStore,
463}
464
465impl ThyFunc {
466 pub const ALL: [ThyFunc; 44] = [
467 ThyFunc::StrLen,
468 ThyFunc::StrConcat,
469 ThyFunc::StrPrefixOf,
470 ThyFunc::StrSuffixOf,
471 ThyFunc::StrContains,
472 ThyFunc::IntToBv8,
473 ThyFunc::Bv8ToInt,
474 ThyFunc::IntToBv32,
475 ThyFunc::Bv32ToInt,
476 ThyFunc::IntToBv64,
477 ThyFunc::Bv64ToInt,
478 ThyFunc::BvAdd,
479 ThyFunc::BvNeg,
480 ThyFunc::BvSub,
481 ThyFunc::BvShl,
482 ThyFunc::BvLshr,
483 ThyFunc::BvAshr,
484 ThyFunc::BvMul,
485 ThyFunc::BvUdiv,
486 ThyFunc::BvSdiv,
487 ThyFunc::BvUrem,
488 ThyFunc::BvSrem,
489 ThyFunc::BvAnd,
490 ThyFunc::BvOr,
491 ThyFunc::BvXor,
492 ThyFunc::BvNot,
493 ThyFunc::BvUle,
494 ThyFunc::BvSle,
495 ThyFunc::BvUge,
496 ThyFunc::BvSge,
497 ThyFunc::BvUgt,
498 ThyFunc::BvSgt,
499 ThyFunc::BvUlt,
500 ThyFunc::BvSlt,
501 ThyFunc::SetEmpty,
502 ThyFunc::SetSng,
503 ThyFunc::SetCup,
504 ThyFunc::SetMem,
505 ThyFunc::SetCap,
506 ThyFunc::SetDif,
507 ThyFunc::SetSub,
508 ThyFunc::MapDefault,
509 ThyFunc::MapSelect,
510 ThyFunc::MapStore,
511 ];
512}
513
514impl fmt::Display for ThyFunc {
515 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
516 match self {
517 ThyFunc::StrLen => write!(f, "strLen"),
518 ThyFunc::StrConcat => write!(f, "strConcat"),
519 ThyFunc::StrPrefixOf => write!(f, "strPrefixOf"),
520 ThyFunc::StrSuffixOf => write!(f, "strSuffixOf"),
521 ThyFunc::StrContains => write!(f, "strContains"),
522 ThyFunc::BvZeroExtend(size) => {
523 write!(f, "app (_ zero_extend {size})")
525 }
526 ThyFunc::BvSignExtend(size) => write!(f, "app (_ sign_extend {size})"),
527 ThyFunc::IntToBv32 => write!(f, "int_to_bv32"),
528 ThyFunc::Bv32ToInt => write!(f, "bv32_to_int"),
529 ThyFunc::IntToBv8 => write!(f, "int_to_bv8"),
530 ThyFunc::Bv8ToInt => write!(f, "bv8_to_int"),
531 ThyFunc::IntToBv64 => write!(f, "int_to_bv64"),
532 ThyFunc::Bv64ToInt => write!(f, "bv64_to_int"),
533 ThyFunc::BvUle => write!(f, "bvule"),
534 ThyFunc::BvSle => write!(f, "bvsle"),
535 ThyFunc::BvUge => write!(f, "bvuge"),
536 ThyFunc::BvSge => write!(f, "bvsge"),
537 ThyFunc::BvUdiv => write!(f, "bvudiv"),
538 ThyFunc::BvSdiv => write!(f, "bvsdiv"),
539 ThyFunc::BvUrem => write!(f, "bvurem"),
540 ThyFunc::BvSrem => write!(f, "bvsrem"),
541 ThyFunc::BvLshr => write!(f, "bvlshr"),
542 ThyFunc::BvAshr => write!(f, "bvashr"),
543 ThyFunc::BvAnd => write!(f, "bvand"),
544 ThyFunc::BvOr => write!(f, "bvor"),
545 ThyFunc::BvXor => write!(f, "bvxor"),
546 ThyFunc::BvNot => write!(f, "bvnot"),
547 ThyFunc::BvAdd => write!(f, "bvadd"),
548 ThyFunc::BvNeg => write!(f, "bvneg"),
549 ThyFunc::BvSub => write!(f, "bvsub"),
550 ThyFunc::BvMul => write!(f, "bvmul"),
551 ThyFunc::BvShl => write!(f, "bvshl"),
552 ThyFunc::BvUgt => write!(f, "bvugt"),
553 ThyFunc::BvSgt => write!(f, "bvsgt"),
554 ThyFunc::BvUlt => write!(f, "bvult"),
555 ThyFunc::BvSlt => write!(f, "bvslt"),
556 ThyFunc::SetEmpty => write!(f, "Set_empty"),
557 ThyFunc::SetSng => write!(f, "Set_sng"),
558 ThyFunc::SetCup => write!(f, "Set_cup"),
559 ThyFunc::SetCap => write!(f, "Set_cap"),
560 ThyFunc::SetDif => write!(f, "Set_dif"),
561 ThyFunc::SetMem => write!(f, "Set_mem"),
562 ThyFunc::SetSub => write!(f, "Set_sub"),
563 ThyFunc::MapDefault => write!(f, "Map_default"),
564 ThyFunc::MapSelect => write!(f, "Map_select"),
565 ThyFunc::MapStore => write!(f, "Map_store"),
566 }
567 }
568}