Skip to content

Commit 43eb3fd

Browse files
committed
SMV: set type
This fixes typechecking for set literals, assignments from set literals to variables of the element type, and typechecking for the "in" set membership operator.
1 parent b0016ca commit 43eb3fd

File tree

8 files changed

+154
-44
lines changed

8 files changed

+154
-44
lines changed
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
KNOWNBUG
1+
CORE broken-smt-backend
22
smv_set2.smv
3-
--bdd
4-
^\[spec1\] x in my_set: PROVED$
5-
^EXIT=0$
3+
4+
^\[spec1\] x in \{ 1, 2 \}: REFUTED$
5+
^\[spec2\] x in 1 \| x in 2: REFUTED$
6+
^EXIT=10$
67
^SIGNAL=0$
78
--
89
^warning: ignoring
910
--
10-
The smv_setin operator is not implemented.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
MODULE main
22

3-
DEFINE my_set := {1, 2};
4-
53
VAR x : 1..3;
64
ASSIGN init(x) := {1, 2, 3};
75
next(x) := x;
86

9-
SPEC x in my_set;
7+
SPEC x in {1, 2};
8+
9+
-- the rhs set can be a singleton
10+
SPEC (x in 1) | (x in 2);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
KNOWNBUG
2+
smv_set3.smv
3+
--bdd
4+
^\[spec1\] x in my_set: PROVED$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring
9+
--
10+
The smv_setin operator does not work with symbols on the RHS.

src/hw_cbmc_irep_ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ IREP_ID_ONE(smv_next)
2323
IREP_ID_ONE(smv_iff)
2424
IREP_ID_TWO(C_smv_iff, "#smv_iff")
2525
IREP_ID_ONE(smv_resize)
26+
IREP_ID_ONE(smv_set)
2627
IREP_ID_ONE(smv_setin)
2728
IREP_ID_ONE(smv_setnotin)
2829
IREP_ID_ONE(smv_signed_cast)

src/smvlang/expr2smv.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,9 @@ expr2smvt::resultt expr2smvt::convert_rec(const exprt &src)
656656
else if(src.id() == ID_mod)
657657
return convert_binary(to_mod_expr(src), src.id_string(), precedencet::MULT);
658658

659+
else if(src.id() == ID_smv_set)
660+
return convert_nondet_choice(src);
661+
659662
else if(src.id() == ID_smv_setin)
660663
return convert_binary(to_binary_expr(src), "in", precedencet::IN);
661664

@@ -791,9 +794,6 @@ expr2smvt::resultt expr2smvt::convert_rec(const exprt &src)
791794
else if(src.id()=="smv_nondet_choice")
792795
return convert_nondet_choice(src);
793796

794-
else if(src.id() == ID_constraint_select_one)
795-
return convert_nondet_choice(src);
796-
797797
else if(src.id()==ID_nondet_bool)
798798
{
799799
exprt nondet_choice_expr("smv_nondet_choice");
@@ -902,6 +902,10 @@ std::string type2smv(const typet &type, const namespacet &ns)
902902
{
903903
return type.get_string(ID_from) + ".." + type.get_string(ID_to);
904904
}
905+
else if(type.id() == ID_smv_set)
906+
{
907+
return "set";
908+
}
905909
else if(type.id()=="submodule")
906910
{
907911
auto code = type.get_string(ID_identifier);

src/smvlang/parser.y

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ static void new_module(YYSTYPE &module)
176176
%token LTLWFF_Token "LTLWFF"
177177
%token PSLWFF_Token "PSLWFF"
178178
%token COMPWFF_Token "COMPWFF"
179-
%token IN_Token "IN"
180179
%token MIN_Token "MIN"
181180
%token MAX_Token "MAX"
182181
%token MIRROR_Token "MIRROR"
@@ -289,7 +288,7 @@ static void new_module(YYSTYPE &module)
289288
%left EX_Token AX_Token EF_Token AF_Token EG_Token AG_Token E_Token A_Token U_Token R_Token V_Token F_Token G_Token H_Token O_Token S_Token T_Token X_Token Y_Token Z_Token EBF_Token ABF_Token EBG_Token ABG_Token
290289
%left EQUAL_Token NOTEQUAL_Token LT_Token GT_Token LE_Token GE_Token
291290
%left union_Token
292-
%left IN_Token NOTIN_Token
291+
%left in_Token
293292
%left mod_Token /* Precedence from CMU SMV, different from NuSMV */
294293
%left LTLT_Token GTGT_Token
295294
%left PLUS_Token MINUS_Token
@@ -673,7 +672,7 @@ formula : term
673672
term : variable_identifier
674673
| next_Token '(' term ')' { init($$, ID_smv_next); mto($$, $3); }
675674
| '(' formula ')' { $$=$2; }
676-
| '{' formula_list '}' { $$=$2; stack_expr($$).id("smv_nondet_choice"); }
675+
| '{' formula_list '}' { $$=$2; stack_expr($$).id(ID_smv_set); }
677676
| INC_Token '(' term ')' { init($$, "inc"); mto($$, $3); }
678677
| DEC_Token '(' term ')' { init($$, "dec"); mto($$, $3); }
679678
| ADD_Token '(' term ',' term ')' { j_binary($$, $3, ID_plus, $5); }
@@ -709,8 +708,7 @@ term : variable_identifier
709708
| term GT_Token term { binary($$, $1, ID_gt, $3); }
710709
| term GE_Token term { binary($$, $1, ID_ge, $3); }
711710
| term union_Token term { binary($$, $1, ID_smv_union, $3); }
712-
| term IN_Token term { binary($$, $1, ID_smv_setin, $3); }
713-
| term NOTIN_Token term { binary($$, $1, ID_smv_setnotin, $3); }
711+
| term in_Token term { binary($$, $1, ID_smv_setin, $3); }
714712
| extend_Token '(' term ',' term ')' { binary($$, $3, ID_smv_extend, $5); }
715713
| resize_Token '(' term ',' term ')' { binary($$, $3, ID_smv_resize, $5); }
716714
| signed_Token '(' term ')' { init($$, ID_smv_signed_cast); mto($$, $3); }

src/smvlang/scanner.l

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ void newlocation(YYSTYPE &x)
101101
"LTLWFF" token(LTLWFF_Token);
102102
"PSLWFF" token(PSLWFF_Token);
103103
"COMPWFF" token(COMPWFF_Token);
104-
"IN" token(IN_Token);
105104
"MIN" token(MIN_Token);
106105
"MAX" token(MAX_Token);
107106
"MIRROR" token(MIRROR_Token);

src/smvlang/smv_typecheck.cpp

Lines changed: 124 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,35 @@ void smv_typecheckt::typecheck_expr_rec(exprt &expr, modet mode)
10011001
}
10021002
else if(expr.id() == ID_smv_setin)
10031003
{
1004-
expr.type()=bool_typet();
1004+
auto &lhs = to_binary_expr(expr).lhs();
1005+
auto &rhs = to_binary_expr(expr).rhs();
1006+
1007+
typecheck_expr_rec(lhs, mode);
1008+
typecheck_expr_rec(rhs, mode);
1009+
1010+
// The RHS can be a set or a singleton
1011+
if(rhs.type().id() == ID_smv_set)
1012+
{
1013+
PRECONDITION(rhs.id() == ID_smv_set);
1014+
// do type union
1015+
typet op_type = lhs.type();
1016+
for(auto &op : rhs.operands())
1017+
op_type = type_union(op_type, op.type(), expr.source_location());
1018+
// now convert
1019+
convert_expr_to(lhs, op_type);
1020+
for(auto &op : rhs.operands())
1021+
convert_expr_to(op, op_type);
1022+
}
1023+
else
1024+
{
1025+
typet op_type =
1026+
type_union(lhs.type(), rhs.type(), expr.source_location());
1027+
1028+
convert_expr_to(lhs, op_type);
1029+
convert_expr_to(rhs, op_type);
1030+
}
1031+
1032+
expr.type() = bool_typet();
10051033
}
10061034
else if(expr.id() == ID_smv_setnotin)
10071035
{
@@ -1232,6 +1260,14 @@ void smv_typecheckt::typecheck_expr_rec(exprt &expr, modet mode)
12321260
<< "signed operand must have unsigned word type";
12331261
}
12341262
}
1263+
else if(expr.id() == ID_smv_set)
1264+
{
1265+
// a set literal
1266+
expr.type() = typet{ID_smv_set};
1267+
1268+
for(auto &op : expr.operands())
1269+
typecheck_expr_rec(op, mode);
1270+
}
12351271
else
12361272
{
12371273
throw errort().with_location(expr.find_source_location())
@@ -1271,6 +1307,33 @@ void smv_typecheckt::lower_node(exprt &expr) const
12711307
{
12721308
expr = typecast_exprt{to_smv_unsigned_cast_expr(expr).op(), expr.type()};
12731309
}
1310+
else if(expr.id() == ID_smv_setin)
1311+
{
1312+
auto &lhs = to_binary_expr(expr).lhs();
1313+
auto &rhs = to_binary_expr(expr).rhs();
1314+
if(rhs.type().id() == ID_smv_set)
1315+
{
1316+
DATA_INVARIANT(
1317+
rhs.id() == ID_smv_set, "rhs of in must be set expression");
1318+
// this is an 'or'
1319+
exprt::operandst disjuncts;
1320+
disjuncts.reserve(rhs.operands().size());
1321+
for(auto &op : rhs.operands())
1322+
{
1323+
DATA_INVARIANT(
1324+
lhs.type() == op.type(), "lhs/rhs of in must have same type");
1325+
disjuncts.push_back(equal_exprt{lhs, op});
1326+
}
1327+
expr = disjunction(std::move(disjuncts));
1328+
}
1329+
else
1330+
{
1331+
// RHS is a singleton; this is equality
1332+
expr.id(ID_equal);
1333+
DATA_INVARIANT(
1334+
lhs.type() == rhs.type(), "lhs/rhs of in must have same type");
1335+
}
1336+
}
12741337
}
12751338

12761339
/*******************************************************************\
@@ -1285,28 +1348,50 @@ Function: smv_typecheckt::convert_expr_to
12851348
12861349
\*******************************************************************/
12871350

1288-
void smv_typecheckt::convert_expr_to(exprt &expr, const typet &type)
1351+
void smv_typecheckt::convert_expr_to(exprt &expr, const typet &dest_type)
12891352
{
1290-
PRECONDITION(type.is_not_nil());
1353+
const auto &src_type = expr.type();
12911354

1292-
if(expr.type() != type)
1355+
PRECONDITION(dest_type.is_not_nil());
1356+
1357+
if(src_type != dest_type)
12931358
{
1294-
if(type.id() == ID_signedbv || type.id() == ID_unsignedbv)
1359+
if(src_type.id() == ID_smv_set && expr.id() == ID_smv_set)
1360+
{
1361+
// sets can be assigned to scalars, which yields a nondeterministic
1362+
// choice
1363+
std::string identifier =
1364+
module + "::var::" + std::to_string(nondet_count++);
1365+
1366+
expr.set(ID_identifier, identifier);
1367+
expr.set("#smv_nondet_choice", true);
1368+
1369+
expr.id(ID_constraint_select_one);
1370+
expr.type() = dest_type;
1371+
1372+
// apply recursively
1373+
for(auto &op : expr.operands())
1374+
convert_expr_to(op, dest_type);
1375+
1376+
return;
1377+
}
1378+
1379+
if(dest_type.id() == ID_signedbv || dest_type.id() == ID_unsignedbv)
12951380
{
12961381
// no implicit conversion
12971382
}
1298-
else if(type.id() == ID_range)
1383+
else if(dest_type.id() == ID_range)
12991384
{
1300-
if(expr.type().id() == ID_range)
1385+
if(src_type.id() == ID_range)
13011386
{
13021387
// range to range
13031388
if(expr.id() == ID_constant)
13041389
{
13051390
// re-type the constant
13061391
auto value = numeric_cast_v<mp_integer>(to_constant_expr(expr));
1307-
if(to_range_type(type).includes(value))
1392+
if(to_range_type(dest_type).includes(value))
13081393
{
1309-
expr = from_integer(value, type);
1394+
expr = from_integer(value, dest_type);
13101395
return;
13111396
}
13121397
}
@@ -1318,26 +1403,40 @@ void smv_typecheckt::convert_expr_to(exprt &expr, const typet &type)
13181403
for(auto &op : expr.operands())
13191404
{
13201405
if(!condition)
1321-
convert_expr_to(op, type);
1406+
convert_expr_to(op, dest_type);
13221407

13231408
condition = !condition;
13241409
}
1325-
expr.type() = type;
1410+
expr.type() = dest_type;
13261411
return;
13271412
}
13281413
else
13291414
{
1330-
expr = typecast_exprt{expr, type};
1415+
expr = typecast_exprt{expr, dest_type};
13311416
return;
13321417
}
13331418
}
1419+
else if(src_type.id() == ID_integer)
1420+
{
1421+
// integer to range
1422+
if(expr.id() == ID_constant)
1423+
{
1424+
// re-type the constant
1425+
auto value = numeric_cast_v<mp_integer>(to_constant_expr(expr));
1426+
if(to_range_type(dest_type).includes(value))
1427+
{
1428+
expr = from_integer(value, dest_type);
1429+
return;
1430+
}
1431+
}
1432+
}
13341433
}
1335-
else if(type.id() == ID_bool)
1434+
else if(dest_type.id() == ID_bool)
13361435
{
13371436
// legacy -- convert 0/1 to false/true
1338-
if(expr.type().id() == ID_range)
1437+
if(src_type.id() == ID_range)
13391438
{
1340-
auto &range_type = to_range_type(expr.type());
1439+
auto &range_type = to_range_type(src_type);
13411440
if(range_type.get_from() == 0 && range_type.get_to() == 0)
13421441
{
13431442
expr = false_exprt{};
@@ -1350,43 +1449,41 @@ void smv_typecheckt::convert_expr_to(exprt &expr, const typet &type)
13501449
}
13511450
}
13521451
}
1353-
else if(type.id() == ID_enumeration)
1452+
else if(dest_type.id() == ID_enumeration)
13541453
{
1355-
auto &e_type = to_enumeration_type(type);
1454+
auto &e_type = to_enumeration_type(dest_type);
13561455

1357-
if(expr.id() == ID_constant && expr.type().id() == ID_enumeration)
1456+
if(expr.id() == ID_constant && src_type.id() == ID_enumeration)
13581457
{
1359-
if(is_contained_in(to_enumeration_type(expr.type()), e_type))
1458+
if(is_contained_in(to_enumeration_type(src_type), e_type))
13601459
{
13611460
// re-type the constant
1362-
expr.type() = type;
1461+
expr.type() = dest_type;
13631462
return;
13641463
}
13651464
else
13661465
{
13671466
throw errort().with_location(expr.find_source_location())
13681467
<< "enum " << to_string(expr) << " not a member of "
1369-
<< to_string(type);
1468+
<< to_string(dest_type);
13701469
}
13711470
}
13721471
else if(expr.id() == ID_typecast)
13731472
{
13741473
// created by type unions
13751474
auto &op = to_typecast_expr(expr).op();
1376-
if(
1377-
expr.type().id() == ID_enumeration &&
1378-
op.type().id() == ID_enumeration)
1475+
if(src_type.id() == ID_enumeration && op.type().id() == ID_enumeration)
13791476
{
1380-
convert_expr_to(op, type);
1477+
convert_expr_to(op, dest_type);
13811478
expr = std::move(op);
13821479
}
13831480
}
13841481
}
13851482

13861483
throw errort().with_location(expr.find_source_location())
1387-
<< "Expected expression of type `" << to_string(type)
1484+
<< "Expected expression of type `" << to_string(dest_type)
13881485
<< "', but got expression `" << to_string(expr) << "', which is of type `"
1389-
<< to_string(expr.type()) << "'";
1486+
<< to_string(src_type) << "'";
13901487
}
13911488
}
13921489

0 commit comments

Comments
 (0)