Skip to main content

cedar_policy_symcc/
lib.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#![warn(missing_docs)]
18#![doc = include_str!("../README.md")]
19
20pub mod err;
21mod symcc;
22mod symccopt;
23
24use cedar_policy::{Effect, Policy, PolicySet, RequestEnv, Schema};
25use nonempty::{nonempty, NonEmpty};
26use std::fmt;
27
28use err::{Error, Result};
29use solver::Solver;
30use symcc::{well_typed_policies, well_typed_policy, Environment, SymCompiler};
31use symccopt::{
32    verify_always_allows_opt, verify_always_denies_opt, verify_always_matches_opt,
33    verify_disjoint_opt, verify_equivalent_opt, verify_implies_opt, verify_matches_disjoint_opt,
34    verify_matches_equivalent_opt, verify_matches_implies_opt, verify_never_errors_opt,
35    verify_never_matches_opt, CompiledPolicies,
36};
37
38pub use symcc::bitvec;
39pub use symcc::ext;
40pub use symcc::extension_types;
41pub use symcc::factory as term_factory;
42pub use symcc::op;
43pub use symcc::solver;
44pub use symcc::solver_pool;
45pub use symcc::term;
46pub use symcc::term_type;
47pub use symcc::type_abbrevs;
48pub use symcc::verifier::Asserts;
49pub use symcc::Interpretation;
50pub use symcc::{CompiledSchema, Env, ResetMode, SmtLibScript, SymEnv};
51
52impl SymEnv {
53    /// Constructs a new [`SymEnv`] from the given [`Schema`] and [`RequestEnv`].
54    pub fn new(schema: &Schema, req_env: &RequestEnv) -> Result<Self> {
55        let env = Environment::from_request_env(req_env, schema.as_ref())
56            .ok_or_else(|| Error::ActionNotInSchema(req_env.action().to_string()))?;
57        Ok(Self::of_env(&env)?)
58    }
59}
60
61/// Validated and well-typed policy.
62#[derive(Clone, Debug)]
63#[deprecated(since = "0.3.0", note = "use `CompiledPolicy` instead")]
64pub struct WellTypedPolicy {
65    policy: cedar_policy_core::ast::Policy,
66}
67
68#[expect(deprecated, reason = "impl on a deprecated struct")]
69impl WellTypedPolicy {
70    /// Returns a reference to the underlying policy.
71    pub fn policy(&self) -> &cedar_policy_core::ast::Policy {
72        &self.policy
73    }
74
75    /// Creates a well-typed policy with respect to the given request environment and schema.
76    /// This ensures that the policy satisfies the well-typedness constraints required by the
77    /// symbolic compiler, by applying Cedar's typechecker transformations.
78    pub fn from_policy(
79        policy: &Policy,
80        env: &RequestEnv,
81        schema: &Schema,
82    ) -> Result<WellTypedPolicy> {
83        well_typed_policy(policy.as_ref(), env, schema).map(|p| WellTypedPolicy { policy: p })
84    }
85
86    /// Converts a [`Policy`] to a [`WellTypedPolicy`] without type checking.
87    /// Note that SymCC may fail on the policy produced by this function.
88    pub fn from_policy_unchecked(policy: &Policy) -> Self {
89        WellTypedPolicy {
90            policy: policy.as_ref().clone(),
91        }
92    }
93}
94
95#[expect(deprecated, reason = "impl for a deprecated struct")]
96impl fmt::Display for WellTypedPolicy {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        write!(f, "{}", self.policy)
99    }
100}
101
102/// Validated and well-typed policy set.
103/// Similar to [`WellTypedPolicy`] but for policy sets.
104#[derive(Clone, Debug)]
105#[deprecated(since = "0.3.0", note = "use `CompiledPolicySet` instead")]
106pub struct WellTypedPolicies {
107    policies: cedar_policy_core::ast::PolicySet,
108}
109
110#[expect(deprecated, reason = "impl on a deprecated struct")]
111impl WellTypedPolicies {
112    /// Returns a reference to the underlying policy set
113    pub fn policy_set(&self) -> &cedar_policy_core::ast::PolicySet {
114        &self.policies
115    }
116
117    /// Creates a well-typed policy set with respect to the given request environment and schema.
118    /// This ensures that the policies satisfy the well-typedness constraints required by the
119    /// symbolic compiler, by applying Cedar's typechecker transformations.
120    pub fn from_policies(
121        ps: &PolicySet,
122        env: &RequestEnv,
123        schema: &Schema,
124    ) -> Result<WellTypedPolicies> {
125        well_typed_policies(ps.as_ref(), env, schema).map(|ps| WellTypedPolicies { policies: ps })
126    }
127
128    /// Converts a [`PolicySet`] to a [`WellTypedPolicies`] without type checking.
129    /// Note that SymCC may fail on the policy set produced by this function.
130    pub fn from_policies_unchecked(ps: &PolicySet) -> Self {
131        WellTypedPolicies {
132            policies: ps.as_ref().clone(),
133        }
134    }
135}
136
137#[expect(deprecated, reason = "impl for a deprecated struct")]
138impl fmt::Display for WellTypedPolicies {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(f, "{}", self.policies)
141    }
142}
143
144/// Represents a symbolically compiled policy. This can be fed into various
145/// functions on [`CedarSymCompiler`] for efficient solver queries (that don't
146/// have to repeat symbolic compilation).
147#[derive(Debug, Clone)]
148pub struct CompiledPolicy {
149    policy: symccopt::CompiledPolicy,
150}
151
152impl CompiledPolicy {
153    /// Compile a policy for the given `RequestEnv`.
154    ///
155    /// This does all the validating and well-typing that you need; you need not
156    /// (and should not) call `WellTypedPolicy::from_policy()` prior to calling
157    /// this.
158    pub fn compile(policy: &Policy, env: &RequestEnv, schema: &Schema) -> Result<Self> {
159        Ok(Self {
160            policy: symccopt::CompiledPolicy::compile(policy.as_ref(), env, schema)?,
161        })
162    }
163
164    /// Compile a policy for the given `RequestEnv`, using a custom `SymEnv`
165    /// rather than the one that would naturally be derived from this
166    /// `RequestEnv`.
167    ///
168    /// Most often, you want `compile()` instead.
169    /// `compile_with_custom_symenv()` is generally used to compile with
170    /// `SymEnv`s that are more concrete than the default `SymEnv`s, with
171    /// constraints/concretizations of the entity hierarchy or request
172    /// variables.
173    ///
174    /// Caller is responsible for some currently-undocumented invariants about
175    /// the relationship between the `RequestEnv` and the `SymEnv`.
176    /// This function has no analogue in the Lean (as of this writing).
177    /// Use at your own risk.
178    pub fn compile_with_custom_symenv(
179        policy: &Policy,
180        env: &RequestEnv,
181        schema: &Schema,
182        symenv: SymEnv,
183    ) -> Result<Self> {
184        Ok(Self {
185            policy: symccopt::CompiledPolicy::compile_with_custom_symenv(
186                policy.as_ref(),
187                env,
188                schema,
189                symenv,
190            )?,
191        })
192    }
193
194    /// Get the `Effect` of this `CompiledPolicy`
195    pub fn effect(&self) -> Effect {
196        self.policy.effect()
197    }
198
199    /// Convert a `CompiledPolicy` to a `CompiledPolicySet` representing a
200    /// singleton policyset with just that policy.
201    ///
202    /// This function is intended to be much more efficient than re-compiling
203    /// with `CompiledPolicySet::compile()`.
204    pub fn into_compiled_policyset(self) -> CompiledPolicySet {
205        CompiledPolicySet {
206            policies: self.policy.into_compiled_policyset(),
207        }
208    }
209}
210
211/// Represents a symbolically compiled policyset. This can be fed into various
212/// functions on [`CedarSymCompiler`] for efficient solver queries (that don't
213/// have to repeat symbolic compilation).
214#[derive(Debug, Clone)]
215pub struct CompiledPolicySet {
216    policies: symccopt::CompiledPolicySet,
217}
218
219impl CompiledPolicySet {
220    /// Compile a policyset for the given `RequestEnv`.
221    ///
222    /// This does all the validating and well-typing that you need; you need not
223    /// (and should not) call `WellTypedPolicies::from_policies()` prior to
224    /// calling this.
225    pub fn compile(pset: &PolicySet, env: &RequestEnv, schema: &Schema) -> Result<Self> {
226        Ok(Self {
227            policies: symccopt::CompiledPolicySet::compile(pset.as_ref(), env, schema)?,
228        })
229    }
230
231    /// Compile a set of policies for the given `RequestEnv`, using a custom
232    /// `SymEnv` rather than the one that would naturally be derived from this
233    /// `RequestEnv`.
234    ///
235    /// Most often, you want `compile()` instead.
236    /// `compile_with_custom_symenv()` is generally used to compile with
237    /// `SymEnv`s that are more concrete than the default `SymEnv`s, with
238    /// constraints/concretizations of the entity hierarchy or request
239    /// variables.
240    ///
241    /// Caller is responsible for some currently-undocumented invariants about
242    /// the relationship between the `RequestEnv` and the `SymEnv`.
243    /// This function has no analogue in the Lean (as of this writing).
244    /// Use at your own risk.
245    pub fn compile_with_custom_symenv(
246        pset: &PolicySet,
247        env: &RequestEnv,
248        schema: &Schema,
249        symenv: SymEnv,
250    ) -> Result<Self> {
251        Ok(Self {
252            policies: symccopt::CompiledPolicySet::compile_with_custom_symenv(
253                pset.as_ref(),
254                env,
255                schema,
256                symenv,
257            )?,
258        })
259    }
260}
261
262/// Cedar symbolic compiler, which takes your policies and schemas
263/// and converts them to SMT queries to perform various verification
264/// tasks such as checking if a policy set always allows/denies,
265/// if two policy sets are equivalent, etc.
266#[derive(Clone, Debug)]
267pub struct CedarSymCompiler<S: Solver> {
268    /// SymCompiler
269    symcc: SymCompiler<S>,
270}
271
272impl<S: Solver> CedarSymCompiler<S> {
273    /// Constructs a new [`CedarSymCompiler`] with the given [`Solver`] instance.
274    ///
275    /// Uses the default [`ResetMode`]; see [`Self::with_reset_mode()`].
276    pub fn new(solver: S) -> Result<Self> {
277        Ok(Self {
278            symcc: SymCompiler::new(solver),
279        })
280    }
281
282    /// Returns this [`CedarSymCompiler`] with the given [`ResetMode`], e.g.
283    /// `CedarSymCompiler::new(solver)?.with_reset_mode(ResetMode::Comment)`.
284    pub fn with_reset_mode(mut self, reset_mode: ResetMode) -> Self {
285        self.symcc.set_reset_mode(reset_mode);
286        self
287    }
288
289    /// Returns the [`ResetMode`] used for queries issued by this [`CedarSymCompiler`]
290    pub fn reset_mode(&self) -> ResetMode {
291        self.symcc.reset_mode()
292    }
293
294    /// Returns a reference to the [`Solver`] instance used to construct this [`CedarSymCompiler`]
295    pub fn solver(&self) -> &S {
296        self.symcc.solver()
297    }
298
299    /// Returns a mutable reference to the [`Solver`] instance used to construct this [`CedarSymCompiler`]
300    pub fn solver_mut(&mut self) -> &mut S {
301        self.symcc.solver_mut()
302    }
303
304    /// Calls the underlying solver to check if the given `asserts` are unsatisfiable.
305    /// Returns `true` iff the asserts are unsatisfiable.
306    ///
307    /// NOTE: This API is an experimental feature that may break or change in the future.
308    pub async fn check_unsat(&mut self, asserts: &WellFormedAsserts<'_>) -> Result<bool> {
309        self.symcc
310            .check_unsat(|_| Ok(asserts.asserts().clone()), asserts.symenv())
311            .await
312    }
313
314    /// Calls the underlying solver with given raw `Asserts` and corresponding `SymEnv`,
315    /// returning `true` iff the asserts are unsatisfiable.
316    ///
317    /// Caller is responsible for ensuring that the `Asserts` and `SymEnv` are
318    /// well-formed and valid with respect to each other.
319    ///
320    /// NOTE: This API is an experimental feature that may break or change in the future.
321    pub async fn check_unsat_raw(&mut self, asserts: Asserts, symenv: &SymEnv) -> Result<bool> {
322        self.symcc.check_unsat(|_| Ok(asserts), symenv).await
323    }
324
325    /// Calls the underlying solver to check if the given `asserts` are unsatisfiable.
326    /// Returns some counterexample to the given symbolic assertions iff they are satisfiable.
327    ///
328    /// NOTE: This API is an experimental feature that may break or change in the future.
329    pub async fn check_sat(&mut self, asserts: &WellFormedAsserts<'_>) -> Result<Option<Env>> {
330        // since `asserts.policies()` doesn't itself produce a clone-able
331        // iterator, we create this iterator which is indeed (cheaply)
332        // clone-able
333        let policies: Vec<&CompiledPolicies<'_>> = asserts.policies().collect();
334        let policies_iter = policies.iter().copied();
335
336        self.symcc
337            .sat_asserts_opt(asserts.asserts(), policies_iter)
338            .await
339    }
340
341    /// Returns true iff the [`WellTypedPolicy`] does not error on any well-formed
342    /// input in the given symbolic environment.
343    ///
344    /// Consider using the optimized version `check_never_errors_opt()` instead,
345    /// which will allow you to reuse a `CompiledPolicy` across many queries.
346    #[deprecated(since = "0.3.0", note = "use `check_never_errors_opt()` instead")]
347    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
348    pub async fn check_never_errors(
349        &mut self,
350        policy: &WellTypedPolicy,
351        symenv: &SymEnv,
352    ) -> Result<bool> {
353        self.symcc.check_never_errors(&policy.policy, symenv).await
354    }
355
356    /// Returns true iff the [`CompiledPolicy`] does not error on any
357    /// well-formed input in the `RequestEnv` it was compiled for.
358    pub async fn check_never_errors_opt(&mut self, policy: &CompiledPolicy) -> Result<bool> {
359        self.symcc.check_never_errors_opt(&policy.policy).await
360    }
361
362    /// Similar to [`Self::check_never_errors`], but returns a counterexample
363    /// if the policy could error on well-formed input.
364    ///
365    /// Consider using the optimized version `check_never_errors_with_counterexample_opt()`
366    /// instead, which will allow you to reuse a `CompiledPolicy` across many
367    /// queries.
368    #[deprecated(
369        since = "0.3.0",
370        note = "use `check_never_errors_with_counterexample_opt()` instead"
371    )]
372    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
373    pub async fn check_never_errors_with_counterexample(
374        &mut self,
375        policy: &WellTypedPolicy,
376        symenv: &SymEnv,
377    ) -> Result<Option<Env>> {
378        self.symcc
379            .check_never_errors_with_counterexample(&policy.policy, symenv)
380            .await
381    }
382
383    /// Similar to [`Self::check_never_errors_opt`], but returns a counterexample
384    /// if the policy could error on well-formed input.
385    pub async fn check_never_errors_with_counterexample_opt(
386        &mut self,
387        policy: &CompiledPolicy,
388    ) -> Result<Option<Env>> {
389        self.symcc
390            .check_never_errors_with_counterexample_opt(&policy.policy)
391            .await
392    }
393
394    /// Returns true iff the [`WellTypedPolicy`] matches all well-formed inputs in
395    /// the given symbolic environment. That is, if `policy` is a `permit`
396    /// policy, it allows all inputs in the `symenv`, or if `policy` is a
397    /// `forbid` policy, it denies all inputs in the `symenv`.
398    ///
399    /// Consider using the optimized version `check_always_matches_opt()` instead,
400    /// which will allow you to reuse a `CompiledPolicy` across many queries.
401    #[deprecated(since = "0.3.0", note = "use `check_always_matches_opt()` instead")]
402    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
403    pub async fn check_always_matches(
404        &mut self,
405        policy: &WellTypedPolicy,
406        symenv: &SymEnv,
407    ) -> Result<bool> {
408        self.symcc
409            .check_always_matches(&policy.policy, symenv)
410            .await
411    }
412
413    /// Returns true iff the [`CompiledPolicy`] matches all well-formed inputs
414    /// in the `RequestEnv` it was compiled for.
415    pub async fn check_always_matches_opt(&mut self, policy: &CompiledPolicy) -> Result<bool> {
416        self.symcc.check_always_matches_opt(&policy.policy).await
417    }
418
419    /// Similar to [`Self::check_always_matches`], but returns a counterexample
420    /// if the policy does not match some well-formed input.
421    ///
422    /// Consider using the optimized version `check_always_matches_with_counterexample_opt()`
423    /// instead, which will allow you to reuse a `CompiledPolicy` across many
424    /// queries.
425    #[deprecated(
426        since = "0.3.0",
427        note = "use `check_always_matches_with_counterexample_opt()` instead"
428    )]
429    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
430    pub async fn check_always_matches_with_counterexample(
431        &mut self,
432        policy: &WellTypedPolicy,
433        symenv: &SymEnv,
434    ) -> Result<Option<Env>> {
435        self.symcc
436            .check_always_matches_with_counterexample(&policy.policy, symenv)
437            .await
438    }
439
440    /// Similar to [`Self::check_always_matches_opt`], but returns a counterexample
441    /// if the policy does not match some well-formed input.
442    pub async fn check_always_matches_with_counterexample_opt(
443        &mut self,
444        policy: &CompiledPolicy,
445    ) -> Result<Option<Env>> {
446        self.symcc
447            .check_always_matches_with_counterexample_opt(&policy.policy)
448            .await
449    }
450
451    /// Returns true iff the [`WellTypedPolicy`] matches no well-formed inputs in
452    /// the given symbolic environment.
453    ///
454    /// Consider using the optimized version `check_never_matches_opt()` instead,
455    /// which will allow you to reuse a `CompiledPolicy` across many queries.
456    #[deprecated(since = "0.3.0", note = "use `check_never_matches_opt()` instead")]
457    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
458    pub async fn check_never_matches(
459        &mut self,
460        policy: &WellTypedPolicy,
461        symenv: &SymEnv,
462    ) -> Result<bool> {
463        self.symcc.check_never_matches(&policy.policy, symenv).await
464    }
465
466    /// Returns true iff the [`CompiledPolicy`] matches no well-formed inputs
467    /// in the `RequestEnv` it was compiled for.
468    pub async fn check_never_matches_opt(&mut self, policy: &CompiledPolicy) -> Result<bool> {
469        self.symcc.check_never_matches_opt(&policy.policy).await
470    }
471
472    /// Similar to [`Self::check_never_matches`], but returns a counterexample
473    /// if the policy matches some well-formed input.
474    ///
475    /// Consider using the optimized version `check_never_matches_with_counterexample_opt()`
476    /// instead, which will allow you to reuse a `CompiledPolicy` across many
477    /// queries.
478    #[deprecated(
479        since = "0.3.0",
480        note = "use `check_never_matches_with_counterexample_opt()` instead"
481    )]
482    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
483    pub async fn check_never_matches_with_counterexample(
484        &mut self,
485        policy: &WellTypedPolicy,
486        symenv: &SymEnv,
487    ) -> Result<Option<Env>> {
488        self.symcc
489            .check_never_matches_with_counterexample(&policy.policy, symenv)
490            .await
491    }
492
493    /// Similar to [`Self::check_never_matches_opt`], but returns a counterexample
494    /// if the policy matches some well-formed input.
495    pub async fn check_never_matches_with_counterexample_opt(
496        &mut self,
497        policy: &CompiledPolicy,
498    ) -> Result<Option<Env>> {
499        self.symcc
500            .check_never_matches_with_counterexample_opt(&policy.policy)
501            .await
502    }
503
504    /// Returns true iff `policy1` and `policy2` match exactly the same set of
505    /// well-formed inputs in the given symbolic environment.
506    ///
507    /// Compare with `check_equivalent`, which takes two policysets (which could consist
508    /// of a single policy, or more) and determines whether the _authorization behavior_
509    /// of those policysets is equivalent for well-formed inputs in the `symenv`. This
510    /// function differs from `check_equivalent` on singleton policysets in how it treats
511    /// `forbid` policies -- while `check_equivalent` trivially holds for any pair of
512    /// `forbid` policies (as they both always-deny), `check_matches_equivalent` only
513    /// holds if the two policies match exactly the same set of inputs. Also, a nonempty
514    /// `permit` and nonempty `forbid` policy can be `check_matches_equivalent`, but can
515    /// never be `check_equivalent`. (By "nonempty" we mean, matches at least one request
516    /// in the given symbolic environment.)
517    ///
518    /// Consider using the optimized version `check_matches_equivalent_opt()` instead,
519    /// which will allow you to reuse a `CompiledPolicy` across many queries.
520    #[deprecated(since = "0.3.0", note = "use `check_matches_equivalent_opt()` instead")]
521    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
522    pub async fn check_matches_equivalent(
523        &mut self,
524        policy1: &WellTypedPolicy,
525        policy2: &WellTypedPolicy,
526        symenv: &SymEnv,
527    ) -> Result<bool> {
528        self.symcc
529            .check_matches_equivalent(&policy1.policy, &policy2.policy, symenv)
530            .await
531    }
532
533    /// Returns true iff the [`CompiledPolicy`] `policy1` and `policy2` match exactly
534    /// the same set of well-formed inputs in the `RequestEnv` they were compiled for.
535    /// (Caller guarantees that both policies were compiled for the same `RequestEnv`.)
536    ///
537    /// Compare with `check_equivalent_opt`, which takes two compiled policysets and
538    /// determines whether the _authorization behavior_ of those policysets is equivalent
539    /// for well-formed inputs in the `RequestEnv`. This function differs from
540    /// `check_equivalent_opt` on singleton policysets in how it treats `forbid` policies --
541    /// while `check_equivalent_opt` trivially holds for any pair of `forbid` policies
542    /// (as they both always-deny), `check_matches_equivalent_opt` only holds if the two
543    /// policies match exactly the same set of inputs. Also, a nonempty `permit` and
544    /// nonempty `forbid` policy can be `check_matches_equivalent_opt`, but can never
545    /// be `check_equivalent_opt`. (By "nonempty" we mean, matches at least one request
546    /// in the `RequestEnv` they were compiled for.)
547    ///
548    /// Corresponds to `checkMatchesEquivalentOpt` in the Lean.
549    pub async fn check_matches_equivalent_opt(
550        &mut self,
551        policy1: &CompiledPolicy,
552        policy2: &CompiledPolicy,
553    ) -> Result<bool> {
554        self.symcc
555            .check_matches_equivalent_opt(&policy1.policy, &policy2.policy)
556            .await
557    }
558
559    /// Similar to [`Self::check_matches_equivalent`], but returns a counterexample
560    /// on which the matching behavior of `policy1` and `policy2` differ.
561    ///
562    /// Corresponds to `matchesEquivalent?` in the Lean.
563    ///
564    /// Consider using the optimized version `check_matches_equivalent_with_counterexample_opt()`
565    /// instead, which will allow you to reuse a `CompiledPolicy` across many queries.
566    #[deprecated(
567        since = "0.3.0",
568        note = "use `check_matches_equivalent_with_counterexample_opt()` instead"
569    )]
570    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
571    pub async fn check_matches_equivalent_with_counterexample(
572        &mut self,
573        policy1: &WellTypedPolicy,
574        policy2: &WellTypedPolicy,
575        symenv: &SymEnv,
576    ) -> Result<Option<Env>> {
577        self.symcc
578            .check_matches_equivalent_with_counterexample(&policy1.policy, &policy2.policy, symenv)
579            .await
580    }
581
582    /// Similar to [`Self::check_matches_equivalent_opt`], but returns a counterexample
583    /// on which the matching behavior of `policy1` and `policy2` differ.
584    ///
585    /// Corresponds to `matchesEquivalentOpt?` in the Lean.
586    pub async fn check_matches_equivalent_with_counterexample_opt(
587        &mut self,
588        policy1: &CompiledPolicy,
589        policy2: &CompiledPolicy,
590    ) -> Result<Option<Env>> {
591        self.symcc
592            .check_matches_equivalent_with_counterexample_opt(&policy1.policy, &policy2.policy)
593            .await
594    }
595
596    /// Returns true iff `policy1` matching implies that `policy2` matches, for every
597    /// well-formed input in the `symenv`. That is, for every request where `policy1`
598    /// matches, `policy2` also matches.
599    ///
600    /// Compare with `check_implies`, which takes two policysets (which could consist of
601    /// a single policy, or more) and determines whether the _authorization decision_ of
602    /// the first implies that of the second. This function differs from `check_implies`
603    /// on singleton policysets in how it treats `forbid` policies -- while for
604    /// `check_implies`, any `forbid` policy trivially implies any `permit` policy (as
605    /// always-deny always implies any policy), for `check_matches_implies`, a `forbid`
606    /// policy may or may not imply a `permit` policy, and a `permit` policy may or may
607    /// not imply a `forbid` policy.
608    ///
609    /// Consider using the optimized version `check_matches_implies_opt()` instead,
610    /// which will allow you to reuse a `CompiledPolicy` across many queries.
611    #[deprecated(since = "0.3.0", note = "use `check_matches_implies_opt()` instead")]
612    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
613    pub async fn check_matches_implies(
614        &mut self,
615        policy1: &WellTypedPolicy,
616        policy2: &WellTypedPolicy,
617        symenv: &SymEnv,
618    ) -> Result<bool> {
619        self.symcc
620            .check_matches_implies(&policy1.policy, &policy2.policy, symenv)
621            .await
622    }
623
624    /// Returns true iff the [`CompiledPolicy`] `policy1` matching implies that `policy2`
625    /// matches, for every well-formed input in the `RequestEnv` they were compiled for.
626    /// (Caller guarantees that both policies were compiled for the same `RequestEnv`.)
627    ///
628    /// Compare with `check_implies_opt`, which takes two compiled policysets and
629    /// determines whether the _authorization decision_ of the first implies that of the
630    /// second. This function differs from `check_implies_opt` on singleton policysets
631    /// in how it treats `forbid` policies -- while for `check_implies_opt`, any `forbid`
632    /// policy trivially implies any `permit` policy (as always-deny always implies any
633    /// policy), for `check_matches_implies_opt`, a `forbid` policy may or may not imply
634    /// a `permit` policy, and a `permit` policy may or may not imply a `forbid` policy.
635    ///
636    /// Corresponds to `checkMatchesImpliesOpt` in the Lean.
637    pub async fn check_matches_implies_opt(
638        &mut self,
639        policy1: &CompiledPolicy,
640        policy2: &CompiledPolicy,
641    ) -> Result<bool> {
642        self.symcc
643            .check_matches_implies_opt(&policy1.policy, &policy2.policy)
644            .await
645    }
646
647    /// Similar to [`Self::check_matches_implies`], but returns a counterexample
648    /// that is matched by `policy1` but not by `policy2` if it exists.
649    ///
650    /// Corresponds to `matchesImplies?` in the Lean.
651    ///
652    /// Consider using the optimized version `check_matches_implies_with_counterexample_opt()`
653    /// instead, which will allow you to reuse a `CompiledPolicy` across many queries.
654    #[deprecated(
655        since = "0.3.0",
656        note = "use `check_matches_implies_with_counterexample_opt()` instead"
657    )]
658    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
659    pub async fn check_matches_implies_with_counterexample(
660        &mut self,
661        policy1: &WellTypedPolicy,
662        policy2: &WellTypedPolicy,
663        symenv: &SymEnv,
664    ) -> Result<Option<Env>> {
665        self.symcc
666            .check_matches_implies_with_counterexample(&policy1.policy, &policy2.policy, symenv)
667            .await
668    }
669
670    /// Similar to [`Self::check_matches_implies_opt`], but returns a counterexample
671    /// that is matched by `policy1` but not by `policy2` if it exists.
672    ///
673    /// Corresponds to `matchesImpliesOpt?` in the Lean.
674    pub async fn check_matches_implies_with_counterexample_opt(
675        &mut self,
676        policy1: &CompiledPolicy,
677        policy2: &CompiledPolicy,
678    ) -> Result<Option<Env>> {
679        self.symcc
680            .check_matches_implies_with_counterexample_opt(&policy1.policy, &policy2.policy)
681            .await
682    }
683
684    /// Returns true iff there is no well-formed input in the `symenv` that is matched
685    /// by both `policy1` and `policy2`. This checks that the sets of inputs matched by
686    /// `policy1` and `policy2` are disjoint.
687    ///
688    /// Compare with `check_disjoint`, which takes two policysets (which could consist
689    /// of a single policy, or more) and determines whether the _authorization behavior_
690    /// of those policysets are disjoint. This function differs from `check_disjoint` on
691    /// singleton policysets in how it treats `forbid` policies -- while for
692    /// `check_disjoint`, any `forbid` policy is trivially disjoint from any other policy
693    /// (as it allows nothing), `check_matches_disjoint` considers whether the `forbid`
694    /// policy may _match_ (rather than _allow_) any input that is matched by the other
695    /// policy.
696    ///
697    /// Consider using the optimized version `check_matches_disjoint_opt()` instead,
698    /// which will allow you to reuse a `CompiledPolicy` across many queries.
699    #[deprecated(since = "0.3.0", note = "use `check_matches_disjoint_opt()` instead")]
700    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
701    pub async fn check_matches_disjoint(
702        &mut self,
703        policy1: &WellTypedPolicy,
704        policy2: &WellTypedPolicy,
705        symenv: &SymEnv,
706    ) -> Result<bool> {
707        self.symcc
708            .check_matches_disjoint(&policy1.policy, &policy2.policy, symenv)
709            .await
710    }
711
712    /// Returns true iff there is no well-formed input in the `RequestEnv` that is
713    /// matched by both [`CompiledPolicy`] `policy1` and `policy2`.
714    /// (Caller guarantees that both policies were compiled for the same `RequestEnv`.)
715    ///
716    /// Compare with `check_disjoint_opt`, which takes two compiled policysets and
717    /// determines whether the _authorization behavior_ of those policysets are disjoint.
718    /// This function differs from `check_disjoint_opt` on singleton policysets in how it
719    /// treats `forbid` policies -- while for `check_disjoint_opt`, any `forbid` policy
720    /// is trivially disjoint from any other policy (as it allows nothing),
721    /// `check_matches_disjoint_opt` considers whether the `forbid` policy may _match_
722    /// (rather than _allow_) any input that is matched by the other policy.
723    ///
724    /// Corresponds to `checkMatchesDisjointOpt` in the Lean.
725    pub async fn check_matches_disjoint_opt(
726        &mut self,
727        policy1: &CompiledPolicy,
728        policy2: &CompiledPolicy,
729    ) -> Result<bool> {
730        self.symcc
731            .check_matches_disjoint_opt(&policy1.policy, &policy2.policy)
732            .await
733    }
734
735    /// Similar to [`Self::check_matches_disjoint`], but returns a counterexample
736    /// that is matched by both `policy1` and `policy2` if it exists.
737    ///
738    /// Corresponds to `matchesDisjoint?` in the Lean.
739    ///
740    /// Consider using the optimized version `check_matches_disjoint_with_counterexample_opt()`
741    /// instead, which will allow you to reuse a `CompiledPolicy` across many queries.
742    #[deprecated(
743        since = "0.3.0",
744        note = "use `check_matches_disjoint_with_counterexample_opt()` instead"
745    )]
746    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
747    pub async fn check_matches_disjoint_with_counterexample(
748        &mut self,
749        policy1: &WellTypedPolicy,
750        policy2: &WellTypedPolicy,
751        symenv: &SymEnv,
752    ) -> Result<Option<Env>> {
753        self.symcc
754            .check_matches_disjoint_with_counterexample(&policy1.policy, &policy2.policy, symenv)
755            .await
756    }
757
758    /// Similar to [`Self::check_matches_disjoint_opt`], but returns a counterexample
759    /// that is matched by both `policy1` and `policy2` if it exists.
760    ///
761    /// Corresponds to `matchesDisjointOpt?` in the Lean.
762    pub async fn check_matches_disjoint_with_counterexample_opt(
763        &mut self,
764        policy1: &CompiledPolicy,
765        policy2: &CompiledPolicy,
766    ) -> Result<Option<Env>> {
767        self.symcc
768            .check_matches_disjoint_with_counterexample_opt(&policy1.policy, &policy2.policy)
769            .await
770    }
771
772    /// Returns true iff the authorization decision of `pset1` implies that of
773    /// `pset2` for every well-formed input in the `symenv`. That is, every
774    /// input allowed by `pset1` is allowed by `pset2`; `pset2` is either more
775    /// permissive than, or equivalent to, `pset1`.
776    ///
777    /// Consider using the optimized version `check_implies_opt()` instead,
778    /// which will allow you to reuse a `CompiledPolicySet` across many queries.
779    #[deprecated(since = "0.3.0", note = "use `check_implies_opt()` instead")]
780    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
781    pub async fn check_implies(
782        &mut self,
783        pset1: &WellTypedPolicies,
784        pset2: &WellTypedPolicies,
785        symenv: &SymEnv,
786    ) -> Result<bool> {
787        self.symcc
788            .check_implies(&pset1.policies, &pset2.policies, symenv)
789            .await
790    }
791
792    /// Returns true iff the authorization decision of `pset1` implies that of
793    /// `pset2` for every well-formed input in the `RequestEnv` which both
794    /// policysets were compiled for. (Caller guarantees that both policysets
795    /// were compiled for the same `RequestEnv`.) That is, every input allowed
796    /// by `pset1` is allowed by `pset2`; `pset2` is either more permissive
797    /// than, or equivalent to, `pset1`.
798    pub async fn check_implies_opt(
799        &mut self,
800        pset1: &CompiledPolicySet,
801        pset2: &CompiledPolicySet,
802    ) -> Result<bool> {
803        self.symcc
804            .check_implies_opt(&pset1.policies, &pset2.policies)
805            .await
806    }
807
808    /// Similar to [`Self::check_implies`], but returns a counterexample
809    /// that is allowed by `pset1` but not by `pset2` if it exists.
810    ///
811    /// Consider using the optimized version `check_implies_with_counterexample_opt()`
812    /// instead, which will allow you to reuse a `CompiledPolicySet` across many
813    /// queries.
814    #[deprecated(
815        since = "0.3.0",
816        note = "use `check_implies_with_counterexample_opt()` instead"
817    )]
818    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
819    pub async fn check_implies_with_counterexample(
820        &mut self,
821        pset1: &WellTypedPolicies,
822        pset2: &WellTypedPolicies,
823        symenv: &SymEnv,
824    ) -> Result<Option<Env>> {
825        self.symcc
826            .check_implies_with_counterexample(&pset1.policies, &pset2.policies, symenv)
827            .await
828    }
829
830    /// Similar to [`Self::check_implies_opt`], but returns a counterexample
831    /// that is allowed by `pset1` but not by `pset2` if it exists.
832    pub async fn check_implies_with_counterexample_opt(
833        &mut self,
834        pset1: &CompiledPolicySet,
835        pset2: &CompiledPolicySet,
836    ) -> Result<Option<Env>> {
837        self.symcc
838            .check_implies_with_counterexample_opt(&pset1.policies, &pset2.policies)
839            .await
840    }
841
842    /// Returns true iff `pset` allows all well-formed inputs in the `symenv`.
843    ///
844    /// Consider using the optimized version `check_always_allows_opt()` instead,
845    /// which will allow you to reuse a `CompiledPolicySet` across many queries.
846    #[deprecated(since = "0.3.0", note = "use `check_always_allows_opt()` instead")]
847    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
848    pub async fn check_always_allows(
849        &mut self,
850        pset: &WellTypedPolicies,
851        symenv: &SymEnv,
852    ) -> Result<bool> {
853        self.symcc.check_always_allows(&pset.policies, symenv).await
854    }
855
856    /// Returns true iff `pset` allows all well-formed inputs in the
857    /// `RequestEnv` which it was compiled for.
858    pub async fn check_always_allows_opt(&mut self, pset: &CompiledPolicySet) -> Result<bool> {
859        self.symcc.check_always_allows_opt(&pset.policies).await
860    }
861
862    /// Similar to [`Self::check_always_allows`], but returns a counterexample
863    /// that is denied by `pset` if it exists.
864    ///
865    /// Consider using the optimized version `check_always_allows_with_counterexample_opt()`
866    /// instead, which will allow you to reuse a `CompiledPolicySet` across many
867    /// queries.
868    #[deprecated(
869        since = "0.3.0",
870        note = "use `check_always_allows_with_counterexample_opt()` instead"
871    )]
872    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
873    pub async fn check_always_allows_with_counterexample(
874        &mut self,
875        pset: &WellTypedPolicies,
876        symenv: &SymEnv,
877    ) -> Result<Option<Env>> {
878        self.symcc
879            .check_always_allows_with_counterexample(&pset.policies, symenv)
880            .await
881    }
882
883    /// Similar to [`Self::check_always_allows_opt`], but returns a counterexample
884    /// that is denied by `pset` if it exists.
885    pub async fn check_always_allows_with_counterexample_opt(
886        &mut self,
887        pset: &CompiledPolicySet,
888    ) -> Result<Option<Env>> {
889        self.symcc
890            .check_always_allows_with_counterexample_opt(&pset.policies)
891            .await
892    }
893
894    /// Returns true iff `pset` denies all well-formed inputs in the `symenv`.
895    ///
896    /// Consider using the optimized version `check_always_denies_opt()` instead,
897    /// which will allow you to reuse a `CompiledPolicySet` across many queries.
898    #[deprecated(since = "0.3.0", note = "use `check_always_denies_opt()` instead")]
899    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
900    pub async fn check_always_denies(
901        &mut self,
902        pset: &WellTypedPolicies,
903        symenv: &SymEnv,
904    ) -> Result<bool> {
905        self.symcc.check_always_denies(&pset.policies, symenv).await
906    }
907
908    /// Returns true iff `pset` denies all well-formed inputs in the
909    /// `RequestEnv` which it was compiled for.
910    pub async fn check_always_denies_opt(&mut self, pset: &CompiledPolicySet) -> Result<bool> {
911        self.symcc.check_always_denies_opt(&pset.policies).await
912    }
913
914    /// Similar to [`Self::check_always_denies`], but returns a counterexample
915    /// that is allowed by `pset` if it exists.
916    ///
917    /// Consider using the optimized version `check_always_denies_with_counterexample_opt()`
918    /// instead, which will allow you to reuse a `CompiledPolicySet` across many
919    /// queries.
920    #[deprecated(
921        since = "0.3.0",
922        note = "use `check_always_denies_with_counterexample_opt()` instead"
923    )]
924    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
925    pub async fn check_always_denies_with_counterexample(
926        &mut self,
927        pset: &WellTypedPolicies,
928        symenv: &SymEnv,
929    ) -> Result<Option<Env>> {
930        self.symcc
931            .check_always_denies_with_counterexample(&pset.policies, symenv)
932            .await
933    }
934
935    /// Similar to [`Self::check_always_denies_opt`], but returns a counterexample
936    /// that is allowed by `pset` if it exists.
937    pub async fn check_always_denies_with_counterexample_opt(
938        &mut self,
939        pset: &CompiledPolicySet,
940    ) -> Result<Option<Env>> {
941        self.symcc
942            .check_always_denies_with_counterexample_opt(&pset.policies)
943            .await
944    }
945
946    /// Returns true iff `pset1` and `pset2` produce the same authorization
947    /// decision on all well-formed inputs in the `symenv`.
948    ///
949    /// Consider using the optimized version `check_equivalent_opt()` instead,
950    /// which will allow you to reuse a `CompiledPolicySet` across many queries.
951    #[deprecated(since = "0.3.0", note = "use `check_equivalent_opt()` instead")]
952    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
953    pub async fn check_equivalent(
954        &mut self,
955        pset1: &WellTypedPolicies,
956        pset2: &WellTypedPolicies,
957        symenv: &SymEnv,
958    ) -> Result<bool> {
959        self.symcc
960            .check_equivalent(&pset1.policies, &pset2.policies, symenv)
961            .await
962    }
963
964    /// Returns true iff `pset1` and `pset2` produce the same authorization
965    /// decision on all well-formed inputs in the `RequestEnv` which both
966    /// policysets were compiled for. (Caller guarantees that both policysets
967    /// were compiled for the same `RequestEnv`.)
968    pub async fn check_equivalent_opt(
969        &mut self,
970        pset1: &CompiledPolicySet,
971        pset2: &CompiledPolicySet,
972    ) -> Result<bool> {
973        self.symcc
974            .check_equivalent_opt(&pset1.policies, &pset2.policies)
975            .await
976    }
977
978    /// Similar to [`Self::check_equivalent`], but returns a counterexample
979    /// on which the authorization decisions of `pset1` and `pset2` differ.
980    ///
981    /// Consider using the optimized version `check_equivalent_with_counterexample_opt()`
982    /// instead, which will allow you to reuse a `CompiledPolicySet` across many
983    /// queries.
984    #[deprecated(
985        since = "0.3.0",
986        note = "use `check_equivalent_with_counterexample_opt()` instead"
987    )]
988    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
989    pub async fn check_equivalent_with_counterexample(
990        &mut self,
991        pset1: &WellTypedPolicies,
992        pset2: &WellTypedPolicies,
993        symenv: &SymEnv,
994    ) -> Result<Option<Env>> {
995        self.symcc
996            .check_equivalent_with_counterexample(&pset1.policies, &pset2.policies, symenv)
997            .await
998    }
999
1000    /// Similar to [`Self::check_equivalent_opt`], but returns a counterexample
1001    /// on which the authorization decisions of `pset1` and `pset2` differ.
1002    pub async fn check_equivalent_with_counterexample_opt(
1003        &mut self,
1004        pset1: &CompiledPolicySet,
1005        pset2: &CompiledPolicySet,
1006    ) -> Result<Option<Env>> {
1007        self.symcc
1008            .check_equivalent_with_counterexample_opt(&pset1.policies, &pset2.policies)
1009            .await
1010    }
1011
1012    /// Returns true iff there is no well-formed input in the `symenv` that is
1013    /// allowed by both `pset1` and `pset2`. If this returns `false`, then there
1014    /// is at least one well-formed input that is allowed by both `pset1` and
1015    /// `pset2`.
1016    ///
1017    /// Consider using the optimized version `check_disjoint_opt()` instead,
1018    /// which will allow you to reuse a `CompiledPolicySet` across many queries.
1019    #[deprecated(since = "0.3.0", note = "use `check_disjoint_opt()` instead")]
1020    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
1021    pub async fn check_disjoint(
1022        &mut self,
1023        pset1: &WellTypedPolicies,
1024        pset2: &WellTypedPolicies,
1025        symenv: &SymEnv,
1026    ) -> Result<bool> {
1027        self.symcc
1028            .check_disjoint(&pset1.policies, &pset2.policies, symenv)
1029            .await
1030    }
1031
1032    /// Returns true iff there is no well-formed input in the `RequestEnv` that
1033    /// is allowed by both `pset1` and `pset2`.
1034    /// (Caller guarantees that `pset1` and `pset2` were compiled for
1035    /// the same `RequestEnv`.)
1036    pub async fn check_disjoint_opt(
1037        &mut self,
1038        pset1: &CompiledPolicySet,
1039        pset2: &CompiledPolicySet,
1040    ) -> Result<bool> {
1041        self.symcc
1042            .check_disjoint_opt(&pset1.policies, &pset2.policies)
1043            .await
1044    }
1045
1046    /// Similar to [`Self::check_disjoint`], but returns a counterexample
1047    /// that is allowed by both `pset1` and `pset2`.
1048    ///
1049    /// Consider using the optimized version `check_disjoint_with_counterexample_opt()`
1050    /// instead, which will allow you to reuse a `CompiledPolicySet` across many
1051    /// queries.
1052    #[deprecated(
1053        since = "0.3.0",
1054        note = "use `check_disjoint_with_counterexample_opt()` instead"
1055    )]
1056    #[expect(deprecated, reason = "deprecated function uses deprecated types")]
1057    pub async fn check_disjoint_with_counterexample(
1058        &mut self,
1059        pset1: &WellTypedPolicies,
1060        pset2: &WellTypedPolicies,
1061        symenv: &SymEnv,
1062    ) -> Result<Option<Env>> {
1063        self.symcc
1064            .check_disjoint_with_counterexample(&pset1.policies, &pset2.policies, symenv)
1065            .await
1066    }
1067
1068    /// Similar to [`Self::check_disjoint_opt`], but returns a counterexample
1069    /// that is allowed by both `pset1` and `pset2`.
1070    pub async fn check_disjoint_with_counterexample_opt(
1071        &mut self,
1072        pset1: &CompiledPolicySet,
1073        pset2: &CompiledPolicySet,
1074    ) -> Result<Option<Env>> {
1075        self.symcc
1076            .check_disjoint_with_counterexample_opt(&pset1.policies, &pset2.policies)
1077            .await
1078    }
1079}
1080
1081/// Well-formed assertions generated by the symbolic compiler.
1082#[derive(Clone, Debug)]
1083pub struct WellFormedAsserts<'a> {
1084    asserts: Asserts,
1085    /// All `CompiledPolicy`s or `CompiledPolicySet`s that were used to generate the asserts.
1086    ///
1087    /// INVARIANT: All of these are for the same `symenv`.
1088    policies: NonEmpty<CompiledPolicies<'a>>,
1089}
1090
1091impl<'a> WellFormedAsserts<'a> {
1092    /// Returns the symbolic environment these asserts were generated in.
1093    pub fn symenv(&self) -> &SymEnv {
1094        // Relying on the INVARIANT that all the items in `self.policies` have
1095        // the same `symenv`
1096        self.policies.first().symenv()
1097    }
1098
1099    /// Returns the underlying raw [`Asserts`].
1100    pub fn asserts(&self) -> &Asserts {
1101        &self.asserts
1102    }
1103
1104    /// Returns the `CompiledPolicies` that were used to generate the asserts
1105    //
1106    // This is not a public function because the `CompiledPolicies` type is not public
1107    fn policies<'s>(&'s self) -> impl Iterator<Item = &'s CompiledPolicies<'a>> + 's {
1108        self.policies.iter()
1109    }
1110}
1111
1112/// Generate the [`WellFormedAsserts`] for the `check_never_errors()`
1113/// operation, without actually calling a solver.
1114///
1115/// That is, the result of
1116/// ```no_compile
1117/// compiler.check_unsat(never_errors_asserts(policy))
1118/// ```
1119/// should be the same as `compiler.check_never_errors_opt(policy)`.
1120///
1121/// Likewise, the result of
1122/// ```no_compile
1123/// compiler.check_sat(never_errors_asserts(policy))
1124/// ```
1125/// should be the same as `compiler.check_never_errors_with_counterexample_opt(policy)`.
1126///
1127/// NOTE: This API is an experimental feature, and the API may change
1128/// or break in the future.
1129pub fn never_errors_asserts<'a>(policy: &'a CompiledPolicy) -> WellFormedAsserts<'a> {
1130    WellFormedAsserts {
1131        asserts: verify_never_errors_opt(&policy.policy),
1132        policies: nonempty![CompiledPolicies::Policy(&policy.policy)],
1133    }
1134}
1135
1136/// Generate the [`WellFormedAsserts`] for the `check_always_matches()`
1137/// operation, without actually calling a solver.
1138///
1139/// That is, the result of
1140/// ```no_compile
1141/// compiler.check_unsat(always_matches_asserts(policy))
1142/// ```
1143/// should be the same as `compiler.check_always_matches_opt(policy)`.
1144///
1145/// Likewise, the result of
1146/// ```no_compile
1147/// compiler.check_sat(always_matches_asserts(policy))
1148/// ```
1149/// should be the same as `compiler.check_always_matches_opt(policy)`.
1150///
1151/// NOTE: This API is an experimental feature, and the API may change
1152/// or break in the future.
1153pub fn always_matches_asserts<'a>(policy: &'a CompiledPolicy) -> WellFormedAsserts<'a> {
1154    WellFormedAsserts {
1155        asserts: verify_always_matches_opt(&policy.policy),
1156        policies: nonempty![CompiledPolicies::Policy(&policy.policy)],
1157    }
1158}
1159
1160/// Generate the [`WellFormedAsserts`] for the `check_never_matches()`
1161/// operation, without actually calling a solver.
1162///
1163/// That is, the result of
1164/// ```no_compile
1165/// compiler.check_unsat(never_matches_asserts(policy))
1166/// ```
1167/// should be the same as `compiler.check_never_matches_opt(policy)`.
1168///
1169/// Likewise, the result of
1170/// ```no_compile
1171/// compiler.check_sat(never_matches_asserts(policy))
1172/// ```
1173/// should be the same as `compiler.check_never_matches_opt(policy)`.
1174///
1175/// NOTE: This API is an experimental feature, and the API may change
1176/// or break in the future.
1177pub fn never_matches_asserts<'a>(policy: &'a CompiledPolicy) -> WellFormedAsserts<'a> {
1178    WellFormedAsserts {
1179        asserts: verify_never_matches_opt(&policy.policy),
1180        policies: nonempty![CompiledPolicies::Policy(&policy.policy)],
1181    }
1182}
1183
1184/// Generate the [`WellFormedAsserts`] for the `check_matches_equivalent()`
1185/// operation, without actually calling a solver.
1186///
1187/// That is, the result of
1188/// ```no_compile
1189/// compiler.check_unsat(matches_equivalent_asserts(policy1, policy2))
1190/// ```
1191/// should be the same as `compiler.check_matches_equivalent(policy1, policy2)`.
1192///
1193/// Likewise, the result of
1194/// ```no_compile
1195/// compiler.check_sat(matches_equivalent_asserts(policy1, policy2))
1196/// ```
1197/// should be the same as `compiler.check_matches_equivalent_opt(policy1, policy2)`.
1198///
1199/// NOTE: This API is an experimental feature, and the API may change
1200/// or break in the future.
1201pub fn matches_equivalent_asserts<'a>(
1202    policy1: &'a CompiledPolicy,
1203    policy2: &'a CompiledPolicy,
1204) -> WellFormedAsserts<'a> {
1205    WellFormedAsserts {
1206        asserts: verify_matches_equivalent_opt(&policy1.policy, &policy2.policy),
1207        policies: nonempty![
1208            CompiledPolicies::Policy(&policy1.policy),
1209            CompiledPolicies::Policy(&policy2.policy)
1210        ],
1211    }
1212}
1213
1214/// Generate the [`WellFormedAsserts`] for the `check_matches_implies()`
1215/// operation, without actually calling a solver.
1216///
1217/// That is, the result of
1218/// ```no_compile
1219/// compiler.check_unsat(matches_implies_asserts(policy1, policy2))
1220/// ```
1221/// should be the same as `compiler.check_matches_implies(policy1, policy2)`.
1222///
1223/// Likewise, the result of
1224/// ```no_compile
1225/// compiler.check_sat(matches_implies_asserts(policy1, policy2))
1226/// ```
1227/// should be the same as `compiler.check_matches_implies_opt(policy1, policy2)`.
1228///
1229/// NOTE: This API is an experimental feature, and the API may change
1230/// or break in the future.
1231pub fn matches_implies_asserts<'a>(
1232    policy1: &'a CompiledPolicy,
1233    policy2: &'a CompiledPolicy,
1234) -> WellFormedAsserts<'a> {
1235    WellFormedAsserts {
1236        asserts: verify_matches_implies_opt(&policy1.policy, &policy2.policy),
1237        policies: nonempty![
1238            CompiledPolicies::Policy(&policy1.policy),
1239            CompiledPolicies::Policy(&policy2.policy)
1240        ],
1241    }
1242}
1243
1244/// Generate the [`WellFormedAsserts`] for the `check_matches_disjoint()`
1245/// operation, without actually calling a solver.
1246///
1247/// That is, the result of
1248/// ```no_compile
1249/// compiler.check_unsat(matches_disjoint_asserts(policy1, policy2))
1250/// ```
1251/// should be the same as `compiler.check_matches_disjoint(policy1, policy2)`.
1252///
1253/// Likewise, the result of
1254/// ```no_compile
1255/// compiler.check_sat(matches_disjoint_asserts(policy1, policy2))
1256/// ```
1257/// should be the same as `compiler.check_matches_disjoint_opt(policy1, policy2)`.
1258///
1259/// NOTE: This API is an experimental feature, and the API may change
1260/// or break in the future.
1261pub fn matches_disjoint_asserts<'a>(
1262    policy1: &'a CompiledPolicy,
1263    policy2: &'a CompiledPolicy,
1264) -> WellFormedAsserts<'a> {
1265    WellFormedAsserts {
1266        asserts: verify_matches_disjoint_opt(&policy1.policy, &policy2.policy),
1267        policies: nonempty![
1268            CompiledPolicies::Policy(&policy1.policy),
1269            CompiledPolicies::Policy(&policy2.policy)
1270        ],
1271    }
1272}
1273
1274/// Generate the [`WellFormedAsserts`] for the `check_always_allows()`
1275/// operation, without actually calling a solver.
1276///
1277/// That is, the result of
1278/// ```no_compile
1279/// compiler.check_unsat(always_allows_asserts(policies))
1280/// ```
1281/// should be the same as `compiler.check_always_allows(policies)`.
1282///
1283/// Likewise, the result of
1284/// ```no_compile
1285/// compiler.check_sat(always_allows_asserts(policies))
1286/// ```
1287/// should be the same as `compiler.check_always_allows_opt(policies)`.
1288///
1289/// NOTE: This API is an experimental feature, and the API may change
1290/// or break in the future.
1291pub fn always_allows_asserts<'a>(policies: &'a CompiledPolicySet) -> WellFormedAsserts<'a> {
1292    WellFormedAsserts {
1293        asserts: verify_always_allows_opt(&policies.policies),
1294        policies: nonempty![CompiledPolicies::PolicySet(&policies.policies)],
1295    }
1296}
1297
1298/// Generate the [`WellFormedAsserts`] for the `check_always_denies()`
1299/// operation, without actually calling a solver.
1300///
1301/// That is, the result of
1302/// ```no_compile
1303/// compiler.check_unsat(always_denies_asserts(policies))
1304/// ```
1305/// should be the same as `compiler.check_always_denies(policies)`.
1306///
1307/// Likewise, the result of
1308/// ```no_compile
1309/// compiler.check_sat(always_denies_asserts(policies))
1310/// ```
1311/// should be the same as `compiler.check_always_denies_opt(policies)`.
1312///
1313/// NOTE: This API is an experimental feature, and the API may change
1314/// or break in the future.
1315pub fn always_denies_asserts<'a>(policies: &'a CompiledPolicySet) -> WellFormedAsserts<'a> {
1316    WellFormedAsserts {
1317        asserts: verify_always_denies_opt(&policies.policies),
1318        policies: nonempty![CompiledPolicies::PolicySet(&policies.policies)],
1319    }
1320}
1321
1322/// Generate the [`WellFormedAsserts`] for the `check_implies()`
1323/// operation, without actually calling a solver.
1324///
1325/// That is, the result of
1326/// ```no_compile
1327/// compiler.check_unsat(implies_asserts(policies1, policies2))
1328/// ```
1329/// should be the same as `compiler.check_implies(policies1, policies2)`.
1330///
1331/// Likewise, the result of
1332/// ```no_compile
1333/// compiler.check_sat(implies_asserts(policies1, policies2))
1334/// ```
1335/// should be the same as `compiler.check_implies_opt(policies1, policies2)`.
1336///
1337/// NOTE: This API is an experimental feature, and the API may change
1338/// or break in the future.
1339pub fn implies_asserts<'a>(
1340    policies1: &'a CompiledPolicySet,
1341    policies2: &'a CompiledPolicySet,
1342) -> WellFormedAsserts<'a> {
1343    WellFormedAsserts {
1344        asserts: verify_implies_opt(&policies1.policies, &policies2.policies),
1345        policies: nonempty![
1346            CompiledPolicies::PolicySet(&policies1.policies),
1347            CompiledPolicies::PolicySet(&policies2.policies)
1348        ],
1349    }
1350}
1351
1352/// Generate the [`WellFormedAsserts`] for the `check_equivalent()`
1353/// operation, without actually calling a solver.
1354///
1355/// That is, the result of
1356/// ```no_compile
1357/// compiler.check_unsat(equivalent_asserts(policies1, policies2))
1358/// ```
1359/// should be the same as `compiler.check_equivalent(policies1, policies2)`.
1360///
1361/// Likewise, the result of
1362/// ```no_compile
1363/// compiler.check_sat(equivalent_asserts(policies1, policies2))
1364/// ```
1365/// should be the same as `compiler.check_equivalent_opt(policies1, policies2)`.
1366///
1367/// NOTE: This API is an experimental feature, and the API may change
1368/// or break in the future.
1369pub fn equivalent_asserts<'a>(
1370    policies1: &'a CompiledPolicySet,
1371    policies2: &'a CompiledPolicySet,
1372) -> WellFormedAsserts<'a> {
1373    WellFormedAsserts {
1374        asserts: verify_equivalent_opt(&policies1.policies, &policies2.policies),
1375        policies: nonempty![
1376            CompiledPolicies::PolicySet(&policies1.policies),
1377            CompiledPolicies::PolicySet(&policies2.policies)
1378        ],
1379    }
1380}
1381
1382/// Generate the [`WellFormedAsserts`] for the `check_disjoint()`
1383/// operation, without actually calling a solver.
1384///
1385/// That is, the result of
1386/// ```no_compile
1387/// compiler.check_unsat(disjoint_asserts(policies1, policies2))
1388/// ```
1389/// should be the same as `compiler.check_disjoint(policies1, policies2)`.
1390///
1391/// Likewise, the result of
1392/// ```no_compile
1393/// compiler.check_sat(disjoint_asserts(policies1, policies2))
1394/// ```
1395/// should be the same as `compiler.check_disjoint_opt(policies1, policies2)`.
1396///
1397/// NOTE: This API is an experimental feature, and the API may change
1398/// or break in the future.
1399pub fn disjoint_asserts<'a>(
1400    policies1: &'a CompiledPolicySet,
1401    policies2: &'a CompiledPolicySet,
1402) -> WellFormedAsserts<'a> {
1403    WellFormedAsserts {
1404        asserts: verify_disjoint_opt(&policies1.policies, &policies2.policies),
1405        policies: nonempty![
1406            CompiledPolicies::PolicySet(&policies1.policies),
1407            CompiledPolicies::PolicySet(&policies2.policies)
1408        ],
1409    }
1410}