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