use proc_macro2;
use std::marker::PhantomData;
pub trait Prop: 'static {
fn kani_proof() -> proc_macro2::TokenStream;
fn verus_proof() -> proc_macro2::TokenStream;
fn creusot_proof() -> proc_macro2::TokenStream;
}
pub struct Established<P: Prop> {
_marker: PhantomData<fn() -> P>,
}
impl<P: Prop> Established<P> {
#[inline(always)]
pub fn assert() -> Self {
Self {
_marker: PhantomData,
}
}
#[inline(always)]
pub fn weaken<Q: Prop>(self) -> Established<Q>
where
P: Implies<Q>,
{
Established {
_marker: PhantomData,
}
}
}
impl<P: Prop> Copy for Established<P> {}
impl<P: Prop> Clone for Established<P> {
fn clone(&self) -> Self {
*self
}
}
impl<P: Prop> std::fmt::Debug for Established<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Established")
}
}
impl<P: Prop + crate::Elicitation> crate::Prompt for Established<P> {
fn prompt() -> Option<&'static str> {
P::prompt()
}
}
impl<P: Prop + crate::Elicitation> crate::Elicitation for Established<P> {
type Style = <P as crate::Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<C: crate::ElicitCommunicator>(communicator: &C) -> crate::ElicitResult<Self> {
tracing::debug!("Eliciting proof proposition for Established");
let _p = P::elicit(communicator).await?;
Ok(Established::assert())
}
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl<P: Prop + 'static> crate::emit_code::ToCodeLiteral for Established<P> {
fn type_tokens() -> proc_macro2::TokenStream {
let p: proc_macro2::TokenStream = std::any::type_name::<P>()
.parse()
.unwrap_or_else(|_| quote::quote! { _ });
quote::quote! { ::elicitation::Established<#p> }
}
fn to_code_literal(&self) -> proc_macro2::TokenStream {
let ty = <Self as crate::emit_code::ToCodeLiteral>::type_tokens();
quote::quote! { <#ty>::assert() }
}
}
#[cfg(feature = "prompt-tree")]
impl<P: Prop> crate::ElicitPromptTree for Established<P> {
fn prompt_tree() -> crate::PromptTree {
crate::PromptTree::Leaf {
prompt: String::new(),
type_name: "Established".to_string(),
}
}
}
#[cfg(feature = "serde")]
impl<P: Prop> serde::Serialize for Established<P> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_unit()
}
}
#[cfg(feature = "serde")]
impl<'de, P: Prop> serde::Deserialize<'de> for Established<P> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
<()>::deserialize(deserializer)?;
Ok(Self::assert())
}
}
impl<P: Prop + 'static> schemars::JsonSchema for Established<P> {
fn schema_name() -> std::borrow::Cow<'static, str> {
"EstablishedProof".into()
}
fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({ "type": "null" })
}
}
pub struct Is<T> {
_marker: PhantomData<fn() -> T>,
}
impl<T: 'static> Prop for Is<T> {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
pub trait Implies<Q: Prop>: Prop {}
impl<P: Prop> Implies<P> for P {}
type AndMarker<P, Q> = (fn() -> P, fn() -> Q);
pub struct And<P: Prop, Q: Prop> {
_marker: PhantomData<AndMarker<P, Q>>,
}
impl<P: Prop, Q: Prop> Prop for And<P, Q> {
fn kani_proof() -> proc_macro2::TokenStream {
let mut ts = P::kani_proof();
ts.extend(Q::kani_proof());
ts
}
fn verus_proof() -> proc_macro2::TokenStream {
let mut ts = P::verus_proof();
ts.extend(Q::verus_proof());
ts
}
fn creusot_proof() -> proc_macro2::TokenStream {
let mut ts = P::creusot_proof();
ts.extend(Q::creusot_proof());
ts
}
}
#[inline(always)]
pub fn both<P: Prop, Q: Prop>(_p: Established<P>, _q: Established<Q>) -> Established<And<P, Q>> {
Established {
_marker: PhantomData,
}
}
#[inline(always)]
pub fn fst<P: Prop, Q: Prop>(_both: Established<And<P, Q>>) -> Established<P> {
Established {
_marker: PhantomData,
}
}
#[inline(always)]
pub fn snd<P: Prop, Q: Prop>(_both: Established<And<P, Q>>) -> Established<Q> {
Established {
_marker: PhantomData,
}
}
pub trait Refines<Base>: 'static {}
impl<T: 'static> Refines<T> for T {}
#[inline(always)]
pub fn downcast<Base: 'static, Refined: Refines<Base>>(
proof: Established<Is<Refined>>,
) -> Established<Is<Base>>
where
Is<Refined>: Implies<Is<Base>>,
{
proof.weaken()
}
type InVariantMarker<E, V> = (fn() -> E, fn() -> V);
pub struct InVariant<E, V> {
_marker: PhantomData<InVariantMarker<E, V>>,
}
impl<E: 'static, V: 'static> Prop for InVariant<E, V> {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_established_is_zero_sized() {
let proof: Established<Is<String>> = Established::assert();
assert_eq!(std::mem::size_of_val(&proof), 0);
}
#[test]
fn test_established_is_copy() {
let proof: Established<Is<String>> = Established::assert();
let proof2 = proof; let _proof3 = proof; let _proof4 = proof2; }
#[test]
fn test_can_construct_proof() {
let _proof: Established<Is<String>> = Established::assert();
let _proof: Established<Is<i32>> = Established::assert();
let _proof: Established<Is<Vec<u8>>> = Established::assert();
}
#[test]
fn test_proof_requires_type() {
fn requires_string_proof(_proof: Established<Is<String>>) {}
let proof: Established<Is<String>> = Established::assert();
requires_string_proof(proof);
}
#[test]
fn test_implies_reflexive() {
let proof: Established<Is<String>> = Established::assert();
let same_proof: Established<Is<String>> = proof.weaken();
let _ = same_proof; }
#[test]
fn test_weaken_with_custom_impl() {
struct StrongProp;
struct WeakProp;
impl Prop for StrongProp {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for WeakProp {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Implies<WeakProp> for StrongProp {}
let strong: Established<StrongProp> = Established::assert();
let _weak: Established<WeakProp> = strong.weaken();
}
#[test]
fn test_cannot_weaken_without_impl() {
struct _PropA;
struct _PropB;
impl Prop for _PropA {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for _PropB {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
}
#[test]
fn test_conjunction_combine() {
struct P;
struct Q;
impl Prop for P {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for Q {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
let p: Established<P> = Established::assert();
let q: Established<Q> = Established::assert();
let _pq: Established<And<P, Q>> = both(p, q);
}
#[test]
fn test_conjunction_project_left() {
struct P;
struct Q;
impl Prop for P {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for Q {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
let _p: Established<P> = fst(pq);
}
#[test]
fn test_conjunction_project_right() {
struct P;
struct Q;
impl Prop for P {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for Q {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
let _q: Established<Q> = snd(pq);
}
#[test]
fn test_conjunction_implies_components() {
struct P;
struct Q;
impl Prop for P {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for Q {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
let _p: Established<P> = fst(pq);
let _q: Established<Q> = snd(pq);
}
#[test]
fn test_conjunction_is_zero_sized() {
struct P;
struct Q;
impl Prop for P {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for Q {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
let pq: Established<And<P, Q>> = both(Established::assert(), Established::assert());
assert_eq!(std::mem::size_of_val(&pq), 0);
}
#[test]
fn test_conjunction_chain() {
struct P;
struct Q;
struct R;
impl Prop for P {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for Q {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
impl Prop for R {
fn kani_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn verus_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
fn creusot_proof() -> proc_macro2::TokenStream {
proc_macro2::TokenStream::new()
}
}
let p: Established<P> = Established::assert();
let q: Established<Q> = Established::assert();
let r: Established<R> = Established::assert();
let pq = both(p, q);
let _pqr: Established<And<And<P, Q>, R>> = both(pq, r);
}
#[test]
fn test_refinement_downcast() {
use core::marker::PhantomData;
struct _NonEmptyString(PhantomData<String>);
impl Refines<String> for _NonEmptyString {}
impl Implies<Is<String>> for Is<_NonEmptyString> {}
let refined_proof: Established<Is<_NonEmptyString>> = Established::assert();
let _base_proof: Established<Is<String>> = downcast(refined_proof);
}
#[test]
fn test_refinement_via_weaken() {
use core::marker::PhantomData;
struct _NonEmptyString(PhantomData<String>);
impl Refines<String> for _NonEmptyString {}
impl Implies<Is<String>> for Is<_NonEmptyString> {}
let refined: Established<Is<_NonEmptyString>> = Established::assert();
let _base: Established<Is<String>> = refined.weaken();
}
#[test]
fn test_refinement_reflexive() {
let proof: Established<Is<String>> = Established::assert();
let _same: Established<Is<String>> = downcast(proof);
}
#[test]
fn test_refinement_chain() {
use core::marker::PhantomData;
struct _HttpsUrl(PhantomData<String>);
struct _ValidUrl(PhantomData<String>);
impl Refines<String> for _ValidUrl {}
impl Implies<Is<String>> for Is<_ValidUrl> {}
impl Refines<_ValidUrl> for _HttpsUrl {}
impl Implies<Is<_ValidUrl>> for Is<_HttpsUrl> {}
impl Refines<String> for _HttpsUrl {} impl Implies<Is<String>> for Is<_HttpsUrl> {}
let https: Established<Is<_HttpsUrl>> = Established::assert();
let valid: Established<Is<_ValidUrl>> = downcast(https);
let _base: Established<Is<String>> = downcast(valid);
}
#[test]
fn test_refinement_direct_chain() {
use core::marker::PhantomData;
struct _HttpsUrl(PhantomData<String>);
struct _ValidUrl(PhantomData<String>);
impl Refines<String> for _ValidUrl {}
impl Implies<Is<String>> for Is<_ValidUrl> {}
impl Refines<_ValidUrl> for _HttpsUrl {}
impl Implies<Is<_ValidUrl>> for Is<_HttpsUrl> {}
impl Refines<String> for _HttpsUrl {}
impl Implies<Is<String>> for Is<_HttpsUrl> {}
let https: Established<Is<_HttpsUrl>> = Established::assert();
let _base: Established<Is<String>> = downcast(https);
}
#[test]
fn test_refinement_zero_sized() {
use core::marker::PhantomData;
struct _NonEmptyString(PhantomData<String>);
impl Refines<String> for _NonEmptyString {}
impl Implies<Is<String>> for Is<_NonEmptyString> {}
let refined: Established<Is<_NonEmptyString>> = Established::assert();
assert_eq!(std::mem::size_of_val(&refined), 0);
let base: Established<Is<String>> = downcast(refined);
assert_eq!(std::mem::size_of_val(&base), 0);
}
#[test]
fn test_cannot_upcast() {
use core::marker::PhantomData;
struct _NonEmptyString(PhantomData<String>);
impl Refines<String> for _NonEmptyString {}
impl Implies<Is<String>> for Is<_NonEmptyString> {}
}
#[test]
fn test_invariant_zero_sized() {
enum _Status {
_Active,
_Inactive,
}
struct _ActiveVariant;
let proof: Established<InVariant<_Status, _ActiveVariant>> = Established::assert();
assert_eq!(std::mem::size_of_val(&proof), 0);
}
#[test]
fn test_invariant_type_safety() {
enum _Status {
_Active,
_Inactive,
}
struct _ActiveVariant;
struct _InactiveVariant;
fn process_active(
_status: _Status,
_proof: Established<InVariant<_Status, _ActiveVariant>>,
) {
}
let proof: Established<InVariant<_Status, _ActiveVariant>> = Established::assert();
process_active(_Status::_Active, proof);
}
#[test]
fn test_invariant_enum_branches() {
enum _State {
_Loading,
_Ready,
_Error,
}
struct _LoadingVariant;
struct _ReadyVariant;
struct _ErrorVariant;
fn handle_loading(_proof: Established<InVariant<_State, _LoadingVariant>>) {
}
fn handle_ready(_proof: Established<InVariant<_State, _ReadyVariant>>) {
}
fn handle_error(_proof: Established<InVariant<_State, _ErrorVariant>>) {
}
let loading_proof: Established<InVariant<_State, _LoadingVariant>> = Established::assert();
handle_loading(loading_proof);
let ready_proof: Established<InVariant<_State, _ReadyVariant>> = Established::assert();
handle_ready(ready_proof);
let error_proof: Established<InVariant<_State, _ErrorVariant>> = Established::assert();
handle_error(error_proof);
}
#[test]
fn test_invariant_with_inhabitation() {
enum _Color {
_Red,
_Green,
_Blue,
}
struct _RedVariant;
let _type_proof: Established<Is<_Color>> = Established::assert();
let _variant_proof: Established<InVariant<_Color, _RedVariant>> = Established::assert();
}
}