elicitation/contracts.rs
1//! Proof-carrying composition primitives.
2//!
3//! This module provides a minimal type-based contract system for building
4//! **formally verified** agent programs. Contracts are zero-cost proof markers that enable
5//! composing elicitation steps with machine-checked guarantees.
6//!
7//! # What Are Contracts?
8//!
9//! Contracts are **compile-time proof markers** that track guarantees through your program.
10//! Unlike runtime validation or testing, contracts provide **mathematical certainty** that
11//! invariants hold at every step of a multi-step workflow.
12//!
13//! ```text
14//! Traditional Approach Contract Approach
15//! ================== =================
16//! validate(x) validate(x) → (x, Proof)
17//! use(x) // Hope valid use(x, Proof) // Type-checked!
18//! ```
19//!
20//! **Key insight**: Validate once, carry proof forward. The type system prevents using
21//! unvalidated data, and all proofs compile away to nothing.
22//!
23//! # Overview
24//!
25//! Contracts let you build multi-step agent workflows where each step's
26//! guarantees are **checked at compile time**. Instead of re-validating data
27//! at every step, you establish proof once and carry it forward.
28//!
29//! ## Comparison to Other Approaches
30//!
31//! | Approach | When Checked | Cost | Guarantees |
32//! |----------|-------------|------|------------|
33//! | **Runtime validation** | Every use | High | None (can forget) |
34//! | **Testing** | Test time | Medium | Statistical only |
35//! | **Static analysis** | Compile time | Low | Heuristic |
36//! | **Contracts (this)** | Compile time | **Zero** | **Mathematical** |
37//!
38//! ## Why Not Dependent Types?
39//!
40//! Dependent type systems (Idris, Agda, Coq) provide similar guarantees but:
41//! - Require theorem proving skills
42//! - Have steep learning curves
43//! - Don't integrate with existing Rust code
44//!
45//! Contracts give you **80% of the benefit with 5% of the complexity**.
46//!
47//! # Quick Start
48//!
49//! ```rust
50//! use elicitation::contracts::{Established, And, both};
51//!
52//! // Define your workflow's propositions
53//! #[derive(elicitation::Prop)]
54//! struct EmailValidated;
55//! #[derive(elicitation::Prop)]
56//! struct ConsentObtained;
57//!
58//! // Step 1: Validate (returns proof if valid)
59//! fn validate_email(email: &str) -> Option<Established<EmailValidated>> {
60//! if email.contains('@') { Some(Established::assert()) } else { None }
61//! }
62//!
63//! // Step 2: Function requiring BOTH proofs
64//! fn register_user(
65//! email: String,
66//! _proof: Established<And<EmailValidated, ConsentObtained>>
67//! ) {
68//! println!("Registered: {}", email);
69//! }
70//!
71//! // Compose: Can't call register_user without both proofs!
72//! # let email = "user@example.com";
73//! if let Some(email_proof) = validate_email(email) {
74//! let consent_proof = Established::assert(); // Would come from consent flow
75//! let combined = both(email_proof, consent_proof);
76//! register_user(email.to_string(), combined); // ✅ Compiles
77//! }
78//! // register_user(email.to_string(), ...); // ❌ Won't compile without proof
79//! ```
80//!
81//! # Core Concepts
82//!
83//! - **Proposition (`Prop`)**: A type-level statement that can be true or false
84//! - **Proof (`Established<P>`)**: Evidence that proposition P holds
85//! - **Inhabitation (`Is<T>`)**: The proposition that a value inhabits type T
86//!
87//! # Example
88//!
89//! ```rust
90//! use elicitation::contracts::{Prop, Established, Is};
91//! use std::marker::PhantomData;
92//!
93//! // A proposition: value inhabits String
94//! type StringValid = Is<String>;
95//!
96//! // Function that requires proof
97//! fn use_validated_string(
98//! s: String,
99//! _proof: Established<StringValid>
100//! ) {
101//! println!("Processing: {}", s);
102//! }
103//!
104//! // Establish proof after validation
105//! let s = String::from("hello");
106//! let proof = Established::assert();
107//! use_validated_string(s, proof);
108//! ```
109//!
110//! # Design Principles
111//!
112//! - **Zero runtime cost**: All proofs are `PhantomData` and disappear at compile time
113//! - **Minimal logic**: Just what's needed for composition (no quantifiers, no negation)
114//! - **Type-safe composition**: Cannot call functions without required proofs
115//! - **Monotonic refinement**: Guarantees accumulate, never weaken unexpectedly
116//!
117//! # When to Use
118//!
119//! Use contracts when:
120//! - Building multi-step agent flows with dependencies between steps
121//! - Enforcing preconditions that must be established by prior steps
122//! - Verifying that validation happens before use (no re-validation needed)
123//!
124//! Don't use contracts when:
125//! - Single-step elicitation (just use `.elicit()` directly)
126//! - No dependencies between steps
127//! - Performance is so critical you can't afford any abstraction (though cost is zero!)
128//!
129//! # Multi-Step Composition Example
130//!
131//! ```rust
132//! use elicitation::contracts::{Established, And, both, Is};
133//!
134//! // Define propositions for agent workflow
135//! #[derive(elicitation::Prop)]
136//! struct EmailValidated;
137//! #[derive(elicitation::Prop)]
138//! struct ConsentObtained;
139//!
140//! // Step 1: Validate email (establishes EmailValidated)
141//! fn validate_email(email: &str) -> Option<Established<EmailValidated>> {
142//! if email.contains('@') {
143//! Some(Established::assert())
144//! } else {
145//! None
146//! }
147//! }
148//!
149//! // Step 2: Get consent (establishes ConsentObtained)
150//! fn get_consent(user: &str) -> Established<ConsentObtained> {
151//! println!("Getting consent from {}", user);
152//! Established::assert()
153//! }
154//!
155//! // Step 3: Register user (requires BOTH proofs)
156//! fn register_user(
157//! email: String,
158//! _proof: Established<And<EmailValidated, ConsentObtained>>
159//! ) {
160//! println!("Registered: {}", email);
161//! }
162//!
163//! // Compose the workflow
164//! let email = "user@example.com";
165//! if let Some(email_proof) = validate_email(email) {
166//! let consent_proof = get_consent(email);
167//! let combined_proof = both(email_proof, consent_proof);
168//! register_user(email.to_string(), combined_proof);
169//! }
170//! ```
171//!
172//! # API Overview
173//!
174//! ## Core Types
175//!
176//! - [`Prop`]: Marker trait for propositions (type-level statements)
177//! - [`Established<P>`]: Proof that proposition P holds
178//! - [`Is<T>`]: Proposition that a value inhabits type T
179//!
180//! ## Logical Operators
181//!
182//! - [`And<P, Q>`][]: Conjunction (both P and Q hold)
183//! - [`Implies<Q>`][]: Implication (P → Q)
184//! - [`Refines<Base>`][]: Type refinement (Refined is a Base with extra constraints)
185//! - [`InVariant<E, V>`][]: Enum is in specific variant
186//!
187//! ## Composition Functions
188//!
189//! - [`both(p, q)`](crate::both): Combine two proofs into conjunction
190//! - `fst(pq)` — Project left proof from conjunction
191//! - `snd(pq)` — Project right proof from conjunction
192//! - `downcast(refined)` — Safe downcast from refined type to base
193//!
194//! # Advanced Patterns
195//!
196//! ## State Machines
197//!
198//! Use `InVariant` to enforce state transitions:
199//!
200//! ```rust
201//! use elicitation::contracts::{Established, InVariant};
202//!
203//! enum Workflow { Draft, Review, Approved }
204//! struct DraftVariant;
205//! struct ReviewVariant;
206//!
207//! fn submit(
208//! _workflow: Workflow,
209//! _draft: Established<InVariant<Workflow, DraftVariant>>
210//! ) -> Established<InVariant<Workflow, ReviewVariant>> {
211//! Established::assert()
212//! }
213//!
214//! // Can only submit from Draft state (type-checked!)
215//! ```
216//!
217//! ## Type Refinement
218//!
219//! Use `Refines` for type hierarchies. Note: both traits must be implemented
220//! in your crate to satisfy orphan rules:
221//!
222//! ```rust,ignore
223//! use elicitation::contracts::{Refines, Is, Established, Implies, downcast};
224//!
225//! struct NonEmptyString(String);
226//! impl Refines<String> for NonEmptyString {}
227//! impl Implies<Is<String>> for Is<NonEmptyString> {}
228//!
229//! let refined: Established<Is<NonEmptyString>> = Established::assert();
230//! let base: Established<Is<String>> = downcast(refined);
231//! // Safe: NonEmptyString is always a String
232//! ```
233//!
234//! # Integration with Elicitation
235//!
236//! Use `elicit_proven()` to get values with proofs:
237//!
238//! ```rust,ignore
239//! use elicitation::{Elicitation, contracts::{Established, Is}};
240//!
241//! // Elicit with proof
242//! let (email, proof): (String, Established<Is<String>>) =
243//! String::elicit_proven(&client).await?;
244//!
245//! // Pass proof to functions requiring validation
246//! send_email(email, proof).await?;
247//! ```
248//!
249//! # Migration Guide
250//!
251//! Contracts are **100% opt-in** and don't affect existing code:
252//!
253//! ```text
254//! Before (still works):
255//! let email = String::elicit(&client).await?;
256//! use_email(email);
257//!
258//! After (with contracts):
259//! let (email, proof) = String::elicit_proven(&client).await?;
260//! use_email_proven(email, proof);
261//! ```
262//!
263//! You can adopt incrementally:
264//! 1. Start with single-step validation
265//! 2. Add contracts to critical paths
266//! 3. Extend to full workflows over time
267//!
268//! # Performance
269//!
270//! **Zero cost at runtime**: All proofs are `PhantomData<fn() -> T>` and compile away completely.
271//!
272//! ```rust
273//! use elicitation::contracts::{Established, Is};
274//!
275//! let proof: Established<Is<String>> = Established::assert();
276//! assert_eq!(std::mem::size_of_val(&proof), 0); // Zero bytes!
277//! ```
278//!
279//! Benchmarks show no measurable overhead compared to unvalidated code.
280//!
281//! # Formal Verification
282//!
283//! All core properties are **formally verified with Kani**:
284//!
285//! - ✅ Proofs are zero-sized (verified with symbolic execution)
286//! - ✅ Cannot call functions without required proofs (type system + Kani)
287//! - ✅ Composition preserves invariants (Kani proves `then()` and `both_tools()`)
288//! - ✅ Refinement is sound (Kani proves `downcast()` safety)
289//!
290//! See `src/kani_tests.rs` for complete verification harnesses.
291//!
292//! # When NOT to Use Contracts
293//!
294//! - **Single-step operations**: Just use regular functions
295//! - **External APIs**: You can't force external code to use proofs
296//! - **Prototyping**: Add contracts after the design stabilizes
297//! - **Performance-critical inner loops**: (Though cost is zero, type complexity adds compile time)
298//!
299//! # Further Reading
300//!
301//! - [Tool contracts](crate::tool): MCP tools with preconditions/postconditions
302//! - [Examples](../../examples): Complete working examples
303//! - [Kani verification](https://model-checking.github.io/kani/): How we verify properties
304
305use proc_macro2;
306use std::marker::PhantomData;
307
308/// Marker trait: types that represent propositions.
309///
310/// A proposition is a type-level statement that can be true or false.
311/// Propositions are combined using logical operators (`And`, `Implies`)
312/// and witnessed by `Established<P>` proofs.
313///
314/// # Examples
315///
316/// ```rust
317/// use elicitation::contracts::{Prop, Is};
318///
319/// // Built-in proposition: value inhabits type T
320/// type StringProp = Is<String>;
321/// ```
322pub trait Prop: 'static {
323 /// Generate a Kani proof harness for this proposition.
324 ///
325 /// Returns a [`proc_macro2::TokenStream`] containing a `#[kani::proof]` harness
326 /// that encodes this proposition as a trusted axiom. There is no default —
327 /// every proposition must supply a proof. Use `#[derive(Prop)]` for trivial
328 /// zero-cost marker propositions.
329 ///
330 /// Available with the `proofs` feature.
331 fn kani_proof() -> proc_macro2::TokenStream;
332
333 /// Generate a Verus proof for this proposition.
334 ///
335 /// Returns a [`proc_macro2::TokenStream`] containing a Verus-verified function
336 /// encoding this proposition's postcondition invariant. There is no default —
337 /// use `#[derive(Prop)]` for trivial marker propositions.
338 ///
339 /// Available with the `proofs` feature.
340 fn verus_proof() -> proc_macro2::TokenStream;
341
342 /// Generate a Creusot contract proof for this proposition.
343 ///
344 /// Returns a [`proc_macro2::TokenStream`] containing a `#[trusted]` Creusot
345 /// contract function encoding this proposition's postcondition. There is no
346 /// default — use `#[derive(Prop)]` for trivial marker propositions.
347 ///
348 /// Available with the `proofs` feature.
349 fn creusot_proof() -> proc_macro2::TokenStream;
350
351 /// The name of the Creusot pearlite `#[logic]` function that expresses this
352 /// proposition as a predicate over the machine state.
353 ///
354 /// When non-empty, `#[formal_method]`-generated Creusot companions emit
355 /// real `#[requires]` / `#[ensures]` contracts using this function instead
356 /// of the weaker `#[trusted]` placeholder.
357 ///
358 /// Set via `#[prop(creusot_invariant_fn = "my_fn")]` on the prop struct.
359 /// The named function must be a `#[cfg(creusot)] #[logic]` function in scope
360 /// wherever the generated companions are compiled.
361 fn creusot_invariant_fn_name() -> &'static str {
362 ""
363 }
364
365 /// The name of a `#[cfg(kani)]` boolean predicate function that expresses
366 /// this proposition as a Kani-evaluable invariant check.
367 ///
368 /// When non-empty, [`formal_method`][crate::formal_method]-generated companion
369 /// structs emit `#[kani::requires(fn(&state))]` / `#[kani::ensures]` contracted
370 /// wrappers, enabling `#[kani::proof_for_contract]` closure proofs that close
371 /// the depth-based inductive argument into a full symbolic proof.
372 ///
373 /// Set via `#[prop(kani_invariant_fn = "my_fn")]` on the prop struct.
374 fn kani_invariant_fn_name() -> &'static str {
375 ""
376 }
377
378 /// The name of a Verus `pub open spec fn` that expresses this proposition
379 /// as a Verus-verifiable predicate over the machine state.
380 ///
381 /// When non-empty, [`formal_method`][crate::formal_method]-generated companion
382 /// structs emit `assume_specification` blocks with real `requires`/`ensures`
383 /// clauses using this function, enabling Verus to check invariant preservation.
384 ///
385 /// Set via `#[prop(verus_invariant_fn = "my_fn")]` on the prop struct.
386 fn verus_invariant_fn_name() -> &'static str {
387 ""
388 }
389}
390
391/// Witness that proposition P has been established.
392///
393/// This is a zero-sized proof marker. Its existence proves that
394/// proposition P holds in the current context.
395///
396/// # Zero Cost
397///
398/// ```rust
399/// use elicitation::contracts::{Established, Is};
400/// use std::mem::size_of;
401///
402/// let proof: Established<Is<String>> = Established::assert();
403/// assert_eq!(size_of::<Established<Is<String>>>(), 0);
404/// ```
405///
406/// # Safety Model
407///
408/// `Established<P>` is a semantic contract, not a memory safety guarantee.
409/// Calling `Established::assert()` asserts that P is true. The type system
410/// ensures you cannot call functions requiring proof without providing it,
411/// but it's your responsibility to only assert when P actually holds.
412pub struct Established<P: Prop> {
413 _marker: PhantomData<fn() -> P>,
414}
415
416/// Relation: proposition `P` can be proven from credential `C`.
417///
418/// Implement this on a `Prop` type to declare which credentials can mint it.
419/// The only way to construct `Established<P>` externally is via
420/// `Established::<P>::prove(&credential)` where `P: ProvableFrom<C>`.
421pub trait ProvableFrom<C>: Prop {}
422
423/// Declare one or more proof-credential ZST types and wire each to its proposition.
424///
425/// # Syntax
426///
427/// ```text
428/// proof_credential! {
429/// /// Optional doc comment.
430/// VISIBILITY CredentialName => PropositionType;
431/// ...
432/// }
433/// ```
434///
435/// Each entry emits:
436/// 1. A zero-sized struct `CredentialName` with the given visibility and doc comments.
437/// 2. `impl ProvableFrom<CredentialName> for PropositionType {}`
438///
439/// Credentials are typically `pub(crate)` so only the factory method that
440/// performed the runtime check can construct them. External code cannot call
441/// `Established::prove(&CredentialName)` without access to the type.
442///
443/// # Example
444///
445/// ```rust
446/// use elicitation::contracts::{Established, Prop, ProvableFrom};
447/// use elicitation::proof_credential;
448///
449/// #[derive(elicitation::Prop)]
450/// pub struct ContrastChecked;
451///
452/// proof_credential! {
453/// /// Witness that a colour pair was verified for WCAG contrast.
454/// pub(crate) NormalContrastVerified => ContrastChecked;
455/// }
456///
457/// // Inside the factory (same crate):
458/// let proof: Established<ContrastChecked> =
459/// Established::prove(&NormalContrastVerified);
460/// ```
461#[macro_export]
462macro_rules! proof_credential {
463 (
464 $(
465 $(#[$meta:meta])*
466 $vis:vis $cred:ident => $prop:ty;
467 )*
468 ) => {
469 $(
470 $(#[$meta])*
471 $vis struct $cred;
472
473 impl $crate::contracts::ProvableFrom<$cred> for $prop {}
474 )*
475 };
476}
477
478impl<P: Prop> Established<P> {
479 /// Mint a proof token by presenting a valid credential.
480 ///
481 /// The credential type `C` must implement `ProvableFrom<C>` for `P`,
482 /// establishing a declared relationship between the two.
483 ///
484 /// # Examples
485 ///
486 /// ```rust
487 /// use elicitation::contracts::{Established, Is, Prop, ProvableFrom};
488 ///
489 /// #[derive(elicitation::Prop)]
490 /// struct InputValidated;
491 ///
492 /// struct ValidInput(String);
493 /// impl ProvableFrom<ValidInput> for InputValidated {}
494 ///
495 /// let input = ValidInput("hello".to_string());
496 /// let proof: Established<InputValidated> = Established::prove(&input);
497 /// ```
498 #[inline(always)]
499 pub fn prove<C>(_credential: &C) -> Self
500 where
501 P: ProvableFrom<C>,
502 {
503 Self {
504 _marker: PhantomData,
505 }
506 }
507
508 /// Assert that proposition `P` holds without a credential.
509 ///
510 /// This is the unchecked escape hatch: callers take responsibility for
511 /// ensuring `P` genuinely holds. Prefer
512 /// [`prove`][Established::prove] when a credential type exists, since
513 /// `prove` encodes the check in the type system.
514 ///
515 /// WCAG proposition types are protected by their credential types being
516 /// `pub(crate)` within `elicit_ui` — even with `assert()` available,
517 /// external code cannot construct the required credential to call
518 /// `prove()`, and calling `assert()` on a WCAG type directly is a clear
519 /// audit-trail violation.
520 #[inline(always)]
521 pub fn assert() -> Self {
522 Self {
523 _marker: PhantomData,
524 }
525 }
526
527 /// Weaken proof to a more general proposition.
528 ///
529 /// If P implies Q, then a proof of P can be used as a proof of Q.
530 /// This is logical weakening - moving from a stronger to a weaker claim.
531 ///
532 /// # Examples
533 ///
534 /// ```rust
535 /// use elicitation::contracts::{Established, Is, Implies};
536 /// use std::marker::PhantomData;
537 ///
538 /// // StringNonEmpty implies String (via refinement)
539 /// #[derive(elicitation::Prop)]
540 /// struct StringNonEmpty;
541 /// impl Implies<Is<String>> for StringNonEmpty {}
542 ///
543 /// let strong_proof: Established<StringNonEmpty> = Established::assert();
544 /// let weak_proof: Established<Is<String>> = strong_proof.weaken();
545 /// ```
546 #[inline(always)]
547 pub fn weaken<Q: Prop>(self) -> Established<Q>
548 where
549 P: Implies<Q>,
550 {
551 Established {
552 _marker: PhantomData,
553 }
554 }
555}
556
557// Make Established copyable (it's zero-sized)
558impl<P: Prop> Copy for Established<P> {}
559impl<P: Prop> Clone for Established<P> {
560 fn clone(&self) -> Self {
561 *self
562 }
563}
564
565// Under Kani, `stub_verified` replaces a call with `kani::any()` of the
566// return type. `Established<P>` is a ZST — its unique value is trivially
567// constructible, so any() just returns the unit token.
568#[cfg(kani)]
569impl<P: Prop> kani::Arbitrary for Established<P> {
570 fn any() -> Self {
571 Self {
572 _marker: std::marker::PhantomData,
573 }
574 }
575}
576
577impl<P: Prop> std::fmt::Debug for Established<P> {
578 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579 write!(f, "Established")
580 }
581}
582
583impl<P: Prop + crate::Elicitation> crate::Prompt for Established<P> {
584 fn prompt() -> Option<&'static str> {
585 P::prompt()
586 }
587}
588
589impl<P: Prop + crate::Elicitation + Send> crate::Elicitation for Established<P> {
590 type Style = <P as crate::Elicitation>::Style;
591
592 #[tracing::instrument(skip(communicator))]
593 async fn elicit<C: crate::ElicitCommunicator>(communicator: &C) -> crate::ElicitResult<Self> {
594 tracing::debug!("Eliciting proof proposition for Established");
595 let _p = P::elicit(communicator).await?;
596 Ok(Established::assert())
597 }
598
599 fn kani_proof() -> proc_macro2::TokenStream {
600 proc_macro2::TokenStream::new()
601 }
602
603 fn verus_proof() -> proc_macro2::TokenStream {
604 proc_macro2::TokenStream::new()
605 }
606
607 fn creusot_proof() -> proc_macro2::TokenStream {
608 proc_macro2::TokenStream::new()
609 }
610}
611
612/// `Established<P>` is a zero-sized proof token — re-asserting it in emitted
613/// code is safe because the token carries no runtime state. The emitted call
614/// `Established::<P>::assert()` is identical to the original construction.
615impl<P: Prop + 'static> crate::emit_code::ToCodeLiteral for Established<P> {
616 fn type_tokens() -> proc_macro2::TokenStream {
617 let p: proc_macro2::TokenStream = std::any::type_name::<P>()
618 .parse()
619 .unwrap_or_else(|_| quote::quote! { _ });
620 quote::quote! { ::elicitation::Established<#p> }
621 }
622
623 fn to_code_literal(&self) -> proc_macro2::TokenStream {
624 let ty = <Self as crate::emit_code::ToCodeLiteral>::type_tokens();
625 quote::quote! { <#ty>::assert() }
626 }
627}
628
629/// `Established<P>` is a zero-sized proof token — it carries no user-facing
630/// prompt. Any struct that holds one as a field still needs the bound satisfied
631/// when `prompt-tree` is enabled; we return an empty `Leaf` so the derive
632/// can compile without noise in the assembled prompt output.
633impl<P: Prop> crate::ElicitPromptTree for Established<P> {
634 fn prompt_tree() -> crate::PromptTree {
635 crate::PromptTree::Leaf {
636 prompt: String::new(),
637 type_name: "Established".to_string(),
638 }
639 }
640}
641
642/// `Established<P>` is a zero-sized proof token. Serialization is trivial —
643/// the token carries no runtime state, so it round-trips through any format
644/// as a unit value. Deserializing reconstructs the token via `assert()`,
645/// which is safe because the surrounding serialized state already encodes the
646/// invariant that P holds (e.g. the bet balance is present in the ledger).
647#[cfg(feature = "serde")]
648impl<P: Prop> serde::Serialize for Established<P> {
649 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
650 serializer.serialize_unit()
651 }
652}
653
654#[cfg(feature = "serde")]
655impl<'de, P: Prop> serde::Deserialize<'de> for Established<P> {
656 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
657 <()>::deserialize(deserializer)?;
658 Ok(Self::assert())
659 }
660}
661
662impl<P: Prop + 'static> schemars::JsonSchema for Established<P> {
663 fn schema_name() -> std::borrow::Cow<'static, str> {
664 "EstablishedProof".into()
665 }
666
667 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
668 schemars::json_schema!({ "type": "null" })
669 }
670}
671
672/// Proposition: value inhabits type T with its invariants.
673///
674/// `Is<T>` represents the statement "a value of type T exists and
675/// satisfies T's type invariants". For contract types (like `StringNonEmpty`),
676/// this includes the contract's preconditions.
677///
678/// # Examples
679///
680/// ```rust
681/// use elicitation::contracts::Is;
682///
683/// // Proposition: a valid String exists
684/// type StringValid = Is<String>;
685///
686/// // Proposition: a non-empty String exists (with contract)
687/// // type StringNonEmptyValid = Is<StringNonEmpty>;
688/// ```
689pub struct Is<T> {
690 _marker: PhantomData<fn() -> T>,
691}
692
693impl<T: 'static> Prop for Is<T> {
694 fn kani_proof() -> proc_macro2::TokenStream {
695 proc_macro2::TokenStream::new()
696 }
697
698 fn verus_proof() -> proc_macro2::TokenStream {
699 proc_macro2::TokenStream::new()
700 }
701
702 fn creusot_proof() -> proc_macro2::TokenStream {
703 proc_macro2::TokenStream::new()
704 }
705}
706
707/// Logical implication: P implies Q.
708///
709/// If P implies Q, then whenever P is true, Q must also be true.
710/// This enables weakening: converting a proof of P into a proof of Q.
711///
712/// # Laws
713///
714/// 1. **Reflexivity**: Every proposition implies itself (P → P)
715/// 2. **Transitivity**: If P → Q and Q → R, then P → R
716///
717/// # Examples
718///
719/// ```rust
720/// use elicitation::contracts::Implies;
721///
722/// #[derive(elicitation::Prop)]
723/// struct Strong;
724/// #[derive(elicitation::Prop)]
725/// struct Weak;
726///
727/// // Declare that Strong implies Weak
728/// impl Implies<Weak> for Strong {}
729/// ```
730pub trait Implies<Q: Prop>: Prop {}
731
732// Reflexivity: Every proposition implies itself
733impl<P: Prop> Implies<P> for P {}
734
735/// Type alias to reduce PhantomData complexity.
736type AndMarker<P, Q> = (fn() -> P, fn() -> Q);
737
738/// Logical conjunction: both P and Q hold.
739///
740/// `And<P, Q>` represents the proposition that both P and Q are true.
741/// This enables combining multiple proofs into a single compound proof.
742///
743/// # Properties
744///
745/// - **Commutative** (logically): P ∧ Q ≡ Q ∧ P
746/// - **Associative** (logically): (P ∧ Q) ∧ R ≡ P ∧ (Q ∧ R)
747/// - **Projectable**: And<P, Q> → P and And<P, Q> → Q
748///
749/// # Examples
750///
751/// ```rust
752/// use elicitation::contracts::{Established, And, both, fst, snd};
753/// use std::marker::PhantomData;
754///
755/// // Two propositions
756/// #[derive(elicitation::Prop)]
757/// struct ValidUrl;
758/// #[derive(elicitation::Prop)]
759/// struct HasPort;
760///
761/// let url_proof: Established<ValidUrl> = Established::assert();
762/// let port_proof: Established<HasPort> = Established::assert();
763///
764/// // Combine into conjunction
765/// let both_proof: Established<And<ValidUrl, HasPort>> = both(url_proof, port_proof);
766///
767/// // Project back out
768/// let url_again: Established<ValidUrl> = fst(both_proof);
769/// let port_again: Established<HasPort> = snd(both_proof);
770/// ```
771pub struct And<P: Prop, Q: Prop> {
772 _marker: PhantomData<AndMarker<P, Q>>,
773}
774
775impl<P: Prop, Q: Prop> Prop for And<P, Q> {
776 fn kani_proof() -> proc_macro2::TokenStream {
777 let mut ts = P::kani_proof();
778 ts.extend(Q::kani_proof());
779 ts
780 }
781
782 fn verus_proof() -> proc_macro2::TokenStream {
783 let mut ts = P::verus_proof();
784 ts.extend(Q::verus_proof());
785 ts
786 }
787
788 fn creusot_proof() -> proc_macro2::TokenStream {
789 let mut ts = P::creusot_proof();
790 ts.extend(Q::creusot_proof());
791 ts
792 }
793}
794
795/// Combine two proofs into a conjunction.
796///
797/// Given proofs of P and Q, construct a proof that both hold.
798///
799/// # Examples
800///
801/// ```rust
802/// use elicitation::contracts::{Established, And, both};
803///
804/// #[derive(elicitation::Prop)]
805/// struct P;
806/// #[derive(elicitation::Prop)]
807/// struct Q;
808///
809/// let p: Established<P> = Established::assert();
810/// let q: Established<Q> = Established::assert();
811/// let pq: Established<And<P, Q>> = both(p, q);
812/// ```
813#[inline(always)]
814pub fn both<P: Prop, Q: Prop>(_p: Established<P>, _q: Established<Q>) -> Established<And<P, Q>> {
815 Established {
816 _marker: PhantomData,
817 }
818}
819
820/// Project left component from conjunction.
821///
822/// Given a proof that both P and Q hold, extract a proof of P.
823///
824/// # Examples
825///
826/// ```rust
827/// use elicitation::contracts::{Established, And, both, fst};
828///
829/// #[derive(elicitation::Prop)]
830/// struct P;
831/// #[derive(elicitation::Prop)]
832/// struct Q;
833///
834/// let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
835/// let p: Established<P> = fst(pq);
836/// ```
837#[inline(always)]
838pub fn fst<P: Prop, Q: Prop>(_both: Established<And<P, Q>>) -> Established<P> {
839 Established {
840 _marker: PhantomData,
841 }
842}
843
844/// Project right component from conjunction.
845///
846/// Given a proof that both P and Q hold, extract a proof of Q.
847///
848/// # Examples
849///
850/// ```rust
851/// use elicitation::contracts::{Established, And, both, snd};
852///
853/// #[derive(elicitation::Prop)]
854/// struct P;
855/// #[derive(elicitation::Prop)]
856/// struct Q;
857///
858/// let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
859/// let q: Established<Q> = snd(pq);
860/// ```
861#[inline(always)]
862pub fn snd<P: Prop, Q: Prop>(_both: Established<And<P, Q>>) -> Established<Q> {
863 Established {
864 _marker: PhantomData,
865 }
866}
867
868/// Type-level refinement: `Self` refines `Base`.
869///
870/// A refinement type has all the properties of its base type, plus
871/// additional constraints. This means:
872/// - If a value inhabits the refined type, it necessarily inhabits the base type
873/// - You can safely downcast from refined proof to base proof
874/// - You cannot upcast (compile error without additional validation)
875///
876/// # Laws
877///
878/// 1. **Reflexivity**: Every type refines itself (T → T)
879/// 2. **Transitivity**: If A refines B and B refines C, then A refines C
880/// 3. **Inhabitation**: `Is<Refined>` implies `Is<Base>` (requires explicit `Implies` impl)
881///
882/// # Example
883///
884/// ```rust,no_run
885/// use elicitation::contracts::{Refines, Is, Established, Implies, Prop, downcast};
886///
887/// // Define a refined string type (in practice, would have validation)
888/// struct NonEmptyString(String);
889///
890/// // Declare refinement relationship and implication
891/// impl Refines<String> for NonEmptyString {}
892/// // In actual code: impl Implies<Is<String>> for Is<NonEmptyString> {}
893///
894/// // Can downcast from refined to base:
895/// // let refined_proof: Established<Is<NonEmptyString>> = Established::assert();
896/// // let base_proof: Established<Is<String>> = downcast(refined_proof);
897/// ```
898pub trait Refines<Base>: 'static {}
899
900// Reflexivity: every type refines itself
901impl<T: 'static> Refines<T> for T {}
902
903/// Downcast proof from refined type to base type.
904///
905/// If you have a proof that a value inhabits a refined type,
906/// you automatically have proof it inhabits the base type.
907///
908/// This works via the `Refines` trait relationship. Users must
909/// implement `Implies<Is<Base>>` for `Is<Refined>` to enable downcasting.
910///
911/// # Examples
912///
913/// ```rust,no_run
914/// use elicitation::contracts::{Refines, Is, Established, Implies, Prop, downcast};
915///
916/// struct NonEmptyString(String);
917/// impl Refines<String> for NonEmptyString {}
918///
919/// // In actual code, you'd implement this in your crate:
920/// // impl Implies<Is<String>> for Is<NonEmptyString> {}
921///
922/// // Then downcast works:
923/// // let refined: Established<Is<NonEmptyString>> = Established::assert();
924/// // let base: Established<Is<String>> = downcast(refined);
925/// ```
926#[inline(always)]
927pub fn downcast<Base: 'static, Refined: Refines<Base>>(
928 proof: Established<Is<Refined>>,
929) -> Established<Is<Base>>
930where
931 Is<Refined>: Implies<Is<Base>>,
932{
933 proof.weaken()
934}
935
936/// Type alias to reduce PhantomData complexity.
937type InVariantMarker<E, V> = (fn() -> E, fn() -> V);
938
939/// Proposition: enum value is in specific variant.
940///
941/// `InVariant<E, V>` represents the statement "enum E is currently
942/// in variant V". This enables variant-specific proofs for enum-based
943/// state machines.
944///
945/// # Type Parameters
946///
947/// - `E`: The enum type
948/// - `V`: A marker type representing the variant (typically a unit struct)
949///
950/// # Example
951///
952/// ```rust
953/// use elicitation::contracts::{InVariant, Established, Prop};
954///
955/// enum Status {
956/// Active,
957/// Inactive,
958/// }
959///
960/// // Marker types for variants
961/// struct ActiveVariant;
962/// struct InactiveVariant;
963///
964/// // Use InVariant to track which variant
965/// fn process_active(_status: Status, _proof: Established<InVariant<Status, ActiveVariant>>) {
966/// // This function can only be called with Active variant
967/// println!("Processing active status");
968/// }
969/// ```
970pub struct InVariant<E, V> {
971 _marker: PhantomData<InVariantMarker<E, V>>,
972}
973
974impl<E: 'static, V: 'static> Prop for InVariant<E, V> {
975 fn kani_proof() -> proc_macro2::TokenStream {
976 proc_macro2::TokenStream::new()
977 }
978
979 fn verus_proof() -> proc_macro2::TokenStream {
980 proc_macro2::TokenStream::new()
981 }
982
983 fn creusot_proof() -> proc_macro2::TokenStream {
984 proc_macro2::TokenStream::new()
985 }
986}
987
988// ── Formal Methods ────────────────────────────────────────────────────────────
989
990/// A function that consumes a proof and produces a proof.
991///
992/// Any `Fn(In, Established<PIn>) -> (Out, Established<POut>)` automatically
993/// implements this trait via the blanket impl below. The signature IS the
994/// contract declaration — no attribute or registry required.
995///
996/// # Type-driven call-graph closure
997///
998/// A function calling an informal helper cannot derive `Established<POut>` from
999/// that helper's result without using [`Established::assert`], the explicit,
1000/// auditable escape hatch. The type system therefore closes the call graph
1001/// automatically: proof tokens only flow from formal call sites.
1002///
1003/// # Evidence bundles
1004///
1005/// When multiple propositions must be satisfied, use an evidence bundle struct
1006/// (a plain struct whose fields are `Established<P>` tokens that implements
1007/// `ProvableFrom<Bundle>`). Pass the bundle as `In` or as `PIn` evidence.
1008///
1009/// # Examples
1010///
1011/// ```rust
1012/// use elicitation::contracts::{Established, FormalMethod, Prop};
1013///
1014/// #[derive(elicitation::Prop)]
1015/// struct Validated;
1016/// #[derive(elicitation::Prop)]
1017/// struct Processed;
1018///
1019/// // Any function with the matching signature is automatically a FormalMethod.
1020/// fn process(input: String, _proof: Established<Validated>)
1021/// -> (String, Established<Processed>)
1022/// {
1023/// (input.to_uppercase(), Established::assert())
1024/// }
1025///
1026/// // Use via the trait or call directly — both work.
1027/// let proof_in = Established::assert();
1028/// let (_result, _proof_out) = process("hello".to_string(), proof_in);
1029/// ```
1030pub trait FormalMethod<In, PIn: Prop, Out, POut: Prop> {
1031 /// Call this method, consuming the input proof and producing an output proof.
1032 fn call_formal(&self, input: In, proof: Established<PIn>) -> (Out, Established<POut>);
1033
1034 /// Generate a Kani proof harness for this method's precondition/postcondition.
1035 ///
1036 /// The default returns an empty token stream (no harness). Override to emit
1037 /// a `#[kani::proof]` harness asserting that the method honours its contract.
1038 fn kani_harness() -> proc_macro2::TokenStream {
1039 proc_macro2::TokenStream::new()
1040 }
1041
1042 /// Generate a Verus proof harness for this method's contract.
1043 fn verus_harness() -> proc_macro2::TokenStream {
1044 proc_macro2::TokenStream::new()
1045 }
1046
1047 /// Generate a Creusot contract for this method.
1048 fn creusot_harness() -> proc_macro2::TokenStream {
1049 proc_macro2::TokenStream::new()
1050 }
1051}
1052
1053/// Blanket impl: any `Fn(In, Established<PIn>) -> (Out, Established<POut>)`
1054/// is automatically a `FormalMethod`.
1055impl<F, In, PIn: Prop, Out, POut: Prop> FormalMethod<In, PIn, Out, POut> for F
1056where
1057 F: Fn(In, Established<PIn>) -> (Out, Established<POut>),
1058{
1059 #[inline(always)]
1060 fn call_formal(&self, input: In, proof: Established<PIn>) -> (Out, Established<POut>) {
1061 self(input, proof)
1062 }
1063}
1064
1065// ── Verified State Machines ───────────────────────────────────────────────────
1066
1067/// A state machine whose states are fully described and whose transitions
1068/// preserve a declared invariant.
1069///
1070/// # Contract
1071///
1072/// A `VerifiedStateMachine` declares two associated types:
1073///
1074/// - `State` — must be [`ElicitComplete`][crate::ElicitComplete]: fully
1075/// introspectable, serialisable, and reasoned about by elicitation tooling.
1076/// - `Invariant` — a [`Prop`] that every valid transition must preserve.
1077///
1078/// Transitions are [`FormalMethod`]s with signature
1079/// `(State, Established<Invariant>) -> (State, Established<Invariant>)`.
1080/// The type system guarantees that a transition cannot produce a new state
1081/// without presenting proof that the invariant still holds.
1082///
1083/// # "Gated community" for formal verification
1084///
1085/// Declaring a `VerifiedStateMachine` is the top-level compiler-enforced
1086/// claim that a system preserves its invariants. Outside a VSM any piece of
1087/// the contracts stack can be used freely; inside, every transition must be
1088/// a `FormalMethod`.
1089///
1090pub trait VerifiedStateMachine {
1091 /// The state type. Must be [`ElicitComplete`][crate::ElicitComplete].
1092 type State: crate::ElicitComplete;
1093
1094 /// The invariant that every transition must preserve.
1095 type Invariant: Prop;
1096
1097 /// Return the Kani harness [`proc_macro2::TokenStream`] for every
1098 /// transition in this machine.
1099 ///
1100 /// Override this in each `VerifiedStateMachine` implementation, listing the
1101 /// companion structs generated by [`formal_method`][crate::formal_method]:
1102 ///
1103 /// ```rust,ignore
1104 /// fn transition_harnesses() -> Vec<proc_macro2::TokenStream> {
1105 /// vec![
1106 /// MyTransitionATransition::kani_harness(),
1107 /// MyTransitionBTransition::kani_harness(),
1108 /// ]
1109 /// }
1110 /// ```
1111 ///
1112 /// The default implementation returns an empty list (no harnesses).
1113 #[cfg(not(kani))]
1114 fn transition_harnesses() -> Vec<proc_macro2::TokenStream> {
1115 vec![]
1116 }
1117
1118 /// Compose the full VSM Kani proof: the invariant proposition proof
1119 /// followed by all `proof_for_contract` closure harnesses for each
1120 /// registered transition.
1121 ///
1122 /// This is the token stream that a `build.rs` should write to a generated
1123 /// `.rs` file so that Kani can verify the entire state machine end-to-end.
1124 ///
1125 /// The composition says: "the invariant is a valid proposition AND every
1126 /// transition preserves it without panicking for any reachable input."
1127 #[cfg(not(kani))]
1128 fn vsm_kani_proof() -> proc_macro2::TokenStream {
1129 let mut ts = Self::Invariant::kani_proof();
1130 let inv_fn = Self::Invariant::kani_invariant_fn_name();
1131 for closure in Self::transition_kani_closure_proofs(inv_fn) {
1132 ts.extend(closure);
1133 }
1134 ts
1135 }
1136
1137 /// Return one `proof_for_contract` closure harness per transition in this machine.
1138 ///
1139 /// Each entry is a `#[kani::proof_for_contract(fn_name)]` harness using the
1140 /// forgive-and-forget pattern. Contracts on the original function (emitted by
1141 /// `#[formal_method]` via `cfg_attr(kani, kani::requires/ensures)`) are verified
1142 /// by DFCC. Once verified, each transition can be replaced with
1143 /// `stub_verified(fn_name)` in multi-step composition harnesses.
1144 ///
1145 /// When `inv_fn` is empty, all entries are empty `TokenStream`s.
1146 ///
1147 /// The default implementation returns an empty list.
1148 #[cfg(not(kani))]
1149 fn transition_kani_closure_proofs(_inv_fn: &str) -> Vec<proc_macro2::TokenStream> {
1150 vec![]
1151 }
1152
1153 /// Return one Creusot companion contract per transition in this machine.
1154 ///
1155 /// Each entry is a `#[cfg(creusot)]` function with `#[requires]`/`#[ensures]`
1156 /// annotations. When the invariant type's [`Prop::creusot_invariant_fn_name`]
1157 /// returns a non-empty string, the contracts are real (no `#[trusted]`).
1158 /// Otherwise they fall back to `#[trusted]` placeholders.
1159 ///
1160 /// The default implementation returns an empty list.
1161 #[cfg(not(kani))]
1162 fn transition_creusot_contracts(_inv_fn: &str) -> Vec<proc_macro2::TokenStream> {
1163 vec![]
1164 }
1165
1166 /// Return one Verus `assume_specification` block per transition in this machine.
1167 ///
1168 /// When `inv_fn` is non-empty, each entry constrains the transition with
1169 /// `requires inv_fn(&state)` / `ensures inv_fn(&r.0)`. When `inv_fn` is
1170 /// empty, trivially-true `requires true, ensures true` stubs are emitted.
1171 ///
1172 /// The default implementation returns an empty list.
1173 #[cfg(not(kani))]
1174 fn transition_verus_contracts(_inv_fn: &str) -> Vec<proc_macro2::TokenStream> {
1175 vec![]
1176 }
1177
1178 /// Return one `#[verifier::external]` stub function per transition.
1179 ///
1180 /// Used by `vsm_verus_standalone_proof()` to generate self-contained Verus
1181 /// source files where no external crate rlibs are available. Each stub
1182 /// declares the function with a `todo!()` body so that `assume_specification`
1183 /// has a concrete symbol to axiomatise.
1184 ///
1185 /// The default implementation returns an empty list.
1186 #[cfg(not(kani))]
1187 fn transition_verus_stubs() -> Vec<proc_macro2::TokenStream> {
1188 vec![]
1189 }
1190
1191 /// Compose the full VSM Creusot proof: the invariant proposition proof
1192 /// followed by one contract per registered transition.
1193 ///
1194 /// When `Self::Invariant::creusot_invariant_fn_name()` is non-empty, the
1195 /// generated companions use real `#[requires]`/`#[ensures]` annotations and
1196 /// Creusot will verify the function bodies. Otherwise trusted placeholders
1197 /// are emitted (same as the stub path).
1198 #[cfg(not(kani))]
1199 fn vsm_creusot_proof() -> proc_macro2::TokenStream {
1200 let mut ts = Self::Invariant::creusot_proof();
1201 let inv_fn = Self::Invariant::creusot_invariant_fn_name();
1202 for contract in Self::transition_creusot_contracts(inv_fn) {
1203 ts.extend(contract);
1204 }
1205 ts
1206 }
1207
1208 /// Compose the full VSM Verus proof: the invariant proposition proof
1209 /// followed by one `assume_specification` block per registered transition.
1210 ///
1211 /// When `Self::Invariant::verus_invariant_fn_name()` is non-empty, the
1212 /// generated companions emit real `requires`/`ensures` Verus contracts.
1213 /// Otherwise trivially-true stubs are emitted.
1214 #[cfg(not(kani))]
1215 fn vsm_verus_proof() -> proc_macro2::TokenStream {
1216 let mut ts = Self::Invariant::verus_proof();
1217 let inv_fn = Self::Invariant::verus_invariant_fn_name();
1218 for contract in Self::transition_verus_contracts(inv_fn) {
1219 ts.extend(contract);
1220 }
1221 ts
1222 }
1223
1224 /// Compose the transition portion of a self-contained Verus proof file:
1225 /// all `#[verifier::external]` stubs followed by all `assume_specification`
1226 /// contracts.
1227 ///
1228 /// Used by `strictly_verus/build.rs` to build files that do **not** import
1229 /// external crate rlibs (which the Verus toolchain cannot link). The
1230 /// caller is responsible for prepending the inline type definitions and
1231 /// the invariant spec function before this output.
1232 #[cfg(not(kani))]
1233 fn vsm_verus_transitions() -> proc_macro2::TokenStream {
1234 let mut ts = proc_macro2::TokenStream::new();
1235 for stub in Self::transition_verus_stubs() {
1236 ts.extend(stub);
1237 }
1238 let inv_fn = Self::Invariant::verus_invariant_fn_name();
1239 for contract in Self::transition_verus_contracts(inv_fn) {
1240 ts.extend(contract);
1241 }
1242 ts
1243 }
1244}
1245
1246/// Convenience alias: a verified transition for state machine `VSM`.
1247///
1248/// Any function (or closure) whose signature is
1249/// `(VSM::State, Established<VSM::Invariant>) -> (VSM::State, Established<VSM::Invariant>)`
1250/// satisfies this bound automatically via the [`FormalMethod`] blanket impl.
1251pub trait VerifiedTransition<VSM: VerifiedStateMachine>:
1252 FormalMethod<VSM::State, VSM::Invariant, VSM::State, VSM::Invariant>
1253{
1254}
1255
1256/// Blanket impl: any `FormalMethod` over the right state/invariant types is a
1257/// `VerifiedTransition` for the corresponding `VerifiedStateMachine`.
1258impl<VSM, T> VerifiedTransition<VSM> for T
1259where
1260 VSM: VerifiedStateMachine,
1261 T: FormalMethod<VSM::State, VSM::Invariant, VSM::State, VSM::Invariant>,
1262{
1263}
1264
1265/// Per-variant concrete construction expressions at each compositional depth.
1266///
1267/// Used by `#[derive(VerifiedStateMachine)]` to emit three harnesses per
1268/// `(transition × variant)`: one per depth. Each depth uses `KaniCompose`
1269/// field expressions from `#[derive(KaniVariantState)]`.
1270pub struct KaniVariantConstruction {
1271 /// Snake_case variant name suffix for harness function names
1272 /// (e.g. `"explain_view"` for `ExplainView`).
1273 pub variant_name: &'static str,
1274 /// Depth-0 construction expression: all collections empty / `None`.
1275 pub depth0: String,
1276 /// Depth-1 construction expression: one element in each collection.
1277 pub depth1: String,
1278 /// Depth-2 construction expression: two elements in each collection.
1279 pub depth2: String,
1280}
1281
1282/// Per-variant concrete construction expressions for VSM state enums.
1283///
1284/// Implemented via `#[derive(KaniVariantState)]`. Used by `derive_vsm` to
1285/// generate per-variant Kani harnesses — three harnesses per
1286/// `(transition × variant)`, one per compositional depth — so that CBMC
1287/// receives a concrete discriminant and bounded fields instead of a fully
1288/// symbolic enum.
1289///
1290/// # Motivation
1291///
1292/// `kani::any::<StateEnum>()` creates a tagged union where ALL variant fields
1293/// are globally symbolic in CBMC. Dropping such a value requires reasoning
1294/// about every variant destructor simultaneously, causing unbounded unwinding
1295/// for variants that contain `Vec<T>` or `String` (non-trivial destructors).
1296///
1297/// Per-variant harnesses give CBMC a concrete discriminant for each proof,
1298/// eliminating the symbolic-enum-drop problem while preserving exhaustive
1299/// coverage through case analysis. Per-depth harnesses extend this to
1300/// cover recursive / collection fields at sizes 0, 1, and 2.
1301pub trait KaniVariantState {
1302 /// Returns per-variant construction expressions at all three depths.
1303 ///
1304 /// Each [`KaniVariantConstruction`] provides:
1305 ///
1306 /// - `variant_name` — snake_case suffix for the harness function name.
1307 /// - `depth0` — all collections empty / `None` (base case).
1308 /// - `depth1` — one element in each collection (inductive step).
1309 /// - `depth2` — two elements in each collection (inductive step ×2).
1310 ///
1311 /// Field expressions use `<T as KaniCompose>::kani_depth{n}()` for
1312 /// non-primitive types, `Vec::new()` / `None` / `String::new()` for
1313 /// recognized collection types, and `kani::any()` for primitives.
1314 fn kani_variant_constructions() -> Vec<KaniVariantConstruction>;
1315}
1316
1317// ── Kani-safe label helpers ───────────────────────────────────────────────────
1318
1319/// Produce a label string for display / diagnostics in formal-method transitions.
1320///
1321/// In normal builds, expands to `format!($args*)` as written. In Kani builds,
1322/// returns `String::new()` instead so CBMC does not model the entire Rust
1323/// formatter machinery — `format!()` involves trait-object dispatch through
1324/// `std::fmt` that multiplies CBMC paths even for fully concrete inputs.
1325///
1326/// # Usage
1327///
1328/// Replace every `format!("...")` that produces a *display label* (content
1329/// irrelevant to invariant correctness) in a `#[formal_method]` transition
1330/// body with `kani_label!("...")`:
1331///
1332/// ```rust,ignore
1333/// let label_left = kani_label!("{old_schema}.{old_table}");
1334/// ```
1335#[macro_export]
1336macro_rules! kani_label {
1337 ($($arg:tt)*) => {{
1338 #[cfg(any(kani, creusot))]
1339 let _s = ::std::string::String::new();
1340 #[cfg(not(any(kani, creusot)))]
1341 let _s = format!($($arg)*);
1342 _s
1343 }};
1344}