diff --git a/src/analyze.rs b/src/analyze.rs
index a4ffa5f4..6ba7faea 100644
--- a/src/analyze.rs
+++ b/src/analyze.rs
@@ -271,6 +271,14 @@ impl<'tcx> Analyzer<'tcx> {
self.def_ids.clone()
}
+ pub fn mark_uses_seq_concat(&self) {
+ self.system.borrow_mut().uses_seq_concat = true;
+ }
+
+ pub fn mark_uses_seq_subseq(&self) {
+ self.system.borrow_mut().uses_seq_subseq = true;
+ }
+
pub fn add_clause(&mut self, clause: chc::Clause) {
self.system.borrow_mut().push_clause(clause);
}
diff --git a/src/analyze/annot.rs b/src/analyze/annot.rs
index 76e76e22..ae74ca94 100644
--- a/src/analyze/annot.rs
+++ b/src/analyze/annot.rs
@@ -137,6 +137,62 @@ pub fn array_model_store_path() -> [Symbol; 3] {
]
}
+pub fn seq_model_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_model"),
+ ]
+}
+
+pub fn seq_empty_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_empty"),
+ ]
+}
+
+pub fn seq_singleton_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_singleton"),
+ ]
+}
+
+pub fn seq_len_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_len"),
+ ]
+}
+
+pub fn seq_push_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_push"),
+ ]
+}
+
+pub fn seq_concat_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_concat"),
+ ]
+}
+
+pub fn seq_subsequence_path() -> [Symbol; 3] {
+ [
+ Symbol::intern("thrust"),
+ Symbol::intern("def"),
+ Symbol::intern("seq_subsequence"),
+ ]
+}
+
pub fn exists_path() -> [Symbol; 3] {
[
Symbol::intern("thrust"),
diff --git a/src/analyze/annot_fn.rs b/src/analyze/annot_fn.rs
index 0249e4aa..7edd1c55 100644
--- a/src/analyze/annot_fn.rs
+++ b/src/analyze/annot_fn.rs
@@ -623,9 +623,18 @@ impl<'a, 'tcx> AnnotFnTranslator<'a, 'tcx> {
FormulaOrTerm::Term(term.tuple_proj(index))
}
ExprKind::Index(array, index, _) => {
+ let array_ty = self.expr_ty(array);
let array_term = self.to_term(array);
let index_term = self.to_term(index);
- FormulaOrTerm::Term(array_term.select(index_term))
+ let is_seq = array_ty
+ .ty_adt_def()
+ .is_some_and(|adt| Some(adt.did()) == self.def_ids.seq_model());
+ let array_inner = if is_seq {
+ array_term.tuple_proj(0)
+ } else {
+ array_term
+ };
+ FormulaOrTerm::Term(array_inner.select(index_term))
}
ExprKind::MethodCall(method, receiver, args, _) => {
if let Some(def_id) = self.typeck.type_dependent_def_id(hir.hir_id) {
@@ -644,6 +653,56 @@ impl<'a, 'tcx> AnnotFnTranslator<'a, 'tcx> {
let t = self.to_term(receiver);
return FormulaOrTerm::Term(t);
}
+ if Some(def_id) == self.def_ids.seq_len() {
+ assert!(args.is_empty(), "Seq::len does not take any arguments");
+ let t = self.to_term(receiver);
+ return FormulaOrTerm::Term(t.tuple_proj(1));
+ }
+ if Some(def_id) == self.def_ids.seq_push() {
+ assert_eq!(args.len(), 1, "Seq::push takes exactly 1 argument");
+ let t = self.to_term(receiver);
+ let v = self.to_term(&args[0]);
+ let arr = t.clone().tuple_proj(0);
+ let len = t.tuple_proj(1);
+ let new_arr = arr.store(len.clone(), v);
+ let new_len = len.add(chc::Term::int(1));
+ return FormulaOrTerm::Term(chc::Term::tuple(vec![new_arr, new_len]));
+ }
+ if Some(def_id) == self.def_ids.seq_concat() {
+ assert_eq!(args.len(), 1, "Seq::concat takes exactly 1 argument");
+ self.analyzer.mark_uses_seq_concat();
+ let t = self.to_term(receiver);
+ let other = self.to_term(&args[0]);
+ let a_arr = t.clone().tuple_proj(0);
+ let a_len = t.tuple_proj(1);
+ let b_arr = other.clone().tuple_proj(0);
+ let b_len = other.tuple_proj(1);
+ // The array half is the recursive SMT-defined
+ // function `seq_concat_arr_int(sa, sn, ta, tn)`;
+ // the length half is computed inline so length
+ // properties remain provable on any solver (the
+ // SMT obligation never has to mention the array).
+ let new_arr = chc::Term::App(
+ chc::Function::SEQ_CONCAT_ARR_INT,
+ vec![a_arr, a_len.clone(), b_arr, b_len.clone()],
+ );
+ let new_len = a_len.add(b_len);
+ return FormulaOrTerm::Term(chc::Term::tuple(vec![new_arr, new_len]));
+ }
+ if Some(def_id) == self.def_ids.seq_subsequence() {
+ assert_eq!(args.len(), 2, "Seq::subsequence takes exactly 2 arguments");
+ self.analyzer.mark_uses_seq_subseq();
+ let t = self.to_term(receiver);
+ let l = self.to_term(&args[0]);
+ let r = self.to_term(&args[1]);
+ let arr = t.tuple_proj(0);
+ let new_arr = chc::Term::App(
+ chc::Function::SEQ_SUBSEQ_ARR_INT,
+ vec![arr, l.clone(), r.clone()],
+ );
+ let new_len = r.sub(l);
+ return FormulaOrTerm::Term(chc::Term::tuple(vec![new_arr, new_len]));
+ }
}
unimplemented!("unsupported method call in formula: {:?}", method)
}
@@ -719,6 +778,24 @@ impl<'a, 'tcx> AnnotFnTranslator<'a, 'tcx> {
let t = self.to_term(&args[0]);
return FormulaOrTerm::Term(chc::Term::box_(t));
}
+ if Some(def_id) == self.def_ids.seq_empty() {
+ assert!(args.is_empty(), "Seq::empty does not take any arguments");
+ let arr = chc::Term::App(chc::Function::SEQ_DEFAULT_ARR_INT, vec![]);
+ return FormulaOrTerm::Term(chc::Term::tuple(vec![
+ arr,
+ chc::Term::int(0),
+ ]));
+ }
+ if Some(def_id) == self.def_ids.seq_singleton() {
+ assert_eq!(args.len(), 1, "Seq::singleton takes exactly 1 argument");
+ let v = self.to_term(&args[0]);
+ let arr = chc::Term::App(chc::Function::SEQ_DEFAULT_ARR_INT, vec![]);
+ let new_arr = arr.store(chc::Term::int(0), v);
+ return FormulaOrTerm::Term(chc::Term::tuple(vec![
+ new_arr,
+ chc::Term::int(1),
+ ]));
+ }
if let rustc_hir::def::DefKind::Ctor(ctor_of, _) = def_kind {
let terms = args.iter().map(|e| self.to_term(e)).collect();
match ctor_of {
diff --git a/src/analyze/did_cache.rs b/src/analyze/did_cache.rs
index 15149120..650d3756 100644
--- a/src/analyze/did_cache.rs
+++ b/src/analyze/did_cache.rs
@@ -24,6 +24,14 @@ struct DefIds {
box_model_new: OnceCell