2017-06-05 02:50:30 +02:00
# include <anthem/HiddenPredicateElimination.h>
# include <anthem/ASTCopy.h>
# include <anthem/ASTUtils.h>
# include <anthem/ASTVisitors.h>
# include <anthem/Exception.h>
# include <anthem/Simplification.h>
# include <anthem/output/AST.h>
namespace anthem
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// HiddenPredicateElimination
//
////////////////////////////////////////////////////////////////////////////////////////////////////
struct PredicateReplacement
{
const ast : : Predicate & predicate ;
ast : : Formula replacement ;
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Replaces all occurrences of a variable in a given term with another variable
struct ReplaceVariableInTermVisitor : public ast : : RecursiveTermVisitor < ReplaceVariableInTermVisitor >
{
static void accept ( ast : : Variable & variable , ast : : Term & , const ast : : VariableDeclaration & original , ast : : VariableDeclaration & replacement )
{
if ( variable . declaration = = & original )
// No dangling variables can result from this operation, and hence, fixing them is not necessary
variable . declaration = & replacement ;
}
// Ignore all other types of expressions
template < class T >
static void accept ( T & , ast : : Term & , const ast : : VariableDeclaration & , ast : : VariableDeclaration & )
{
}
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Replaces all occurrences of a variable in a given formula with another variable
struct ReplaceVariableInFormulaVisitor : public ast : : RecursiveFormulaVisitor < ReplaceVariableInFormulaVisitor >
{
static void accept ( ast : : Comparison & comparison , ast : : Formula & , const ast : : VariableDeclaration & original , ast : : VariableDeclaration & replacement )
{
comparison . left . accept ( ReplaceVariableInTermVisitor ( ) , comparison . left , original , replacement ) ;
comparison . right . accept ( ReplaceVariableInTermVisitor ( ) , comparison . right , original , replacement ) ;
}
static void accept ( ast : : In & in , ast : : Formula & , const ast : : VariableDeclaration & original , ast : : VariableDeclaration & replacement )
{
in . element . accept ( ReplaceVariableInTermVisitor ( ) , in . element , original , replacement ) ;
in . set . accept ( ReplaceVariableInTermVisitor ( ) , in . set , original , replacement ) ;
}
static void accept ( ast : : Predicate & predicate , ast : : Formula & , const ast : : VariableDeclaration & original , ast : : VariableDeclaration & replacement )
{
for ( auto & argument : predicate . arguments )
argument . accept ( ReplaceVariableInTermVisitor ( ) , argument , original , replacement ) ;
}
// Ignore all other types of expressions
template < class T >
static void accept ( T & , ast : : Formula & , const ast : : VariableDeclaration & , ast : : VariableDeclaration & )
{
}
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Replace a predicate in a term with a formula
struct ReplacePredicateInFormulaVisitor : public ast : : RecursiveFormulaVisitor < ReplacePredicateInFormulaVisitor >
{
static void accept ( ast : : Predicate & predicate , ast : : Formula & formula , const PredicateReplacement & predicateReplacement )
{
2018-04-13 20:40:40 +02:00
if ( predicate . declaration ! = predicateReplacement . predicate . declaration )
2017-06-05 02:50:30 +02:00
return ;
auto formulaReplacement = ast : : prepareCopy ( predicateReplacement . replacement ) ;
for ( size_t i = 0 ; i < predicate . arguments . size ( ) ; i + + )
{
assert ( predicateReplacement . predicate . arguments [ i ] . is < ast : : Variable > ( ) ) ;
const auto & original = * predicateReplacement . predicate . arguments [ i ] . get < ast : : Variable > ( ) . declaration ;
assert ( predicate . arguments [ i ] . is < ast : : Variable > ( ) ) ;
auto & replacement = * predicate . arguments [ i ] . get < ast : : Variable > ( ) . declaration ;
formulaReplacement . accept ( ReplaceVariableInFormulaVisitor ( ) , formulaReplacement , original , replacement ) ;
}
formula = std : : move ( formulaReplacement ) ;
}
// Ignore all other types of expressions
template < class T >
static void accept ( T & , ast : : Formula & , const PredicateReplacement & )
{
}
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Detect whether a formula contains a circular dependency on a given predicate
struct DetectCircularDependcyVisitor : public ast : : RecursiveFormulaVisitor < DetectCircularDependcyVisitor >
{
2018-04-13 20:40:40 +02:00
static void accept ( ast : : Predicate & predicate , ast : : Formula & , const ast : : PredicateDeclaration & predicateDeclaration , bool & hasCircularDependency )
2017-06-05 02:50:30 +02:00
{
2018-04-13 20:40:40 +02:00
if ( predicate . declaration = = & predicateDeclaration )
2017-06-05 02:50:30 +02:00
hasCircularDependency = true ;
}
// Ignore all other types of expressions
template < class T >
2018-04-13 20:40:40 +02:00
static void accept ( T & , ast : : Formula & , const ast : : PredicateDeclaration & , bool & )
2017-06-05 02:50:30 +02:00
{
}
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////
2017-06-12 15:34:35 +02:00
// Finds the replacement for predicates of the form “p(X1, ..., Xn) <-> q(X1, ..., Xn)”
2018-04-13 20:40:40 +02:00
PredicateReplacement findReplacement ( const ast : : PredicateDeclaration & predicateDeclaration , const ast : : Predicate & predicate )
2017-06-05 02:50:30 +02:00
{
// Declare variable used, only used in debug mode
2018-04-13 20:40:40 +02:00
( void ) ( predicateDeclaration ) ;
2017-06-05 02:50:30 +02:00
2018-04-13 20:40:40 +02:00
assert ( predicate . declaration = = & predicateDeclaration ) ;
2017-06-12 15:34:35 +02:00
2017-06-05 02:50:30 +02:00
// Replace with “#true”
2017-06-12 15:34:35 +02:00
return { predicate , ast : : Formula : : make < ast : : Boolean > ( true ) } ;
}
2017-06-05 02:50:30 +02:00
2017-06-12 15:34:35 +02:00
////////////////////////////////////////////////////////////////////////////////////////////////////
2017-06-05 02:50:30 +02:00
2017-06-12 15:34:35 +02:00
// Finds the replacement for predicates of the form “p(X1, ..., Xn) <-> not q(X1, ..., Xn)”
2018-04-13 20:40:40 +02:00
PredicateReplacement findReplacement ( const ast : : PredicateDeclaration & predicateDeclaration , const ast : : Not & not_ )
2017-06-12 15:34:35 +02:00
{
// Declare variable used, only used in debug mode
2018-04-13 20:40:40 +02:00
( void ) ( predicateDeclaration ) ;
2017-06-05 02:50:30 +02:00
2017-06-12 15:34:35 +02:00
assert ( not_ . argument . is < ast : : Predicate > ( ) ) ;
2018-04-13 20:40:40 +02:00
assert ( not_ . argument . get < ast : : Predicate > ( ) . declaration = = & predicateDeclaration ) ;
2017-06-05 02:50:30 +02:00
2017-06-12 15:34:35 +02:00
// Replace with “#false”
return { not_ . argument . get < ast : : Predicate > ( ) , ast : : Formula : : make < ast : : Boolean > ( false ) } ;
}
2017-06-05 02:50:30 +02:00
2017-06-12 15:34:35 +02:00
////////////////////////////////////////////////////////////////////////////////////////////////////
2017-06-05 02:50:30 +02:00
2017-06-12 15:34:35 +02:00
// Finds the replacement for predicates of the form “forall X1, ..., Xn (p(X1, ..., Xn) <-> ...)”
2018-04-13 20:40:40 +02:00
PredicateReplacement findReplacement ( const ast : : PredicateDeclaration & predicateDeclaration , const ast : : Biconditional & biconditional )
2017-06-12 15:34:35 +02:00
{
// Declare variable used, only used in debug mode
2018-04-13 20:40:40 +02:00
( void ) ( predicateDeclaration ) ;
2017-06-05 02:50:30 +02:00
assert ( biconditional . left . is < ast : : Predicate > ( ) ) ;
2018-04-13 20:40:40 +02:00
assert ( biconditional . left . get < ast : : Predicate > ( ) . declaration = = & predicateDeclaration ) ;
2017-06-05 02:50:30 +02:00
// TODO: avoid copy
return { biconditional . left . get < ast : : Predicate > ( ) , ast : : prepareCopy ( biconditional . right ) } ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Finds a replacement for a predicate that should be hidden
2018-04-13 20:40:40 +02:00
PredicateReplacement findReplacement ( const ast : : PredicateDeclaration & predicateDeclaration , const ast : : Formula & completedPredicateDefinition )
2017-06-05 02:50:30 +02:00
{
2017-06-12 15:34:35 +02:00
// TODO: refactor
2017-06-05 02:50:30 +02:00
if ( completedPredicateDefinition . is < ast : : ForAll > ( ) )
2018-04-13 20:40:40 +02:00
return findReplacement ( predicateDeclaration , completedPredicateDefinition . get < ast : : ForAll > ( ) . argument ) ;
2017-06-12 15:34:35 +02:00
else if ( completedPredicateDefinition . is < ast : : Biconditional > ( ) )
2018-04-13 20:40:40 +02:00
return findReplacement ( predicateDeclaration , completedPredicateDefinition . get < ast : : Biconditional > ( ) ) ;
2017-06-05 02:50:30 +02:00
else if ( completedPredicateDefinition . is < ast : : Predicate > ( ) )
2018-04-13 20:40:40 +02:00
return findReplacement ( predicateDeclaration , completedPredicateDefinition . get < ast : : Predicate > ( ) ) ;
2017-06-05 02:50:30 +02:00
else if ( completedPredicateDefinition . is < ast : : Not > ( ) )
2018-04-13 20:40:40 +02:00
return findReplacement ( predicateDeclaration , completedPredicateDefinition . get < ast : : Not > ( ) ) ;
2017-06-05 02:50:30 +02:00
2018-04-19 16:35:37 +02:00
throw CompletionException ( " unsupported completed definition for predicate “ " + predicateDeclaration . name + " / " + std : : to_string ( predicateDeclaration . arity ( ) ) + " ” for hiding predicates " ) ;
2017-06-05 02:50:30 +02:00
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2018-04-13 20:40:40 +02:00
void eliminateHiddenPredicates ( std : : vector < ast : : Formula > & completedFormulas , Context & context )
2017-06-05 02:50:30 +02:00
{
2018-04-13 20:40:40 +02:00
if ( context . defaultPredicateVisibility = = ast : : PredicateDeclaration : : Visibility : : Visible )
2017-06-05 02:50:30 +02:00
{
context . logger . log ( output : : Priority : : Debug ) < < " no predicates to be eliminated " ;
return ;
}
2018-04-13 20:40:40 +02:00
assert ( context . defaultPredicateVisibility = = ast : : PredicateDeclaration : : Visibility : : Hidden ) ;
// TODO: get rid of index-wise matching of completed formulas and predicate declarations
size_t i = - 1 ;
2017-06-05 02:50:30 +02:00
// Replace all occurrences of hidden predicates
2018-04-13 20:40:40 +02:00
for ( auto & predicateDeclaration : context . predicateDeclarations )
2017-06-05 02:50:30 +02:00
{
2018-04-13 20:40:40 +02:00
// Check that the predicate is used and not declared #external
if ( ! predicateDeclaration - > isUsed | | predicateDeclaration - > isExternal )
continue ;
2017-06-05 02:50:30 +02:00
2018-04-13 20:40:40 +02:00
i + + ;
2017-06-05 02:50:30 +02:00
2018-04-13 20:40:40 +02:00
const auto isPredicateVisible =
( predicateDeclaration - > visibility = = ast : : PredicateDeclaration : : Visibility : : Visible )
| | ( predicateDeclaration - > visibility = = ast : : PredicateDeclaration : : Visibility : : Default
& & context . defaultPredicateVisibility = = ast : : PredicateDeclaration : : Visibility : : Visible ) ;
2017-06-05 02:50:30 +02:00
// If the predicate ought to be visible, don’ t eliminate it
2018-04-13 20:40:40 +02:00
if ( isPredicateVisible )
2017-06-05 02:50:30 +02:00
continue ;
2018-04-19 16:35:37 +02:00
context . logger . log ( output : : Priority : : Debug ) < < " eliminating “ " < < predicateDeclaration - > name < < " / " < < predicateDeclaration - > arity ( ) < < " ” " ;
2017-06-05 02:50:30 +02:00
const auto & completedPredicateDefinition = completedFormulas [ i ] ;
2018-04-13 20:40:40 +02:00
auto replacement = findReplacement ( * predicateDeclaration , completedPredicateDefinition ) ;
2017-06-05 02:50:30 +02:00
bool hasCircularDependency = false ;
2018-04-13 20:40:40 +02:00
replacement . replacement . accept ( DetectCircularDependcyVisitor ( ) , replacement . replacement , * predicateDeclaration , hasCircularDependency ) ;
2017-06-05 02:50:30 +02:00
if ( hasCircularDependency )
{
2018-04-19 16:35:37 +02:00
context . logger . log ( output : : Priority : : Warning ) < < " cannot hide predicate “ " < < predicateDeclaration - > name < < " / " < < predicateDeclaration - > arity ( ) < < " ” due to circular dependency " ;
2017-06-05 02:50:30 +02:00
continue ;
}
for ( size_t j = 0 ; j < completedFormulas . size ( ) ; j + + )
if ( j ! = i )
completedFormulas [ j ] . accept ( ReplacePredicateInFormulaVisitor ( ) , completedFormulas [ j ] , replacement ) ;
// TODO: refactor
completedFormulas [ i ] = ast : : Formula : : make < ast : : Boolean > ( true ) ;
}
const auto canBeRemoved =
[ & ] ( const ast : : Formula & completedFormula )
{
if ( ! completedFormula . is < ast : : Boolean > ( ) )
return false ;
return completedFormula . get < ast : : Boolean > ( ) . value = = true ;
} ;
auto removedFormulas = std : : remove_if ( completedFormulas . begin ( ) , completedFormulas . end ( ) , canBeRemoved ) ;
completedFormulas . erase ( removedFormulas , completedFormulas . end ( ) ) ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}