diff --git a/src/analyze.rs b/src/analyze.rs index fce97595..f3fd091b 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -7,7 +7,7 @@ //! CHC solver with the [`Analyzer::solve`] and subsequently reports the result. use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; use rustc_hir::lang_items::LangItem; @@ -378,6 +378,45 @@ impl<'tcx> Analyzer<'tcx> { enum_def } + /// Registers the definitions of the enums `ty` is built from, including the ones reachable + /// through the fields of the datatypes it mentions. + pub fn register_enum_defs_in_ty(&self, type_builder: &TypeBuilder<'tcx>, ty: mir_ty::Ty<'tcx>) { + use mir_ty::{TypeSuperVisitable as _, TypeVisitable as _}; + struct EnumCollector<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + builder: &'a TypeBuilder<'tcx>, + enums: HashSet, + visited: HashSet>, + } + impl<'tcx> mir_ty::TypeVisitor> for EnumCollector<'_, 'tcx> { + fn visit_ty(&mut self, ty: mir_ty::Ty<'tcx>) { + let ty = self.builder.resolve_model_ty(ty); + if let mir_ty::TyKind::Adt(def, args) = ty.kind() { + if self.visited.insert(ty) { + if def.is_enum() { + self.enums.insert(def.did()); + } + for field in def.all_fields() { + field.ty(self.tcx, args).visit_with(self); + } + } + } + ty.super_visit_with(self); + } + } + + let mut visitor = EnumCollector { + tcx: self.tcx, + builder: type_builder, + enums: HashSet::new(), + visited: HashSet::new(), + }; + ty.visit_with(&mut visitor); + for def_id in visitor.enums { + self.get_or_register_enum_def(def_id); + } + } + pub fn register_def(&mut self, def_id: DefId, rty: rty::RefinedType) { tracing::info!(def_id = ?def_id, rty = %rty.display(), "register_def"); self.defs.insert(def_id, DefTy::Concrete(rty)); diff --git a/src/analyze/annot_fn.rs b/src/analyze/annot_fn.rs index 7d0200a8..d9628f42 100644 --- a/src/analyze/annot_fn.rs +++ b/src/analyze/annot_fn.rs @@ -617,6 +617,8 @@ impl<'a, 'tcx> AnnotFnTranslator<'a, 'tcx> { ); }; let param_ty = self.pat_ty(param.pat); + self.analyzer + .register_enum_defs_in_ty(&self.type_builder, param_ty); let sort = self.type_builder.build(param_ty).to_sort(); let var_term = chc::Term::FormulaQuantifiedVar(sort.clone(), ident.name.to_string()); inner_translator.env.insert(hir_id, var_term); diff --git a/src/analyze/basic_block.rs b/src/analyze/basic_block.rs index 97ae3ec0..5c4c996d 100644 --- a/src/analyze/basic_block.rs +++ b/src/analyze/basic_block.rs @@ -1433,40 +1433,9 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { } fn register_enum_defs(&mut self) { - use mir_ty::{TypeSuperVisitable as _, TypeVisitable as _}; - struct EnumCollector<'tcx> { - tcx: mir_ty::TyCtxt<'tcx>, - builder: TypeBuilder<'tcx>, - enums: std::collections::HashSet, - visited: std::collections::HashSet>, - } - impl<'tcx> mir_ty::TypeVisitor> for EnumCollector<'tcx> { - fn visit_ty(&mut self, ty: mir_ty::Ty<'tcx>) { - let ty = self.builder.resolve_model_ty(ty); - if let mir_ty::TyKind::Adt(def, args) = ty.kind() { - if self.visited.insert(ty) { - if def.is_enum() { - self.enums.insert(def.did()); - } - for field in def.all_fields() { - field.ty(self.tcx, args).visit_with(self); - } - } - } - ty.super_visit_with(self); - } - } - let mut visitor = EnumCollector { - tcx: self.tcx, - builder: self.type_builder.clone(), - enums: std::collections::HashSet::new(), - visited: std::collections::HashSet::new(), - }; for local_decl in &self.local_decls { - local_decl.ty.visit_with(&mut visitor); - } - for def_id in visitor.enums { - self.ctx.get_or_register_enum_def(def_id); + self.ctx + .register_enum_defs_in_ty(&self.type_builder, local_decl.ty); } } } diff --git a/src/chc.rs b/src/chc.rs index ac8e3b99..c7fe3901 100644 --- a/src/chc.rs +++ b/src/chc.rs @@ -1365,6 +1365,11 @@ impl Atom { self.args.iter().flat_map(|t| t.fv()).chain(guard_fvs) } + /// Iterates over the variables bound by the quantifiers of the guard. + pub fn iter_quantified_vars(&self) -> impl Iterator { + self.guard.iter().flat_map(|g| g.iter_quantified_vars()) + } + pub fn guarded(self, new_guard: Formula) -> Atom { let Atom { guard: self_guard, @@ -1651,6 +1656,26 @@ impl Formula { } } + /// Iterates over the variables bound by the quantifiers occurring in this formula. + pub fn iter_quantified_vars(&self) -> impl Iterator { + self.iter_quantified_vars_impl() + } + + fn iter_quantified_vars_impl(&self) -> Box + '_> { + match self { + Formula::Atom(atom) => Box::new(atom.iter_quantified_vars()), + Formula::Not(fo) => Box::new(fo.iter_quantified_vars()), + Formula::And(fs) => Box::new(fs.iter().flat_map(Formula::iter_quantified_vars)), + Formula::Or(fs) => Box::new(fs.iter().flat_map(Formula::iter_quantified_vars)), + Formula::Implies(lhs, rhs) => { + Box::new(lhs.iter_quantified_vars().chain(rhs.iter_quantified_vars())) + } + Formula::Exists(vars, fo) | Formula::Forall(vars, fo) => { + Box::new(vars.iter().chain(fo.iter_quantified_vars())) + } + } + } + pub fn push_conj(&mut self, other: Self) { match self { Formula::And(fs) => { @@ -1823,6 +1848,13 @@ impl Body { pub fn iter_atoms(&self) -> impl Iterator> { self.formula.iter_atoms().chain(&self.atoms) } + + /// Iterates over the variables bound by the quantifiers occurring in this body. + pub fn iter_quantified_vars(&self) -> impl Iterator { + self.formula + .iter_quantified_vars() + .chain(self.atoms.iter().flat_map(Atom::iter_quantified_vars)) + } } impl Body diff --git a/src/chc/format_context.rs b/src/chc/format_context.rs index 984a788a..e3bf834e 100644 --- a/src/chc/format_context.rs +++ b/src/chc/format_context.rs @@ -226,6 +226,11 @@ fn collect_sorts(system: &chc::System) -> BTreeSet { for a in clause.body.iter_atoms() { atom_sorts(clause, a, &mut sorts); } + let quantified_vars = clause + .head + .iter_quantified_vars() + .chain(clause.body.iter_quantified_vars()); + sorts.extend(quantified_vars.map(|(_, sort)| sort.clone())); } for sort in sorts.clone() { diff --git a/tests/ui/fail/annot_exists_enum_binder.rs b/tests/ui/fail/annot_exists_enum_binder.rs new file mode 100644 index 00000000..40e873fa --- /dev/null +++ b/tests/ui/fail/annot_exists_enum_binder.rs @@ -0,0 +1,22 @@ +//@error-in-other-file: Unsat +//@compile-flags: -Adead_code -C debug-assertions=off +//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper + +use thrust_models::exists; + +pub enum X { + A(i64), + B(bool), +} + +impl thrust_models::Model for X { + type Ty = X; +} + +#[thrust_macros::requires(true)] +#[thrust_macros::ensures(exists(|x: X| result < 0))] +fn f() -> i64 { + 1 +} + +fn main() {} diff --git a/tests/ui/fail/annot_exists_tuple_binder.rs b/tests/ui/fail/annot_exists_tuple_binder.rs new file mode 100644 index 00000000..3c38cfa6 --- /dev/null +++ b/tests/ui/fail/annot_exists_tuple_binder.rs @@ -0,0 +1,13 @@ +//@error-in-other-file: Unsat +//@compile-flags: -C debug-assertions=off +//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper + +use thrust_models::exists; + +#[thrust_macros::requires(true)] +#[thrust_macros::ensures(exists(|p: (i64, bool)| result < 0))] +fn f() -> i64 { + 1 +} + +fn main() {} diff --git a/tests/ui/pass/annot_exists_enum_binder.rs b/tests/ui/pass/annot_exists_enum_binder.rs new file mode 100644 index 00000000..2f888d3e --- /dev/null +++ b/tests/ui/pass/annot_exists_enum_binder.rs @@ -0,0 +1,22 @@ +//@check-pass +//@compile-flags: -Adead_code -C debug-assertions=off +//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper + +use thrust_models::exists; + +pub enum X { + A(i64), + B(bool), +} + +impl thrust_models::Model for X { + type Ty = X; +} + +#[thrust_macros::requires(true)] +#[thrust_macros::ensures(exists(|x: X| result >= 0))] +fn f() -> i64 { + 1 +} + +fn main() {} diff --git a/tests/ui/pass/annot_exists_tuple_binder.rs b/tests/ui/pass/annot_exists_tuple_binder.rs new file mode 100644 index 00000000..f9247318 --- /dev/null +++ b/tests/ui/pass/annot_exists_tuple_binder.rs @@ -0,0 +1,13 @@ +//@check-pass +//@compile-flags: -C debug-assertions=off +//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper + +use thrust_models::exists; + +#[thrust_macros::requires(true)] +#[thrust_macros::ensures(exists(|p: (i64, bool)| result >= 0))] +fn f() -> i64 { + 1 +} + +fn main() {}