Skip to content

Commit 4386617

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 439bf5b commit 4386617

File tree

6 files changed

+60
-42
lines changed

6 files changed

+60
-42
lines changed

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -292,30 +292,35 @@ void c_typecheck_baset::typecheck_expr_main(exprt &expr)
292292
else if(expr.id()==ID_forall ||
293293
expr.id()==ID_exists)
294294
{
295-
// op0 is a declaration,
296-
// op1 the bound expression
297-
assert(expr.operands().size()==2);
298-
expr.type()=bool_typet();
295+
// These have two operands.
296+
// op0 is a tuple with one declaration,
297+
// op1 is the bound expression
298+
auto &binary_expr = to_binary_expr(expr);
299+
auto &binding = binary_expr.op0().operands();
300+
auto &where = binary_expr.op1();
301+
302+
DATA_INVARIANT(binding.size() == 1, "we expect one declaration in forall/exists");
299303

300-
if(expr.op0().get(ID_statement)!=ID_decl)
304+
if(binding.front().get(ID_statement) != ID_decl)
301305
{
302306
error().source_location = expr.source_location();
303307
error() << "expected declaration as operand of quantifier" << eom;
304308
throw 0;
305309
}
306310

307-
if(has_subexpr(expr.op1(), ID_side_effect))
311+
if(has_subexpr(where, ID_side_effect))
308312
{
309313
error().source_location = expr.source_location();
310314
error() << "quantifier must not contain side effects" << eom;
311315
throw 0;
312316
}
313317

314-
// replace declaration by symbol expression
315-
symbol_exprt bound=to_symbol_expr(expr.op0().op0());
316-
expr.op0().swap(bound);
318+
expr.type() = bool_typet();
317319

318-
implicit_typecast_bool(expr.op1());
320+
// replace declarations by symbol expressions
321+
binding.front() = to_code_decl(to_code(binding.front())).symbol();
322+
323+
implicit_typecast_bool(binary_expr.op1());
319324
}
320325
else if(expr.id()==ID_label)
321326
{
@@ -738,10 +743,16 @@ void c_typecheck_baset::typecheck_expr_operands(exprt &expr)
738743
}
739744
else if(expr.id()==ID_forall || expr.id()==ID_exists)
740745
{
741-
assert(expr.operands().size()==2);
746+
// These introduce new symbols, which need to be added to the symbol table
747+
// before the second operand is typechecked.
748+
749+
DATA_INVARIANT(expr.operands().size() == 2, "forall/exists are binary");
750+
DATA_INVARIANT(
751+
to_binary_expr(expr).op0().operands().size() == 1,
752+
"forall/exists have one declaration as first operand");
742753

743-
ansi_c_declarationt &declaration=
744-
to_ansi_c_declaration(expr.op0());
754+
ansi_c_declarationt &declaration =
755+
to_ansi_c_declaration(to_binary_expr(expr).op0().op0());
745756

746757
typecheck_declaration(declaration);
747758

@@ -762,7 +773,7 @@ void c_typecheck_baset::typecheck_expr_operands(exprt &expr)
762773
if(s_it==symbol_table.symbols.end())
763774
{
764775
error().source_location = expr.source_location();
765-
error() << "failed to find decl symbol '" << identifier
776+
error() << "failed to find bound symbol `" << identifier
766777
<< "' in symbol table" << eom;
767778
throw 0;
768779
}
@@ -780,7 +791,7 @@ void c_typecheck_baset::typecheck_expr_operands(exprt &expr)
780791
code_declt decl(symbol.symbol_expr());
781792
decl.add_source_location()=declaration.source_location();
782793

783-
expr.op0()=decl;
794+
expr.op0().op0() = decl;
784795

785796
typecheck_expr(expr.op1());
786797
}

src/ansi-c/expr2c.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -798,13 +798,17 @@ std::string expr2ct::convert_quantifier(
798798
if(src.operands().size()!=2)
799799
return convert_norep(src, precedence);
800800

801+
// our made-up syntax can only do one symbol
802+
if(src.op0().operands().size() != 1)
803+
return convert_norep(src, precedence);
804+
801805
unsigned p0, p1;
802806

803-
std::string op0=convert_with_precedence(src.op0(), p0);
807+
std::string op0 = convert_with_precedence(src.op0().op0(), p0);
804808
std::string op1=convert_with_precedence(src.op1(), p1);
805809

806810
std::string dest=symbol+" { ";
807-
dest+=convert(src.op0().type());
811+
dest += convert(src.op0().op0().type());
808812
dest+=" "+op0+"; ";
809813
dest+=op1;
810814
dest+=" }";

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)
@@ -468,15 +470,15 @@ quantifier_expression:
468470
{
469471
$$=$1;
470472
set($$, ID_forall);
471-
mto($$, $4);
473+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($4)) } ));
472474
mto($$, $5);
473475
PARSER.pop_scope();
474476
}
475477
| TOK_EXISTS compound_scope '{' declaration comma_expression '}'
476478
{
477479
$$=$1;
478480
set($$, ID_exists);
479-
mto($$, $4);
481+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($4)) } ));
480482
mto($$, $5);
481483
PARSER.pop_scope();
482484
}
@@ -806,15 +808,15 @@ ACSL_binding_expression:
806808
{
807809
$$=$1;
808810
set($$, ID_forall);
809-
mto($$, $3);
811+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($3)) } ));
810812
mto($$, $4);
811813
PARSER.pop_scope();
812814
}
813815
| TOK_ACSL_EXISTS compound_scope declaration ACSL_binding_expression
814816
{
815817
$$=$1;
816818
set($$, ID_exists);
817-
mto($$, $3);
819+
parser_stack($$).add_to_operands(tuple_exprt( { std::move(parser_stack($3)) } ));
818820
mto($$, $4);
819821
PARSER.pop_scope();
820822
}

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)