1
0
forked from Alepha/Alepha

Add the attestation work.

This commit is contained in:
2023-02-09 21:10:08 -08:00
parent 4affb80931
commit 306d2145a3
4 changed files with 547 additions and 0 deletions

214
Proof/Attestation.h Normal file
View File

@ -0,0 +1,214 @@
/*!
* @file
* @brief The `Attestation` framework permits code which can provide limited compiletime guarantees of conditions.
*/
#pragma once
#include <cassert>
#include <cstdlib>
#include <utility>
#include <type_traits>
namespace Alepha::Proof
{
template< typename Attestation >
Attestation
impute()
{
return Attestation{};
}
template< typename Permission >
auto
attest( Permission )
{
return impute< typename Permission::attestation_type >();
}
#if 0
namespace attestation_detail
{
template< typename ... Args > struct typelist {};
template< typename T > struct make_typelist;
template< typename ... Args >
struct make_typelist< std::tuple< Args... > >
{
using type= typelist< Args... >;
};
template< typename Policy, typename = void >
struct conjugates
{
using type= typelist< Policy >;
};
template< typename Policy >
struct conjugates< Policy, std::void_t< decltype( typename Policy::conjunction_type{} ) > >
{
using type= typename make_typelist< typename Policy::conjunction_type >::type;
};
template< typename value, typename Conjugates >
struct in_set : std::false_type {};
template< typename value >
struct in_set< value, typelist< value > > : std::true_type {};
template< typename value, typename ... Conjugates >
struct in_set< value, typelist< value, Conjugates... > > : std::true_type {};
template< typename value, typename X, typename ... Conjugates >
struct in_set< value, typelist< X, Conjugates... > > : in_set< value, Conjugates... > {};
template< typename value, typename X >
struct in_set< value, typelist< X > > : std::false_type {};
template< typename A, typename B >
struct is_subset_of : std::false_type {};
template< typename T, typename ... Left, typename ... Right >
struct is_subset_of< typelist< T, Left... >, typelist< Right... > >
: std::integral_constant< bool, in_set< T, Right... >::value
&& is_subset_of< Left..., Right... > > {};
template< typename ... Right >
struct is_subset_of< typelist<>, typelist< Right... > >
: std::true_type {};
template< typename ... Left >
struct is_subset_of< typelist< Left... >, typelist<> >
: std::false_type {};
template<>
struct is_subset_of< typelist<>, typelist<> > : std::true_type {};
template< typename ... lists >
struct set_union;
template< typename ... elements >
struct set_union< typelist< elements... > > : typelist< elements... > {};
template< typename ... elements >
struct set_union< typelist< elements... > > : typelist< elements... > {};
}
#endif
/*!
* @brief An Attestation is a representation of some particular fact about runtime code known at compiletime.
*
* An Attestation is a copyable, zero-storage type which represents at compiletime a fact about runtime code.
* Attestations can be used as compiletime predicates for a function by making that function take an attestation
* as an argument. Every attestation is created only by the limited set of classes as indicated by the `averant`
* member of the `Policy` for an Attestation. These policies are also tag types which distinguish the kind of
* Attestation.
*
* Attestations can be copied freely, as any fact once true is considered to continue being true. Attestations
* should not be held over the long term -- they are best used as temporaries, such as ephemeral arguments to
* a function requiring that fact to be true.
*/
template< typename Policy >
class Attestation
{
private:
struct permission_t { using attestation_type= Attestation; };
static inline permission_t permission;
Attestation()= default;
template< typename A > friend A impute();
//template< typename A, typename P > friend A attest( P );
friend typename Policy::averant;
//using conjugates= typename attestation_detail::conjugates< Policy >::type;
template< typename > friend class Attestation;
public:
template< typename > class Witness;
#if 0
template< typename ... Policies >
Attestation( Attestation< Policies >... )
{
static_assert( attestation_detail::is_subset_of< conjugates,
typename attestation_detail::set_union< Policies >::type >::value,
"Cannot make an attestation without sufficient proof of existance for "
"required facts." );
}
template< typename ForeignPolicy >
Attestation( Attestation< ForeignPolicy > )
//: Attestation( attestation_detail::expand< ForeignPolicy >::
{
static_assert( attestation_detail::is_subset_of< conjugates,
typelist< Attestations... > >::value, "Cannot make an attestation without "
"sufficient proof of existance for required facts." );
}
#endif
template< typename T >
auto
aver( const T &r )
{
return Witness< const T & >( r );
}
template< typename T >
auto
averCopy( T r )
{
return Witness< T >( std::move( r ) );
}
};
/*!
* @brief A witness knows a fact (Attestation) to be true about some particular value.
*
* In the Attestation framework, a Witness is an attestation type which binds a particular fact (a regular Attestation) to the
* value for which that fact is true. For example, one can create a `Witness` to the fact that a particular `std::vector` is
* sorted. This is extremely useful as it cuts down on the need to create specialized "constrained" types for specific use cases.
*
* Witnesses are not actually a proxy for the value for which the fact is true. A witness must `testify` as to which value the
* fact is actually true for. The `testify` function returns the actual value for which the `Attestation` is known to hold.
*/
template< typename Policy >
template< typename T >
class Attestation< Policy >::Witness
{
private:
T value;
friend Attestation< Policy >;
explicit Witness( T i_v ) : value( std::move( i_v ) ) {}
public:
inline friend T &testify( Witness &w ) noexcept { return w.value; }
inline friend T const &testify( const Witness &w ) noexcept { return w.value; }
inline friend Attestation< Policy > fact( Witness w ) noexcept
{ return Attestation< Policy >{}; }
};
template< typename Policy >
template< typename T >
class Attestation< Policy >::Witness< T & >
{
private:
T *p;
friend Attestation< Policy >;
explicit Witness( T &r ) : p( &r ) {}
public:
friend T &testify( Witness w ) noexcept { return *w.p; }
friend Attestation< Policy > fact( Witness w ) noexcept { return Attestation< Policy >{}; }
};
}

12
Proof/Makefile Normal file
View File

@ -0,0 +1,12 @@
#CXX=clang++
CXX=g++
CXXFLAGS+= -std=c++1z
#CXXFLAGS+= -stdlib=libstdc++
CXXFLAGS+= -O3
all: simple test
test.cc: Attestation.h
simple.cc: Attestation.h
clean:
$(RM) *.o test

134
Proof/simple.cc Normal file
View File

@ -0,0 +1,134 @@
#include "Attestation.h"
#include <iostream>
#include <iomanip>
#include <iterator>
#include <vector>
#include <algorithm>
#include <mutex>
#include <exception>
// Ignore the headers, for the moment, just assume that I have included what I need.
namespace
{
// An initial way you might do things...
class Thing0
{
private:
std::mutex mtx;
void helper_needs_lock() {}
public:
void
function()
{
std::lock_guard< std::mutex > lock( mtx );
helper_needs_lock();
}
};
// A better way you might do it.
class Thing1
{
private:
std::mutex mtx;
void helper( const std::lock_guard< std::mutex > & ) {}
public:
void
function()
{
std::lock_guard< std::mutex > lock( mtx );
helper( lock );
}
};
// A more adaptable way you might do it.
class WithLock
{
public:
WithLock( const std::lock_guard< std::mutex > & ) {}
WithLock( const std::unique_lock< std::mutex > &lock ) { assert( lock.owns_lock() ); }
};
class Thing2
{
private:
std::mutex mtx;
void helper( WithLock ) {}
public:
void
function()
{
std::lock_guard< std::mutex > lock( mtx );
helper( lock );
}
};
// How might we generalize this?
struct Sorter;
struct sorted_tag { using averant= Sorter; };
using Sorted= Alepha::Proof::Attestation< sorted_tag >;
struct Sorter
{
static Sorted::Witness< const std::vector< int > & >
sort( std::vector< int > &v )
{
std::sort( begin( v ), end( v ) );
return attest( Sorted::permission ).aver< const std::vector< int > & >( v );
}
static Sorted::Witness< const std::vector< int > & >
check( std::vector< int > &v )
{
if( !std::is_sorted( begin( v ), end( v ) ) ) throw std::runtime_error( "" );
return attest( Sorted::permission ).aver< const std::vector< int > & >( v );
}
};
}
namespace // Other guy
{
bool
binarySearch( Sorted::Witness< const std::vector< int > & > v, const int data )
{
return std::binary_search( begin( testify( v ) ), end( testify( v ) ), data );
}
}
int
main()
{
auto data= std::vector< int >( std::istream_iterator< int >( std::cin ),
std::istream_iterator< int >() );
auto witness= Sorter::check( data );
std::cout << std::boolalpha << binarySearch( witness, 42 ) << std::endl;
}

187
Proof/test.cc Normal file
View File

@ -0,0 +1,187 @@
#include "Attestation.h"
#include <cstddef>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <mutex>
#include <memory>
#include <random>
namespace
{
struct Tester;
struct foobar_tag { using averant= Tester; };
using FoobarFact= Alepha::Proof::Attestation< foobar_tag >;
struct Tester
{
static void
test()
{
FoobarFact token= Alepha::Proof::impute< FoobarFact >();
FoobarFact token2= attest( FoobarFact::permission );
}
};
struct Sorter;
struct sorted_tag { using averant= Sorter; };
using Sorted= Alepha::Proof::Attestation< sorted_tag >;
struct Sorter
{
static Sorted::Witness< const std::vector< int > & >
sort( std::vector< int > &v )
{
std::sort( begin( v ), end( v ) );
return attest( Sorted::permission ).aver< const std::vector< int > & >( v );
}
};
struct Locker;
struct with_lock_tag { using averant= Locker; };
using Locked= Alepha::Proof::Attestation< with_lock_tag >;
struct Locker
{
struct WithLock : Locked
{
public:
WithLock( const std::lock_guard< std::mutex > & )
: Locked( attest( Locked::permission ) ) {}
WithLock( std::lock_guard< std::mutex > && )= delete;
};
};
using WithLock= Locker::WithLock;
struct NullChecker;
struct non_null_tag { using averant= NullChecker; };
using NonNull= Alepha::Proof::Attestation< non_null_tag >;
struct NullChecker
{
template< typename Ptype >
static NonNull::Witness< Ptype >
isNotNull( Ptype p )
{
if( !p ) throw std::logic_error( "" );
return attest( NonNull::permission ).averCopy( std::move( p ) );
}
};
template< typename P >
auto
makeUnique()
{
return NullChecker::isNotNull( std::make_unique< P >() );
}
// Page aligned example
struct page_aligned_tag { using averant= struct AlignmentChecker; };
using PageAligned= Alepha::Proof::Attestation< page_aligned_tag >;
struct AlignmentChecker
{
template< typename Ptype >
static auto
check( Ptype *pointer )
{
if( pointer % 4096 ) throw std::runtime_error( "" );
return attest( PageAligned::permission ).template averCopy< Ptype *const >( pointer );
}
static PageAligned::Witness< void *const >
allocate( const std::size_t amt )
{
void *rv;
const int ec= posix_memalign( &rv, 4096, amt );
if( ec ) std::get_new_handler()();
return attest( PageAligned::permission ).averCopy< void *const >( rv );
}
};
template< typename T >
PageAligned::Witness< std::unique_ptr< T > >
make_unique_on_page()
{
// Dtor ordering and transition of responsibility struct
struct Safety
{
std::unique_ptr< void > memory;
explicit Safety( void *p ) : memory( p ) { new (p) T(); }
~Safety()= delete;
std::unique_ptr< T >
finish () & noexcept
{
return std::unique_ptr< T >( reinterpret_cast< T * >( memory.release() ) );
}
};
auto safe_adapt= []( auto p )
{
std::array< std::uint8_t, sizeof( Safety ) > locals;
auto safe= (new (locals.data()) Safety( witness( p ) ))->finish();
return std::make_pair( std::move( safe ), fact( p ) );
};
auto [ ptr, alignment ]= safe_adapt( AlignmentChecker::allocate( sizeof( T ) ) );
return alignment.template averCopy< std::unique_ptr< T > >( std::move( ptr ) );
}
}
int
main()
{
Tester::test();
//attest( FoobarFact::permission );
FoobarFact token= Alepha::Proof::impute< FoobarFact >();
std::vector< int > v{ 5, 11, 3, 2, 1 };
Sorted::Witness< const std::vector< int > & > sortedVector= Sorter::sort( v );
assert( std::is_sorted( begin( testify( sortedVector ) ), end( testify( sortedVector ) ) ) );
std::shuffle( begin( v ), end( v ), std::random_device() );
assert( !std::is_sorted( begin( testify( sortedVector ) ), end( testify( sortedVector ) ) ) );
std::mutex mtx;
auto checker= []( WithLock ) {};
std::lock_guard< std::mutex > lk( mtx );
checker( lk );
auto myInt= makeUnique< int >();
return EXIT_SUCCESS;
}
namespace
{
namespace mongo
{
struct OperationContext { int member; };
void
operation( NonNull::Witness< OperationContext * > opCtx )
{
// ...
(void) testify( opCtx )->member;
}
}
}