Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: |
|
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
c3f897359f35531163f9b28cd9bd735f |
| User & Date: | achavasse 2020-06-20 14:32:02.342 |
Context
|
2020-06-20
| ||
| 16:07 | Fixed one of the tuple unification rule. check-in: 48bb81ebdd user: achavasse tags: trunk | |
| 14:32 |
| |
|
2020-06-15
| ||
| 22:09 | Refactored the integer/ct_int unification to defer the size/sign check to a postprocessing callback. check-in: ac4e681af3 user: achavasse tags: trunk | |
Changes
Changes to .fossil-settings/ignore-glob.
1 2 3 4 5 6 7 8 | file-* ci-comment-* *-baseline *-merge *-original build subprojects/*/* tools/vscode-extension/vscode-extension | > | 1 2 3 4 5 6 7 8 9 | file-* ci-comment-* *-baseline *-merge *-original build subprojects/*/* tools/vscode-extension/vscode-extension builddir |
Changes to bs/builtins/types/constrainedfunc/invoke.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class ConstrainedFuncInvocationRule : public InvocationRule
{
public:
| | | > > > | | < | > > | | | | | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class ConstrainedFuncInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto callPat = VEC( c.domain(), args, HOLE( "_"_sid ) );
// Unify with the constraint pattern. We only care that there is at least one
// solution, and if so, we defer the actual call resolution to the contained
// function.
auto cfunc = FromValue< ConstrainedFunc >( callee );
assert( cfunc );
UnificationContext uc( c );
uc.setComplexity( GetComplexity( cfunc->constraintPat() ) + GetComplexity( callPat ) );
for( auto&& [s, uc] : Unify( cfunc->constraintPat(), callPat, uc ) )
{
if( Postprocess( s, uc ) )
return cfunc->invRule()->resolveInvocation( c, loc, cfunc->func(), args );
}
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
};
void SetupConstrainedFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToIRExpr( Value(
|
| ︙ | ︙ |
Changes to bs/builtins/types/constrainedfunc/unify.cpp.
| ︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 |
auto it = g.begin();
if( it != g.end() )
{
auto func = ValueToIRExpr( cfunc->func() );
co_yield Unify( lhs, func, uc );
}
} );
}
}
| > > > > > > > > > > > > > > > > > > > > > > > > > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
auto it = g.begin();
if( it != g.end() )
{
auto func = ValueToIRExpr( cfunc->func() );
co_yield Unify( lhs, func, uc );
}
} );
// constrainedfunc param / any arg:
// Just yield the constrained func.
//
// This is because when we monomorphize a function that takes
// a polymorphic function type, we turn the later into a
// compile time constant. So it isn't really a parameter
// in the resulting monomorphic function, and we just want
// to ignore whatever parameter is being passed there
e.unificationRuleSet()->addSymRule( URINFOS,
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< builtins::ConstrainedFunc >(),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
{
co_yield { lhs, uc };
} );
}
}
|
Changes to bs/builtins/types/func/invoke.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class FunctionInvocationRule : public InvocationRule
{
public:
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class FunctionInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
optional< UnificationContext > bestUC;
optional< Term > bestSol;
bool ambiguous = false;
auto sig = GetFuncSig( callee );
auto callPat = VEC( c.domain(), args, HOLE( "_"_sid ) );
auto us = FindBestUnification( sig, callPat, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
|
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,uc] = get< UniSol >( us );
| | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,uc] = get< UniSol >( us );
return invoke( c, loc, callee, args, s, uc );
}
Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, UnificationContext& uc ) const final
{
auto newCallee = prepareFunc( c, 0, callee, unifiedCallPat, uc );
if( newCallee.isPoison() )
return PoisonValue();
auto callDecomp = Decompose( unifiedCallPat,
Vec(
|
| ︙ | ︙ |
Changes to bs/builtins/types/localvar/invoke.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto val = BuildComputedValue( lv.type(),
llr::Load( llr::VarAddress( lv.index() ), lv.type() ) );
return sema::CanBeInvoked( c, val );
}
| | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
auto val = BuildComputedValue( lv.type(),
llr::Load( llr::VarAddress( lv.index() ), lv.type() ) );
return sema::CanBeInvoked( c, val );
}
Value resolveInvocation( const Context& c, uint32_t locationId, const Value& callee, const Term& args ) const final
{
auto lv = *FromValue< LocalVar >( callee );
auto val = BuildComputedValue( lv.type(),
llr::Load( llr::VarAddress( lv.index() ), lv.type() ) );
return sema::GetInvocationRule( *c.env(), val )->resolveInvocation( c, locationId, val, args );
|
| ︙ | ︙ |
Changes to bs/builtins/types/overloadset/invoke.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class OverloadSetInvocationRule : public InvocationRule
{
public:
| | > > | > > > | | > > > > > > > > < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class OverloadSetInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );
optional< UnificationContext > bestUC;
optional< Term > bestSol;
const OverloadSet::Overload* bestOvl = nullptr;
bool ambiguous = false;
auto rtPat = HOLE( "_"_sid );
UnificationContext uc( c );
for( auto&& [s,ovl,uc] : pOvlSet->unify( c.domain(), args, rtPat, uc ) )
{
if( uc.numUnknownValues() )
continue;
if( bestUC && uc.score() < bestUC->score() )
continue;
auto pps = Postprocess( s, uc );
if( !pps )
continue;
if( bestUC && uc.score() == bestUC->score() )
{
ambiguous = true;
continue;
}
bestUC = uc;
bestSol = move( *pps );
bestOvl = &ovl;
ambiguous = false;
}
if( ambiguous )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
if( !bestSol )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
return bestOvl->pInvRule->invoke( c, loc, *bestOvl->callee, args, *bestSol, *bestUC );
}
};
ptr< InvocationRule >& GetOverloadSetInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< OverloadSetInvocationRule >();
return pRule;
|
| ︙ | ︙ |
Changes to bs/builtins/types/template/invoke.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::sema;
using namespace goose::parse;
namespace goose::builtins
{
class TemplateFunctionInvocationRule : public InvocationRule
{
public:
| | | | | < < | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::sema;
using namespace goose::parse;
namespace goose::builtins
{
class TemplateFunctionInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto tf = FromValue< TFunc >( callee );
assert( tf );
optional< UnificationContext > bestUC;
optional< Term > bestSol;
bool ambiguous = false;
auto callPat = VEC( c.domain(), args, HOLE( "_"_sid ) );
auto us = FindBestUnification( tf->signature(), callPat, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"template function arguments mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,uc] = get< UniSol >( us );
return invoke( c, loc, callee, args, s, uc );
}
Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, UnificationContext& uc ) const final
{
auto callDecomp = Decompose( unifiedCallPat,
Vec(
SubTerm(), // domain
SubTerm(), // args
SubTerm() // return type
)
);
auto&& [domain, unifiedArgs, unifiedRType] = *callDecomp;
DiagnosticsContext dc( loc, loc ? "Called here." : "", false );
auto instanceFunc = InstantiateTFunc( c, callee, unifiedCallPat, uc );
if( instanceFunc.isPoison() )
return PoisonValue();
return GetFuncInvocationRule()->resolveInvocation( c, loc, instanceFunc, args );
}
optional< Term > getSignature( const Value& callee ) const final
{
auto tfuncVal = FromValue< TFunc >( callee );
assert( tfuncVal );
return tfuncVal->signature();
|
| ︙ | ︙ |
Changes to bs/builtins/types/tuple/init.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::llr;
namespace goose::builtins
{
void SetupTupleInitialize( Env& e )
{
| < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::llr;
namespace goose::builtins
{
void SetupTupleInitialize( Env& e )
{
// Tuples default initialization: attempt to default initialize every element.
RegisterBuiltinFunc< Intrinsic< void (
CustomPattern< Reference, Reference::PatternMutable< TuplePattern > >
) > >( e, e.extInitialize(),
[]( const Context& c, const Value& tupRef )
{
auto ref = *FromValue< Reference >( tupRef );
|
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
addr.appendToPath( index++ );
auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
Load( move( addr ), rt.type() ) )
.setLocationId( elemType.locationId() );
DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
| | > > > > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
addr.appendToPath( index++ );
auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
Load( move( addr ), rt.type() ) )
.setLocationId( elemType.locationId() );
DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeTuple( elemRef ) );
DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeTuple( init ) );
return true;
} );
} );
// Tuples initialization: intialize each element with the corresponding element
|
| ︙ | ︙ | |||
69 70 71 72 73 74 75 |
{
auto elemType = *ValueFromIRExpr( t );
auto initType = *ValueFromIRExpr( i );
// Create a mutable reference to the element to initialize
ReferenceType rt( t, TSID( mut ) );
auto addr = lref.address();
| | | > > > > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
{
auto elemType = *ValueFromIRExpr( t );
auto initType = *ValueFromIRExpr( i );
// Create a mutable reference to the element to initialize
ReferenceType rt( t, TSID( mut ) );
auto addr = lref.address();
addr.appendToPath( index );
auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
Load( move( addr ), rt.type() ) )
.setLocationId( elemType.locationId() );
// Create a reference to the element to initialize, that have the same behavior as the original
// reference to the initializer tuple.
ReferenceType irt( i, rref.type().behavior() );
auto initAddr = rref.address();
initAddr.appendToPath( index++ );
auto initRef = BuildComputedValue( ValueToIRExpr( ToValue( irt ) ),
Load( move( initAddr ), irt.type() ) )
.setLocationId( initType.locationId() );
DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeTuple( elemRef, initRef ) );
DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeTuple( init ) );
return true;
} );
} );
}
}
|
Changes to bs/execute/vm.cpp.
| ︙ | ︙ | |||
155 156 157 158 159 160 161 |
return PoisonValue();
return ValueFromIRExpr( **pTerm );
}
optional< Value > VM::execute( const llr::AllocVar& av )
{
| | | | < | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
return PoisonValue();
return ValueFromIRExpr( **pTerm );
}
optional< Value > VM::execute( const llr::AllocVar& av )
{
m_frame.setTemporary( av.index(), BuildUninitializedValue( av.type() ) );
return nullopt;
}
optional< Value > VM::execute( const llr::Load& l )
{
const auto* addr = calcAddress( l.addr() );
if( !addr )
return nullopt;
return ValueFromIRExpr( *addr );
}
optional< Value > VM::execute( const llr::Store& s )
{
auto* addr = calcAddress( s.addr() );
if( !addr )
return nullopt;
auto result = Evaluate( s.val(), *this );
if( !result.isConstant() )
return PoisonValue();
*addr = ValueToIRExpr( result );
return nullopt;
}
optional< Value > VM::execute( const llr::Phi& p )
{
p.forAllIncomings( [&]( auto&& bb, auto&& val )
{
if( bb == m_pPreviousBB )
{
m_frame.setTemporary( p.destIndex(), ValueToIRExpr( Evaluate( val, *this ) ) );
return false;
|
| ︙ | ︙ | |||
327 328 329 330 331 332 333 |
{
auto* optPTerm = m_frame.getTemporary( va.index );
if( !optPTerm )
return nullptr;
return &( **optPTerm );
}
| > > > > > > > > > > > > > > > > > | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
{
auto* optPTerm = m_frame.getTemporary( va.index );
if( !optPTerm )
return nullptr;
return &( **optPTerm );
}
Term VM::BuildUninitializedValue( const Value& type )
{
if( !IsTupleType( type ) )
return TSID( UNINITIALIZED );
auto tupContent = make_shared< Vector >();
tupContent->reserve( TupleTypeSize( type ) );
ForEachInTupleType( type, [&]( auto&& t )
{
tupContent->append( BuildUninitializedValue( *ValueFromIRExpr( t ) ) );
return true;
} );
return ValueToIRExpr( Value( ValueToIRExpr( type ), TERM( move( tupContent ) ) ) );
}
|
Changes to bs/execute/vm.h.
| ︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
ptr< BasicBlock > executeTerminator( const T& )
{
return nullptr;
}
private:
static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
template< typename F >
Value executeEqualityBinOp( const llr::BinaryOp& bo, F&& func );
template< typename F >
Value executeLogicBinOp( const llr::BinaryOp& bo, F&& func );
| > | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
ptr< BasicBlock > executeTerminator( const T& )
{
return nullptr;
}
private:
static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
static Term BuildUninitializedValue( const Value& type );
template< typename F >
Value executeEqualityBinOp( const llr::BinaryOp& bo, F&& func );
template< typename F >
Value executeLogicBinOp( const llr::BinaryOp& bo, F&& func );
|
| ︙ | ︙ |
Changes to bs/sema/inv-ruleset.h.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
virtual ~InvocationRule() {}
virtual bool canBeInvoked( const Context& c, const Value& callee ) const
{
return true;
}
| | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
virtual ~InvocationRule() {}
virtual bool canBeInvoked( const Context& c, const Value& callee ) const
{
return true;
}
virtual Value resolveInvocation( const Context& c, uint32_t locationId, const Value& callee, const Term& args ) const = 0;
virtual Value invoke( const Context& c, uint32_t locationId, const Value& callee, const Term& args, const Term& unifiedCallPat, UnificationContext& uc ) const
{
return PoisonValue();
}
virtual optional< Term > getSignature( const Value& callee ) const
{
return nullopt;
|
| ︙ | ︙ |
Changes to bs/sema/invocation.cpp.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
if( callee.isPoison() || args.isPoison() )
return PoisonValue();
auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
if( loc == ~0 )
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
| | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
if( callee.isPoison() || args.isPoison() )
return PoisonValue();
auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
if( loc == ~0 )
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
auto result = pInvRule->resolveInvocation( c, loc, callee, args.val() ).setLocationId( loc );
// If the result is non-void, register it for destruction.
// TODO probably just always store it as a numbered temporary
// since we'll need this later to implement the dot operator
// (as it will need to create a reference to the temporary)
if( c.codeBuilder() && result.type() != GetValueType< void >() )
{
|
| ︙ | ︙ |
Changes to bs/sema/overloadset.cpp.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
return paramUTrie->merge( *params, uTrieMergeFunc );
} );
return success;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
return paramUTrie->merge( *params, uTrieMergeFunc );
} );
return success;
}
OverloadSet::UniGen OverloadSet::unify( const Term& domPat, const Term& argsPat, const Term& rtPat, const UnificationContext& uc ) const
{
auto argDecomp = Decompose( argsPat,
Val< pvec >()
);
if( !argDecomp )
co_return;
for( auto&& [domain,paramUTrie] : Enumerate( m_trie ) )
{
auto localC = uc;
localC.addComplexity( GetComplexity( domain ) + GetComplexity( domPat ) );
for( auto&& [uniDom,uc] : Unify( domain, domPat, localC ) )
{
for( auto&& [paramsVec,uniParamsVec,rtTrie,uc] : paramUTrie->unify( *argDecomp->get(), uc ) )
{
auto params = TERM( make_shared< Vector >( paramsVec ) );
auto uniParams = TERM( make_shared< Vector >( uniParamsVec ) );
for( auto&& [rt,ovl] : Enumerate( rtTrie ) )
{
auto localC = uc;
localC.addComplexity( GetComplexity( rt ) + GetComplexity( rtPat ) );
for( auto&& [uniRt,uc] : Unify( rt, rtPat, localC ) )
{
|
| ︙ | ︙ |
Changes to bs/sema/overloadset.h.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
using UniGen = Generator< tuple<
Term,
const Overload&,
UnificationContext
> >;
| < | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
using UniGen = Generator< tuple<
Term,
const Overload&,
UnificationContext
> >;
UniGen unify( const Term& domPat, const Term& argsPat, const Term& rtPat, const UnificationContext& uc ) const;
private:
Term m_identity;
using trie_type = Trie< ptr< UTrie< Trie< Overload > > > >;
trie_type m_trie;
};
|
| ︙ | ︙ |
Changes to bs/sema/postprocess.cpp.
| ︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
auto&& [pPP, t] = *result;
return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
}
optional< Term > Postprocess( const Term& src, UnificationContext& uc )
{
if( !holds_alternative< pvec >( src ) )
return src;
if( auto optPP = UnwrapPostprocFunc( src ) )
{
auto val = Postprocess( optPP->first, uc );
if( !val )
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
auto&& [pPP, t] = *result;
return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
}
optional< Term > Postprocess( const Term& src, UnificationContext& uc )
{
if( auto optHole = HoleFromIRExpr( src ) )
{
const auto& hole = *optHole;
if( !hole.name.isNumerical() )
{
// If the name is not found, output it as is.
auto index = uc.getLHSHoleIndex( hole.name );
if( index == UnificationContext::InvalidIndex )
return src;
const auto& val = uc.getValue( index );
if( !val )
return src;
return Postprocess( *val, uc );
}
else
{
const auto& optVal = uc.getValue( hole.name.id() );
if( !optVal )
return HOLE( "_"_sid );
return Postprocess( *optVal, uc );
}
}
if( !holds_alternative< pvec >( src ) )
return src;
if( auto optPP = UnwrapPostprocFunc( src ) )
{
auto val = Postprocess( optPP->first, uc );
if( !val )
|
| ︙ | ︙ |
Changes to bs/sema/substitute.cpp.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 |
auto outputTerms = make_shared< Vector >();
outputTerms->reserve( vec.terms().size() );
for( auto&& t : vec.terms() )
outputTerms->append( Substitute( t, context ) );
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 27 28 29 30 31 32 33 34 35 36 |
auto outputTerms = make_shared< Vector >();
outputTerms->reserve( vec.terms().size() );
for( auto&& t : vec.terms() )
outputTerms->append( Substitute( t, context ) );
return outputTerms;
}
}
|
Changes to bs/sema/substitute.h.
1 2 3 4 5 6 |
#ifndef GOOSE_SEMA_SUBSTITUTE_H
#define GOOSE_SEMA_SUBSTITUTE_H
namespace goose::sema
{
extern Term Substitute( const Term& src, const UnificationContext& context );
| < | 1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_SEMA_SUBSTITUTE_H
#define GOOSE_SEMA_SUBSTITUTE_H
namespace goose::sema
{
extern Term Substitute( const Term& src, const UnificationContext& context );
}
#endif
|
Changes to bs/sema/tests/unify-holes.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 19 |
namespace
{
// Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
// and that this solution is the expected one.
void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const UnificationScore& expectedScore )
{
Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
| > > | | > > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
namespace
{
// Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
// and that this solution is the expected one.
void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const UnificationScore& expectedScore )
{
Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
UnificationContext uc( ctxt );
uc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );
auto g = Unify( lhs, rhs, uc );
auto it = g.begin();
REQUIRE( it != g.end() );
auto&& [e,c] = *it;
REQUIRE( c.numUnknownValues() == 0 );
REQUIRE( c.score() == expectedScore );
auto sol = Postprocess( e, c );
REQUIRE( sol == expectedSolution );
++it;
REQUIRE( it == g.end() );
}
void CheckForNoSolution( const Term& lhs, const Term& rhs )
{
Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
UnificationContext uc( ctxt );
uc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );
auto g = Unify( lhs, rhs, uc );
auto it = g.begin();
REQUIRE( it == g.end() );
}
}
SCENARIO( "Unify works", "[unify]" )
|
| ︙ | ︙ |
Changes to bs/sema/tests/utrie-unify.cpp.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
vector< tuple< Term, string, UnificationScore > > solutions;
for( auto&& [lv, v,content,c] : trie->unify( vec, context ) )
{
if( c.numUnknownValues() )
continue;
| | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
vector< tuple< Term, string, UnificationScore > > solutions;
for( auto&& [lv, v,content,c] : trie->unify( vec, context ) )
{
if( c.numUnknownValues() )
continue;
auto sol = Postprocess( TERM( make_shared< Vector >( v ) ), c );
solutions.emplace_back( *sol, content, c.score() );
}
sort( solutions.begin(), solutions.end(), []( auto&& a, auto&& b )
{
return get< 1 >( a ) < get< 1 >( b );
} );
|
| ︙ | ︙ |
Changes to bs/sema/uni-context.h.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
}
bool isValueResolutionRequired() const
{
return m_valuesAreRequired;
}
| < < < < < | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
}
bool isValueResolutionRequired() const
{
return m_valuesAreRequired;
}
const optional< Term >& getValue( uint32_t index ) const
{
assert( m_pCow->values.size() > index );
return m_pCow->values[index].m_term;
}
template< typename T >
|
| ︙ | ︙ | |||
89 90 91 92 93 94 95 |
void subComplexity( uint32_t c ) { m_complexity -=c; }
void setComplexity( uint32_t complexity ) { m_complexity = complexity; }
void addAnonymousHole() { ++m_numAnonymousHoles; }
auto score() const { return UnificationScore( m_complexity, m_pCow->holeDict.size() + m_numAnonymousHoles ); }
| < < < < < < | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
void subComplexity( uint32_t c ) { m_complexity -=c; }
void setComplexity( uint32_t complexity ) { m_complexity = complexity; }
void addAnonymousHole() { ++m_numAnonymousHoles; }
auto score() const { return UnificationScore( m_complexity, m_pCow->holeDict.size() + m_numAnonymousHoles ); }
// Used to detect and reject recursive hole nesting.
bool isHoleLocked( uint32_t index ) const;
void lockHole( uint32_t index );
void unlockHole( uint32_t index );
private:
void setValueRequired( uint32_t index );
|
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
unordered_map< HoleName, uint32_t > holeDict;
unordered_set< uint32_t > lockedHoles;
};
ptr< Cow > m_pCow = make_shared< Cow >();
bool m_valuesAreRequired = true;
| < | 121 122 123 124 125 126 127 128 129 130 131 |
unordered_map< HoleName, uint32_t > holeDict;
unordered_set< uint32_t > lockedHoles;
};
ptr< Cow > m_pCow = make_shared< Cow >();
bool m_valuesAreRequired = true;
};
}
#endif
|
Changes to bs/sema/unify.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "sema.h"
namespace goose::sema
{
UnificationSolution FindBestUnification( const Term& lhs, const Term& rhs, const Context& context )
{
optional< UnificationContext > bestUC;
optional< Term > bestSol;
bool ambiguous = false;
| > > > | > > > | | < < < < < | < < < < < < < < < < < < < < < < < | < < | < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include "sema.h"
namespace goose::sema
{
UnificationSolution FindBestUnification( const Term& lhs, const Term& rhs, const Context& context )
{
optional< UnificationContext > bestUC;
optional< Term > bestSol;
bool ambiguous = false;
UnificationContext uc( context );
uc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );
for( auto&& [s, uc] : Unify( lhs, rhs, uc ) )
{
if( uc.numUnknownValues() )
continue;
if( bestUC && uc.score() < bestUC->score() )
continue;
auto pps = Postprocess( s, uc );
if( !pps )
continue;
if( bestUC && uc.score() == bestUC->score() )
{
ambiguous = true;
continue;
}
bestUC = uc;
bestSol = move( *pps );
ambiguous = false;
}
if( ambiguous )
return AmbiguousUnification{};
if( !bestSol )
return {};
return make_pair( move( *bestSol ), move( *bestUC ) );
}
UniGen Unify( const Term& lhs, const Term& rhs, const UnificationContext& context )
{
const auto& rules = context.rules()->uniRules();
MatchSolution bestSol;
|
| ︙ | ︙ |
Changes to bs/sema/unify.h.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
UniSol
>;
UnificationSolution FindBestUnification( const Term& lhs, const Term& rhs, const Context& context );
using UniGen = Generator< pair< Term, UnificationContext > >;
| < < < < < < < < < | 14 15 16 17 18 19 20 21 22 23 24 25 |
UniSol
>;
UnificationSolution FindBestUnification( const Term& lhs, const Term& rhs, const Context& context );
using UniGen = Generator< pair< Term, UnificationContext > >;
UniGen Unify( const Term& lhs, const Term& rhs, const UnificationContext& context );
UniGen HalfUnify( const Term& lhs, const UnificationContext& context );
}
#endif
|
Changes to bs/verify/builder.cpp.
| ︙ | ︙ | |||
102 103 104 105 106 107 108 |
if( m_remapper )
{
m_remapper->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
{
if( auto predVar = retrieveVar( index, predIndex ) )
{
if( !newVar )
| | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
if( m_remapper )
{
m_remapper->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
{
if( auto predVar = retrieveVar( index, predIndex ) )
{
if( !newVar )
newVar = BuildZ3ConstantFromType( *this, predVar->type, format( "v{}", m_nextUniqueId++ ) );
if( newVar->expr.is_int() )
{
add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
newVar->expr == GetAsInt( predVar->expr, newVar->type ) ) );
}
else if( newVar->expr.is_bv() )
|
| ︙ | ︙ |
Changes to bs/verify/builder.h.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
public:
Builder( const sema::Context& c, z3::solver& solver, Remapper* remapper = nullptr ) :
m_context( &c ),
m_solver( &solver ),
m_remapper( remapper )
{}
| | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public:
Builder( const sema::Context& c, z3::solver& solver, Remapper* remapper = nullptr ) :
m_context( &c ),
m_solver( &solver ),
m_remapper( remapper )
{}
const auto& context() const { return *m_context; }
auto* solver() { return m_solver; }
auto* remapper() { return m_remapper; }
uint32_t currentBBIndex() const { return m_currentBBIndex; }
void setTraceMode( bool b ) { m_traceMode = b; }
void setCFG( const ptr< llr::CFG >& cfg )
|
| ︙ | ︙ |
Changes to bs/verify/call.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
return nullopt;
const auto& ft = func->type();
const auto& fvi = ft.verifInfos();
if( !fvi )
return nullopt;
| | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
return nullopt;
const auto& ft = func->type();
const auto& fvi = ft.verifInfos();
if( !fvi )
return nullopt;
auto retExpr = BuildZ3ConstantFromType( b, ft.returnType(), format( "r{}", b.newUniqueId() ) );
// Create a temporary builder to construct the z3 expressions out of the
// function's pre-conditions and postconditions, configured
// to perform the necessary replacements for arguments and for the
// return value placeholder.
Builder cb( b.context(), *b.solver(), b.remapper() );
|
| ︙ | ︙ |
Changes to bs/verify/condition.cpp.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
);
if( !result )
return true;
auto&& [type, val, locId] = *result;
| | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
);
if( !result )
return true;
auto&& [type, val, locId] = *result;
if( auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) ) )
m_builder.setVar( varId, move( *paramInit ) );
++varId;
return true;
} );
}
|
| ︙ | ︙ | |||
74 75 76 77 78 79 80 |
"invalid condition." );
return true;
}
stringstream sstr;
sstr << '$' << m_nextIndex++;
| | | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
"invalid condition." );
return true;
}
stringstream sstr;
sstr << '$' << m_nextIndex++;
auto boolCst = GetZ3Context().bool_const( sstr.str().c_str() );
m_idAndLocs.emplace_back( boolCst.id(), val.locationId() );
m_solver.add( zv->expr, boolCst );
return true;
} );
return success;
}
bool Condition::verify()
{
if( m_solver.check() == z3::check_result::sat )
return true;
// TODO: emit a generic "those verification expressions fucking suck"
// error if it's neither sat or unsat.
// Retrieve the unsat core to find out which assertions are unsatisfiable,
// and emit error messages.
auto unsatCore = m_solver.unsat_core();
llvm::SmallVector< uint32_t, 8 > errorLocations;
|
| ︙ | ︙ |
Changes to bs/verify/func.cpp.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 |
if( !result )
return true;
auto&& [type, val, locId] = *result;
auto paramVal = BuildComputedValue( type, llr::Load( llr::VarAddress( varId ), type ) );
// Initialize every parameter containing variable with an freshly named constant of the right type.
| | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
if( !result )
return true;
auto&& [type, val, locId] = *result;
auto paramVal = BuildComputedValue( type, llr::Load( llr::VarAddress( varId ), type ) );
// Initialize every parameter containing variable with an freshly named constant of the right type.
if( auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) ) )
m_builder.setVar( varId, move( *paramInit ) );
++varId;
if( auto zv = BuildZ3ExprFromValue( m_builder, paramVal ) )
{
ForEachPredicate( m_builder, type, zv->expr, [&]( auto&& z3expr, auto locId )
|
| ︙ | ︙ |
Changes to bs/verify/phi.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "verify.h"
#include "builtins/builtins.h"
namespace goose::verify
{
optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr )
{
const auto* remapper = b.remapper();
if( !remapper )
return nullopt;
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include "verify.h"
#include "builtins/builtins.h"
namespace goose::verify
{
optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr )
{
const auto* remapper = b.remapper();
if( !remapper )
return nullopt;
auto newVar = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", b.newUniqueId() ) );
if( !newVar )
return nullopt;
auto& c = GetZ3Context();
uint32_t bbIndex = b.currentBBIndex();
// Model the ssa phi operation by creating a series of equality assertions, each implied by
|
| ︙ | ︙ |
Changes to bs/verify/storage.cpp.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
if( !tinfo )
return nullopt;
// The only aggregate type that we handle for now are tuples.
// TODO: arrays
auto elemType = GetTupleTypeElement( val->type, index );
auto elemExpr = tinfo->proj( val->expr, index );
| < < < | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
if( !tinfo )
return nullopt;
// The only aggregate type that we handle for now are tuples.
// TODO: arrays
auto elemType = GetTupleTypeElement( val->type, index );
auto elemExpr = tinfo->proj( val->expr, index );
val = Z3Val{ move( elemExpr ), *ValueFromIRExpr( elemType ) };
}
return val;
}
optional< Z3Val > LoadFromAddress( Builder& b, const BaseAddress& baseAddr )
{
|
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
auto elemIndex = path[index];
for( uint32_t i = 0; i < elemCount; ++i )
{
auto elemType = GetTupleTypeElement( aggregate.type, i );
auto elemExpr = tinfo->proj( aggregate.expr, i );
| < < | | < < | < | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
auto elemIndex = path[index];
for( uint32_t i = 0; i < elemCount; ++i )
{
auto elemType = GetTupleTypeElement( aggregate.type, i );
auto elemExpr = tinfo->proj( aggregate.expr, i );
// Copy all elements from the existing value as is,
// except for the one pointed to by the current path index.
if( i == elemIndex )
{
// If we didn't reach the end of the path yet, recurse.
// Otherwise, it means we finally reached the nested member that we wanted to modify,
// so push the new value.
if( index < ( path.size() - 1 ) )
{
auto newElem = ModifyAggregate( b, Z3Val{ move( elemExpr ), *ValueFromIRExpr( elemType ) }, path, ++index, move( valToStore ) );
if( !newElem )
return nullopt;
args.push_back( move( *newElem ) );
}
else
args.push_back( valToStore.expr );
}
else
args.push_back( move( elemExpr ) );
}
return tinfo->ctor( args );
}
void StoreToAddress( Builder& b, const Address& addr, Z3Val&& valToStore )
{
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 |
void StoreToAddressForBB( Builder& b, uint32_t bbIndex, const llr::VarAddress& va, Z3Val&& val )
{
b.setVarForBasicBlock( bbIndex, va.index, move( val ) );
}
void HavocAddressForBB( Builder& b, uint32_t bbIndex, const Term& type, const Address& addr )
{
| | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
void StoreToAddressForBB( Builder& b, uint32_t bbIndex, const llr::VarAddress& va, Z3Val&& val )
{
b.setVarForBasicBlock( bbIndex, va.index, move( val ) );
}
void HavocAddressForBB( Builder& b, uint32_t bbIndex, const Term& type, const Address& addr )
{
auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );
if( !valToStore )
return;
StoreToAddressForBB( b, bbIndex, addr, move( *valToStore ) );
}
}
|
Changes to bs/verify/type.cpp.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
optional< TypeInfo > TypeCache::CreateTypeInfoForBasicType( const sema::Context& c, const Value& typeVal )
{
// Handle all non-aggregate types (bool, signed int, unsigned int, floats) directly.
// TODO: some are missing and will be dealt with later on.
if( ValueToIRExpr( typeVal ) == GetValueType< bool >() )
{
return TypeInfo { GetZ3Context().bool_sort(),
| | > > > > | > > > > > | > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
optional< TypeInfo > TypeCache::CreateTypeInfoForBasicType( const sema::Context& c, const Value& typeVal )
{
// Handle all non-aggregate types (bool, signed int, unsigned int, floats) directly.
// TODO: some are missing and will be dealt with later on.
if( ValueToIRExpr( typeVal ) == GetValueType< bool >() )
{
return TypeInfo { GetZ3Context().bool_sort(),
[]( auto&& b )
{
return GetZ3Context().bool_const( format( "u{}", b.newUniqueId() ).c_str() );
},
[]( auto&& b, auto&& val )
{
return GetZ3Context().bool_val( *FromValue< bool >( val ) );
}
};
}
if( auto intType = FromValue< builtins::IntegerType >( typeVal ) )
{
if( intType->m_signed )
{
return TypeInfo { GetZ3Context().int_sort(),
[]( auto&& b )
{
return GetZ3Context().int_const( format( "u{}", b.newUniqueId() ).c_str() );
},
[]( auto&& b, auto&& val )
{
auto intVal = FromValue< APSInt >( val );
return GetZ3Context().int_val( intVal->getExtValue() );
}
};
}
else
{
auto numBits = intType->m_numBits;
return TypeInfo { GetZ3Context().bv_sort( intType->m_numBits ),
[numBits]( auto&& b )
{
return GetZ3Context().bv_const( format( "u{}", b.newUniqueId() ).c_str(), numBits );
},
[numBits]( auto&& b, auto&& val )
{
auto intVal = FromValue< APSInt >( val );
return GetZ3Context().bv_val( intVal->getExtValue(), numBits );
}
};
}
}
|
| ︙ | ︙ | |||
126 127 128 129 130 131 132 |
elemSorts.push_back( *tinfo->sort );
}
llvm::SmallVector< z3::func_decl, 8 > projs;
auto&& [tupSort, tupCtor] = mkZ3TupleSort( sortName.c_str(), size, elemNames, elemSorts, projs );
const auto& ctor = tupCtor;
| > > > > > > > > > > > > > > | < < < | < < < < < < | | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
elemSorts.push_back( *tinfo->sort );
}
llvm::SmallVector< z3::func_decl, 8 > projs;
auto&& [tupSort, tupCtor] = mkZ3TupleSort( sortName.c_str(), size, elemNames, elemSorts, projs );
const auto& ctor = tupCtor;
auto undefined = [size, elemTInfos, ctor]( auto&& b )
{
llvm::SmallVector< z3::expr, 8 > args;
args.reserve( size );
for( auto&& tinfo : elemTInfos )
{
auto elem = tinfo.undefined( b );
args.push_back( move( elem ) );
}
return ctor( args.size(), &args.front() );
};
auto build = [size, elemTInfos, ctor]( auto&& b, auto&& val )
{
llvm::SmallVector< z3::expr, 8 > args;
args.reserve( size );
uint32_t i = 0;
ForEachInTuple( val, [&]( auto&& elemVal )
{
auto elem = elemTInfos[i++].build( b, elemVal );
args.push_back( move( elem ) );
return true;
} );
return ctor( args.size(), &args.front() );
};
auto proj = [projs]( auto&& tup, auto&& index )
{
assert( index < projs.size() );
return projs[index]( tup );
};
auto ctorWrapper = [ctor]( auto&& args )
{
return ctor( args.size(), &args.front() );
};
return TypeInfo { tupSort, undefined, build, proj, ctorWrapper };
}
|
Changes to bs/verify/type.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_VERIFY_TYPE_H
#define GOOSE_VERIFY_TYPE_H
namespace goose::verify
{
struct TypeInfo
{
optional< z3::sort > sort;
| > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#ifndef GOOSE_VERIFY_TYPE_H
#define GOOSE_VERIFY_TYPE_H
namespace goose::verify
{
struct TypeInfo
{
optional< z3::sort > sort;
function< z3::expr ( Builder& ) > undefined;
function< z3::expr ( Builder&, const Value& ) > build;
function< z3::expr ( const z3::expr&, uint32_t index ) > proj;
function< z3::expr ( llvm::SmallVector< z3::expr, 8 > ) > ctor;
};
// For complex types that require the creation of custom constructor funcs in z3
// (for instances tuples), we keep a cache to avoid recreating them multiple times.
class TypeCache
{
public:
|
| ︙ | ︙ |
Changes to bs/verify/value.cpp.
1 2 3 4 5 6 7 8 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
| | | | | < < < | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
optional< Z3Val > BuildZ3ValFromConstant( Builder& b, const Value& val )
{
auto loweredVal = LowerConstantForVerification( b.context(), val );
if( !loweredVal || loweredVal->isPoison() )
return nullopt;
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), loweredVal->type() );
if( !tinfo )
return nullopt;
auto zexpr = tinfo->build( b, *loweredVal );
return Z3Val { move( zexpr ), *ValueFromIRExpr( val.type() ) };
}
optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToIRExpr( type ) );
if( !tinfo )
return nullopt;
assert( tinfo->sort );
return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type };
}
optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), type );
if( !tinfo )
return nullopt;
assert( tinfo->sort );
return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), *ValueFromIRExpr( type ) };
}
|
| ︙ | ︙ | |||
178 179 180 181 182 183 184 |
optional< Z3Val > BuildZ3Op( Builder& b, const GetTemporary& instr )
{
auto zv = b.retrieveVar( instr.index() );
if( zv )
return zv;
| | | < | | > > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
optional< Z3Val > BuildZ3Op( Builder& b, const GetTemporary& instr )
{
auto zv = b.retrieveVar( instr.index() );
if( zv )
return zv;
return BuildZ3ConstantFromType( b, instr.type(), format( "v{}", instr.index() ) );
}
optional< Z3Val > BuildZ3Op( Builder& b, const AllocVar& instr )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToIRExpr( instr.type() ) );
if( !tinfo )
return nullopt;
return Z3Val{ tinfo->undefined( b ), instr.type() };
}
// Implemented in phi.cpp
optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr );
// TODO: LoadConstStr. Build a z3 str value.
|
| ︙ | ︙ | |||
381 382 383 384 385 386 387 |
optional< Z3Val > BuildZ3Op( Builder& b, const Placeholder& instr )
{
const auto* expr = b.retrievePlaceholder( instr.name() );
if( expr )
return Z3Val{ *expr, *ValueFromIRExpr( instr.type() ) };
| | | | | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
optional< Z3Val > BuildZ3Op( Builder& b, const Placeholder& instr )
{
const auto* expr = b.retrievePlaceholder( instr.name() );
if( expr )
return Z3Val{ *expr, *ValueFromIRExpr( instr.type() ) };
return BuildZ3ConstantFromType( b, instr.type(), format( "p{}", instr.name() ) );
}
optional< Z3Val > BuildZ3Op( Builder& b, const llr::Instruction& instr )
{
return visit( [&]( auto&& e )
{
return BuildZ3Op( b, e );
}, instr.content() );
}
optional< Z3Val > BuildZ3ExprFromValue( Builder& b, const Value& val )
{
if( val.isPoison() )
return nullopt;
if( val.isConstant() )
return BuildZ3ValFromConstant( b, val );
if( auto expr = BuildZ3Op( b, *val.llr() ) )
return expr;
return BuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
}
}
|
Changes to bs/verify/value.h.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
{
struct Z3Val
{
z3::expr expr;
ir::Value type;
};
| | | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
{
struct Z3Val
{
z3::expr expr;
ir::Value type;
};
extern optional< Z3Val > BuildZ3ValFromConstant( Builder& b, const Value& val );
extern optional< Z3Val > BuildZ3ExprFromValue( Builder& b, const Value& val );
extern optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
extern optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );
extern optional< Z3Val > BuildZ3Op( Builder& b, const llr::Instruction& instr );
extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
extern z3::expr GetAsBitVec( const Z3Val& zv );
extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
extern z3::expr GetAsInt( const Z3Val& zv );
|
| ︙ | ︙ |
Changes to tests/noprelude/verify/z3out-test-1.txt.
1 2 3 4 5 6 7 8 9 | === Checking compilation-time call === === Begin function verification trace === (= b1 true) (= e1_2 (and b1 (distinct p0 5))) (= b2 e1_2) (= e1_3 (and b1 (not (distinct p0 5)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | === Checking compilation-time call === === Begin function verification trace === (= b1 true) (= e1_2 (and b1 (distinct p0 5))) (= b2 e1_2) (= e1_3 (and b1 (not (distinct p0 5)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v30 p0)) (=> e2_3 (= v30 5)) check_unsat (and b3 (not (= v30 5))) === End function verification trace === === Begin function verification trace === (= b1 true) === End function verification trace === |
Changes to tests/noprelude/verify/z3out-test-2.txt.
1 2 3 4 5 6 7 8 9 | === Checking compilation-time call === === Checking compilation-time call === === Begin function verification trace === assume (distinct p0 p1) (= b1 true) (= e1_2 (and b1 (< p0 p1))) (= b2 e1_2) | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | === Checking compilation-time call === === Checking compilation-time call === === Begin function verification trace === assume (distinct p0 p1) (= b1 true) (= e1_2 (and b1 (< p0 p1))) (= b2 e1_2) (=> e1_2 (= v34 p1)) (=> e1_2 (= v36 p0)) (= e1_3 (and b1 (not (< p0 p1)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v37 p0)) (=> e2_3 (= v37 v34)) (=> e1_3 (= v38 p1)) (=> e2_3 (= v38 v36)) check_unsat (and b3 (not (> v37 v38))) === End function verification trace === === Begin function verification trace === (= b1 true) check_unsat (not (distinct 2 1)) === End function verification trace === |
Changes to tests/noprelude/verify/z3out-test-3.txt.
1 2 3 4 5 6 7 8 9 10 11 | === Checking compilation-time call === === Checking compilation-time call === === Checking compilation-time call === === Begin function verification trace === assume (distinct p0 p1) (= b1 true) (= e1_2 (and b1 (< p0 p1))) (= b2 e1_2) | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | === Checking compilation-time call === === Checking compilation-time call === === Checking compilation-time call === === Begin function verification trace === assume (distinct p0 p1) (= b1 true) (= e1_2 (and b1 (< p0 p1))) (= b2 e1_2) (=> e1_2 (= v34 p1)) (=> e1_2 (= v36 p0)) (= e1_3 (and b1 (not (< p0 p1)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v37 p0)) (=> e2_3 (= v37 v34)) (=> e1_3 (= v38 p1)) (=> e2_3 (= v38 v36)) check_unsat (and b3 (not (> (- v37 v38) 0))) === End function verification trace === === Checking compilation-time call === check_unsat (not (distinct 2 1)) === Begin function verification trace === (= b1 true) |
| ︙ | ︙ |
Changes to tests/noprelude/verify/z3out-test-4.txt.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 | === Checking compilation-time call === === Begin function verification trace === assume (distinct p0 p1) (= b1 true) (= e1_2 (and b1 (< p0 p1))) (= b2 e1_2) | | | | | | | | | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | === Checking compilation-time call === === Begin function verification trace === assume (distinct p0 p1) (= b1 true) (= e1_2 (and b1 (< p0 p1))) (= b2 e1_2) (=> e1_2 (= v34 p1)) (=> e1_2 (= v36 p0)) (= e1_3 (and b1 (not (< p0 p1)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v37 p0)) (=> e2_3 (= v37 v34)) (=> e1_3 (= v38 p1)) (=> e2_3 (= v38 v36)) check_unsat (and b3 (not (> (- v37 v38) 0))) === End function verification trace === === Begin function verification trace === assume (distinct p0 0) (= b1 true) check_unsat (not (distinct p0 (* 2 p0))) assume (> r39 0) check_unsat (not (distinct r39 0)) === End function verification trace === === Checking compilation-time call === check_unsat (not (distinct 5 0)) === Begin function verification trace === (= b1 true) |
| ︙ | ︙ |
Changes to tests/noprelude/verify/z3out-test-logic-and.txt.
1 2 3 4 | === Begin function verification trace === (= b1 true) (= e1_2 (and b1 false)) (= b2 e1_2) | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | === Begin function verification trace === (= b1 true) (= e1_2 (and b1 false)) (= b2 e1_2) (=> e1_2 (= v31 true)) (= e1_3 (and b1 (not false))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v32 false)) (=> e2_3 (= v33 v31)) (=> e2_3 (= v32 v33)) === End function verification trace === === Begin function verification trace === (= b1 true) === End function verification trace === |
Changes to tests/noprelude/verify/z3out-test-logic-or.txt.
1 2 3 4 | === Begin function verification trace === (= b1 true) (= e1_2 (and b1 (not false))) (= b2 e1_2) | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | === Begin function verification trace === (= b1 true) (= e1_2 (and b1 (not false))) (= b2 e1_2) (=> e1_2 (= v31 true)) (= e1_3 (and b1 false)) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v32 true)) (=> e2_3 (= v33 v31)) (=> e2_3 (= v32 v33)) === End function verification trace === === Begin function verification trace === (= b1 true) === End function verification trace === |