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}