Skip to content

Commit f8299c9

Browse files
author
Daniel Kroening
committed
quantifiers are variable bindings
This commit makes the quantifier base class use the bindings base class. Methods and constructor for the special case of quantifying over one variable are retained.
1 parent 4dd483a commit f8299c9

File tree

7 files changed

+61
-44
lines changed

7 files changed

+61
-44
lines changed

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,30 +290,36 @@ void c_typecheck_baset::typecheck_expr_main(exprt &expr)
290290
else if(expr.id()==ID_forall ||
291291
expr.id()==ID_exists)
292292
{
293-
// op0 is a declaration,
294-
// op1 the bound expression
295-
expr.type()=bool_typet();
293+
// These have two operands.
294+
// op0 is a tuple with one declaration,
295+
// op1 is the bound expression
296296
auto &binary_expr = to_binary_expr(expr);
297+
auto &binding = binary_expr.op0().operands();
298+
auto &where = binary_expr.op1();
299+
300+
DATA_INVARIANT(
301+
binding.size() == 1, "we expect one declaration in forall/exists");
297302

298-
if(binary_expr.op0().get(ID_statement) != ID_decl)
303+
if(binding.front().get(ID_statement) != ID_decl)
299304
{
300305
error().source_location = expr.source_location();
301306
error() << "expected declaration as operand of quantifier" << eom;
302307
throw 0;
303308
}
304309

305-
if(has_subexpr(binary_expr.op1(), ID_side_effect))
310+
if(has_subexpr(where, ID_side_effect))
306311
{
307312
error().source_location = expr.source_location();
308313
error() << "quantifier must not contain side effects" << eom;
309314
throw 0;
310315
}
311316

312-
// replace declaration by symbol expression
313-
symbol_exprt bound = to_code_decl(to_code(binary_expr.op0())).symbol();
314-
binary_expr.op0().swap(bound);
317+
expr.type() = bool_typet();
315318

316-
implicit_typecast_bool(binary_expr.op1());
319+
// replace declarations by symbol expressions
320+
binding.front() = to_code_decl(to_code(binding.front())).symbol();
321+
322+
implicit_typecast_bool(where);
317323
}
318324
else if(expr.id()==ID_label)
319325
{
@@ -710,6 +716,9 @@ void c_typecheck_baset::typecheck_expr_operands(exprt &expr)
710716
}
711717
else if(expr.id()==ID_forall || expr.id()==ID_exists)
712718
{
719+
// These introduce new symbols, which need to be added to the symbol table
720+
// before the second operand is typechecked.
721+
713722
auto &binary_expr = to_binary_expr(expr);
714723

715724
ansi_c_declarationt &declaration = to_ansi_c_declaration(binary_expr.op0());
@@ -733,7 +742,7 @@ void c_typecheck_baset::typecheck_expr_operands(exprt &expr)
733742
if(s_it==symbol_table.symbols.end())
734743
{
735744
error().source_location = expr.source_location();
736-
error() << "failed to find decl symbol '" << identifier
745+
error() << "failed to find bound symbol `" << identifier
737746
<< "' in symbol table" << eom;
738747
throw 0;
739748
}

src/ansi-c/expr2c.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -791,20 +791,21 @@ std::string expr2ct::convert_trinary(
791791
}
792792

793793
std::string expr2ct::convert_quantifier(
794-
const exprt &src,
794+
const quantifier_exprt &src,
795795
const std::string &symbol,
796796
unsigned precedence)
797797
{
798-
if(src.operands().size()!=2)
798+
// our made-up syntax can only do one symbol
799+
if(src.op0().operands().size() != 1)
799800
return convert_norep(src, precedence);
800801

801802
unsigned p0, p1;
802803

803-
std::string op0=convert_with_precedence(src.op0(), p0);
804-
std::string op1=convert_with_precedence(src.op1(), p1);
804+
std::string op0 = convert_with_precedence(src.symbol(), p0);
805+
std::string op1 = convert_with_precedence(src.where(), p1);
805806

806807
std::string dest=symbol+" { ";
807-
dest+=convert(src.op0().type());
808+
dest += convert(src.symbol().type());
808809
dest+=" "+op0+"; ";
809810
dest+=op1;
810811
dest+=" }";
@@ -3720,13 +3721,16 @@ std::string expr2ct::convert_with_precedence(
37203721
return convert_trinary(to_if_expr(src), "?", ":", precedence = 3);
37213722

37223723
else if(src.id()==ID_forall)
3723-
return convert_quantifier(src, "forall", precedence=2);
3724+
return convert_quantifier(
3725+
to_quantifier_expr(src), "forall", precedence = 2);
37243726

37253727
else if(src.id()==ID_exists)
3726-
return convert_quantifier(src, "exists", precedence=2);
3728+
return convert_quantifier(
3729+
to_quantifier_expr(src), "exists", precedence = 2);
37273730

37283731
else if(src.id()==ID_lambda)
3729-
return convert_quantifier(src, "LAMBDA", precedence=2);
3732+
return convert_quantifier(
3733+
to_quantifier_expr(src), "LAMBDA", precedence = 2);
37303734

37313735
else if(src.id()==ID_with)
37323736
return convert_with(src, precedence=16);

src/ansi-c/expr2c_class.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ class expr2ct
128128
const exprt &src, unsigned &precedence);
129129

130130
std::string convert_quantifier(
131-
const exprt &src, const std::string &symbol,
131+
const quantifier_exprt &,
132+
const std::string &symbol,
132133
unsigned precedence);
133134

134135
std::string convert_with(

src/ansi-c/parser.y

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern char *yyansi_ctext;
2626

2727
#include "ansi_c_y.tab.h"
2828

29+
#include <util/mathematical_expr.h>
30+
2931
#ifdef _MSC_VER
3032
// possible loss of data
3133
#pragma warning(disable:4242)
@@ -469,15 +471,15 @@ quantifier_expression:
469471
{
470472
$$=$1;
471473
set($$, ID_forall);
472-
mto($$, $4);
474+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($4)) } ));
473475
mto($$, $5);
474476
PARSER.pop_scope();
475477
}
476478
| TOK_EXISTS compound_scope '{' declaration comma_expression '}'
477479
{
478480
$$=$1;
479481
set($$, ID_exists);
480-
mto($$, $4);
482+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($4)) } ));
481483
mto($$, $5);
482484
PARSER.pop_scope();
483485
}
@@ -810,15 +812,15 @@ ACSL_binding_expression:
810812
{
811813
$$=$1;
812814
set($$, ID_forall);
813-
mto($$, $3);
815+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($3)) } ));
814816
mto($$, $4);
815817
PARSER.pop_scope();
816818
}
817819
| TOK_ACSL_EXISTS compound_scope declaration ACSL_binding_expression
818820
{
819821
$$=$1;
820822
set($$, ID_exists);
821-
mto($$, $3);
823+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($3)) } ));
822824
mto($$, $4);
823825
PARSER.pop_scope();
824826
}

src/solvers/smt2/smt2_conv.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,7 @@ void smt2_convt::convert_expr(const exprt &expr)
18071807
else if(quantifier_expr.id() == ID_exists)
18081808
out << "(exists ";
18091809

1810-
exprt bound=expr.op0();
1810+
exprt bound = quantifier_expr.symbol();
18111811

18121812
out << "((";
18131813
convert_expr(bound);

src/solvers/smt2/smt2_parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ exprt smt2_parsert::quantifier_expression(irep_idt id)
283283
// go backwards, build quantified expression
284284
for(auto r_it=bindings.rbegin(); r_it!=bindings.rend(); r_it++)
285285
{
286-
binary_predicate_exprt quantifier(*r_it, id, result);
286+
quantifier_exprt quantifier(id, *r_it, result);
287287
result=quantifier;
288288
}
289289

src/util/mathematical_expr.h

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -313,35 +313,35 @@ class binding_exprt : public binary_exprt
313313
};
314314

315315
/// \brief A base class for quantifier expressions
316-
class quantifier_exprt : public binary_predicate_exprt
316+
class quantifier_exprt : public binding_exprt
317317
{
318318
public:
319-
quantifier_exprt(
320-
const irep_idt &_id,
321-
const symbol_exprt &_symbol,
322-
const exprt &_where)
323-
: binary_predicate_exprt(_symbol, _id, _where)
324-
{
325-
}
326-
327-
symbol_exprt &symbol()
319+
/// constructor for single variable
320+
quantifier_exprt(irep_idt _id, symbol_exprt _symbol, exprt _where)
321+
: binding_exprt(_id, {std::move(_symbol)}, std::move(_where), bool_typet())
328322
{
329-
return static_cast<symbol_exprt &>(op0());
330323
}
331324

332-
const symbol_exprt &symbol() const
325+
/// constructor for multiple variables
326+
quantifier_exprt(irep_idt _id, const variablest &_variables, exprt _where)
327+
: binding_exprt(_id, _variables, std::move(_where), bool_typet())
333328
{
334-
return static_cast<const symbol_exprt &>(op0());
335329
}
336330

337-
exprt &where()
331+
// for the special case of one variable
332+
symbol_exprt &symbol()
338333
{
339-
return op1();
334+
auto &variables = this->variables();
335+
PRECONDITION(variables.size() == 1);
336+
return variables.front();
340337
}
341338

342-
const exprt &where() const
339+
// for the special case of one variable
340+
const symbol_exprt &symbol() const
343341
{
344-
return op1();
342+
auto &variables = this->variables();
343+
PRECONDITION(variables.size() == 1);
344+
return variables.front();
345345
}
346346
};
347347

@@ -354,8 +354,9 @@ inline bool can_cast_expr<quantifier_exprt>(const exprt &base)
354354
inline void validate_expr(const quantifier_exprt &value)
355355
{
356356
validate_operands(value, 2, "quantifier expressions must have two operands");
357-
DATA_INVARIANT(
358-
value.op0().id() == ID_symbol, "quantified variable shall be a symbol");
357+
for(auto &op : value.variables())
358+
DATA_INVARIANT(
359+
op.id() == ID_symbol, "quantified variable shall be a symbol");
359360
}
360361

361362
/// \brief Cast an exprt to a \ref quantifier_exprt

0 commit comments

Comments
 (0)