Skip to content

Commit a4be706

Browse files
committed
Support existential quantification in formula_fn
1 parent 362cec2 commit a4be706

6 files changed

Lines changed: 138 additions & 0 deletions

File tree

src/analyze/annot.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ pub fn box_model_new_path() -> [Symbol; 3] {
125125
]
126126
}
127127

128+
pub fn exists_path() -> [Symbol; 3] {
129+
[
130+
Symbol::intern("thrust"),
131+
Symbol::intern("def"),
132+
Symbol::intern("exists"),
133+
]
134+
}
135+
128136
/// A [`annot::Resolver`] implementation for resolving function parameters.
129137
///
130138
/// The parameter names and their sorts needs to be configured via

src/analyze/annot_fn.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,13 @@ impl<'tcx> AnnotFnTranslator<'tcx> {
201201
self.tcx.normalize_erasing_regions(param_env, instantiated)
202202
}
203203

204+
fn pat_ty(&self, pat: &'tcx rustc_hir::Pat<'tcx>) -> mir_ty::Ty<'tcx> {
205+
let ty = self.typeck.pat_ty(pat);
206+
let instantiated = mir_ty::EarlyBinder::bind(ty).instantiate(self.tcx, self.generic_args);
207+
let param_env = mir_ty::ParamEnv::reveal_all();
208+
self.tcx.normalize_erasing_regions(param_env, instantiated)
209+
}
210+
204211
pub fn to_formula_fn(&self) -> FormulaFn<'tcx> {
205212
let formula = self.to_formula(self.body.value);
206213
let params = self
@@ -376,6 +383,52 @@ impl<'tcx> AnnotFnTranslator<'tcx> {
376383
if let ExprKind::Path(qpath) = &func_expr.kind {
377384
let res = self.typeck.qpath_res(qpath, func_expr.hir_id);
378385
if let rustc_hir::def::Res::Def(def_kind, def_id) = res {
386+
if Some(def_id) == self.def_ids.exists() {
387+
assert_eq!(args.len(), 1, "exists takes exactly 1 argument");
388+
let ExprKind::Closure(closure) = args[0].kind else {
389+
unimplemented!("exists argument must be a closure");
390+
};
391+
let closure_body = self.tcx.hir().body(closure.body);
392+
let mut inner = self.env.clone();
393+
let mut vars = Vec::new();
394+
for param in closure_body.params {
395+
let rustc_hir::PatKind::Binding(_, hir_id, ident, None) =
396+
param.pat.kind
397+
else {
398+
unimplemented!(
399+
"exists closure parameter must be a simple binding: {:?}",
400+
param.pat
401+
);
402+
};
403+
let param_ty = self.pat_ty(param.pat);
404+
let sort = self.type_builder.build(param_ty).to_sort();
405+
let var_term = chc::Term::FormulaExistentialVar(
406+
sort.clone(),
407+
ident.name.to_string(),
408+
);
409+
inner.insert(hir_id, var_term);
410+
vars.push((ident.name.to_string(), sort));
411+
}
412+
let inner_translator = Self {
413+
env: inner,
414+
tcx: self.tcx,
415+
local_def_id: self.local_def_id,
416+
typeck: self.typeck,
417+
body: self.body,
418+
generic_args: self.generic_args,
419+
def_ids: self.def_ids.clone(),
420+
type_builder: TypeBuilder::new(
421+
self.tcx,
422+
self.def_ids.clone(),
423+
self.local_def_id.to_def_id(),
424+
),
425+
};
426+
let body_formula = inner_translator.to_formula(closure_body.value);
427+
return FormulaOrTerm::Formula(chc::Formula::exists(
428+
vars,
429+
body_formula,
430+
));
431+
}
379432
if Some(def_id) == self.def_ids.mut_model_new() {
380433
assert_eq!(args.len(), 2, "Mut::new takes exactly 2 arguments");
381434
let t1 = self.to_term(&args[0]);

src/analyze/did_cache.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct DefIds {
2222

2323
mut_model_new: OnceCell<Option<DefId>>,
2424
box_model_new: OnceCell<Option<DefId>>,
25+
26+
exists: OnceCell<Option<DefId>>,
2527
}
2628

2729
/// Retrieves and caches well-known [`DefId`]s.
@@ -160,4 +162,11 @@ impl<'tcx> DefIdCache<'tcx> {
160162
.box_model_new
161163
.get_or_init(|| self.annotated_def(&crate::analyze::annot::box_model_new_path()))
162164
}
165+
166+
pub fn exists(&self) -> Option<DefId> {
167+
*self
168+
.def_ids
169+
.exists
170+
.get_or_init(|| self.annotated_def(&crate::analyze::annot::exists_path()))
171+
}
163172
}

std.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ mod thrust_models {
234234
impl<T, E> Model for Result<T, E> where T: Model, E: Model {
235235
type Ty = Result<<T as Model>::Ty, <E as Model>::Ty>;
236236
}
237+
238+
#[allow(dead_code)]
239+
#[thrust::def::exists]
240+
pub fn exists<T>(_x: T) -> bool {
241+
unimplemented!()
242+
}
237243
}
238244

239245
#[thrust::extern_spec_fn]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@error-in-other-file: Unsat
2+
//@compile-flags: -C debug-assertions=off
3+
//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper
4+
5+
#[thrust::trusted]
6+
#[thrust::callable]
7+
fn rand() -> i32 { unimplemented!() }
8+
9+
#[thrust::formula_fn]
10+
fn _thrust_requires_f() -> bool {
11+
true
12+
}
13+
14+
#[thrust::formula_fn]
15+
fn _thrust_ensures_f(result: i32) -> bool {
16+
thrust_models::exists(|x: i32| result == 2 * x)
17+
}
18+
19+
#[allow(path_statements)]
20+
fn f() -> i32 {
21+
#[thrust::requires_path]
22+
_thrust_requires_f;
23+
24+
#[thrust::ensures_path]
25+
_thrust_ensures_f;
26+
27+
let x = rand();
28+
x + x + x
29+
}
30+
31+
fn main() {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@check-pass
2+
//@compile-flags: -C debug-assertions=off
3+
//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper
4+
5+
#[thrust::trusted]
6+
#[thrust::callable]
7+
fn rand() -> i32 { unimplemented!() }
8+
9+
#[thrust::formula_fn]
10+
fn _thrust_requires_f() -> bool {
11+
true
12+
}
13+
14+
#[thrust::formula_fn]
15+
fn _thrust_ensures_f(result: i32) -> bool {
16+
thrust_models::exists(|x: i32| result == 2 * x)
17+
}
18+
19+
#[allow(path_statements)]
20+
fn f() -> i32 {
21+
#[thrust::requires_path]
22+
_thrust_requires_f;
23+
24+
#[thrust::ensures_path]
25+
_thrust_ensures_f;
26+
27+
let x = rand();
28+
x + x
29+
}
30+
31+
fn main() {}

0 commit comments

Comments
 (0)