Skip to content

Commit c4f7a89

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 c4f7a89

File tree

7 files changed

+99
-77
lines changed

7 files changed

+99
-77
lines changed

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -290,30 +290,37 @@ 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 declarations,
295+
// op1 is the bound expression
296296
auto &binary_expr = to_binary_expr(expr);
297+
auto &bindings = binary_expr.op0().operands();
298+
auto &where = binary_expr.op1();
297299

298-
if(binary_expr.op0().get(ID_statement) != ID_decl)
300+
for(const auto &binding : bindings)
299301
{
300-
error().source_location = expr.source_location();
301-
error() << "expected declaration as operand of quantifier" << eom;
302-
throw 0;
302+
if(binding.get(ID_statement) != ID_decl)
303+
{
304+
error().source_location = expr.source_location();
305+
error() << "expected declaration as operand of quantifier" << eom;
306+
throw 0;
307+
}
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+
for(auto &binding : bindings)
321+
binding = to_code_decl(to_code(binding)).symbol();
322+
323+
implicit_typecast_bool(where);
317324
}
318325
else if(expr.id()==ID_label)
319326
{
@@ -710,48 +717,55 @@ void c_typecheck_baset::typecheck_expr_operands(exprt &expr)
710717
}
711718
else if(expr.id()==ID_forall || expr.id()==ID_exists)
712719
{
720+
// These introduce new symbols, which need to be added to the symbol table
721+
// before the second operand is typechecked.
722+
713723
auto &binary_expr = to_binary_expr(expr);
724+
auto &bindings = binary_expr.op0().operands();
714725

715-
ansi_c_declarationt &declaration = to_ansi_c_declaration(binary_expr.op0());
726+
for(auto &binding : bindings)
727+
{
728+
ansi_c_declarationt &declaration = to_ansi_c_declaration(binding);
716729

717-
typecheck_declaration(declaration);
730+
typecheck_declaration(declaration);
718731

719-
if(declaration.declarators().size()!=1)
720-
{
721-
error().source_location = expr.source_location();
722-
error() << "expected one declarator exactly" << eom;
723-
throw 0;
724-
}
732+
if(declaration.declarators().size() != 1)
733+
{
734+
error().source_location = expr.source_location();
735+
error() << "forall/exists expects one declarator exactly" << eom;
736+
throw 0;
737+
}
725738

726-
irep_idt identifier=
727-
declaration.declarators().front().get_name();
739+
irep_idt identifier = declaration.declarators().front().get_name();
728740

729-
// look it up
730-
symbol_tablet::symbolst::const_iterator s_it=
731-
symbol_table.symbols.find(identifier);
741+
// look it up
742+
symbol_tablet::symbolst::const_iterator s_it =
743+
symbol_table.symbols.find(identifier);
732744

733-
if(s_it==symbol_table.symbols.end())
734-
{
735-
error().source_location = expr.source_location();
736-
error() << "failed to find decl symbol '" << identifier
737-
<< "' in symbol table" << eom;
738-
throw 0;
739-
}
745+
if(s_it == symbol_table.symbols.end())
746+
{
747+
error().source_location = expr.source_location();
748+
error() << "failed to find bound symbol `" << identifier
749+
<< "' in symbol table" << eom;
750+
throw 0;
751+
}
740752

741-
const symbolt &symbol=s_it->second;
753+
const symbolt &symbol = s_it->second;
742754

743-
if(symbol.is_type || symbol.is_extern || symbol.is_static_lifetime ||
744-
!is_complete_type(symbol.type) || symbol.type.id()==ID_code)
745-
{
746-
error().source_location = expr.source_location();
747-
error() << "unexpected quantified symbol" << eom;
748-
throw 0;
749-
}
755+
if(
756+
symbol.is_type || symbol.is_extern || symbol.is_static_lifetime ||
757+
!is_complete_type(symbol.type) || symbol.type.id() == ID_code)
758+
{
759+
error().source_location = expr.source_location();
760+
error() << "unexpected quantified symbol" << eom;
761+
throw 0;
762+
}
750763

751-
code_declt decl(symbol.symbol_expr());
752-
decl.add_source_location()=declaration.source_location();
764+
code_declt decl(symbol.symbol_expr());
765+
decl.add_source_location() = declaration.source_location();
753766

754-
binary_expr.op0() = decl;
767+
binding = decl;
768+
}
755769

756770
typecheck_expr(binary_expr.op1());
757771
}

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)