Skip to content

Commit a0f1236

Browse files
committed
Implement enum_is_in_range builtin function
This adds a new builtin function that allows user to explicitly check that an enum has a valid value (and also this function in an assert).
1 parent 4cc9611 commit a0f1236

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2841,6 +2841,33 @@ exprt c_typecheck_baset::do_special_functions(
28412841
overflow.type() = bool_typet{};
28422842
return overflow;
28432843
}
2844+
else if(identifier == CPROVER_PREFIX "enum_is_in_range")
2845+
{
2846+
// Check correct number of arguments
2847+
if(expr.arguments().size() != 1)
2848+
{
2849+
std::ostringstream error_message;
2850+
error_message << expr.source_location().as_string() << ": " << identifier
2851+
<< " takes exactly 1 argument, but "
2852+
<< expr.arguments().size() << " were provided";
2853+
throw invalid_source_file_exceptiont{error_message.str()};
2854+
}
2855+
auto arg1 = expr.arguments()[0];
2856+
typecheck_expr(arg1);
2857+
if(!can_cast_type<c_enum_tag_typet>(arg1.type()))
2858+
{
2859+
// Can't enum range check a non-enum
2860+
std::ostringstream error_message;
2861+
error_message << expr.source_location().as_string() << ": " << identifier
2862+
<< " expects enum, but (" << expr2c(arg1, *this)
2863+
<< ") has type `" << type2c(arg1.type(), *this) << '`';
2864+
throw invalid_source_file_exceptiont{error_message.str()};
2865+
}
2866+
else
2867+
{
2868+
return expr;
2869+
}
2870+
}
28442871
else if(
28452872
identifier == "__builtin_add_overflow" ||
28462873
identifier == "__builtin_sub_overflow" ||

src/ansi-c/cprover_builtin_headers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,6 @@ __CPROVER_bool __CPROVER_overflow_mult();
103103
__CPROVER_bool __CPROVER_overflow_plus();
104104
__CPROVER_bool __CPROVER_overflow_shl();
105105
__CPROVER_bool __CPROVER_overflow_unary_minus();
106+
107+
// enumerations
108+
__CPROVER_bool __CPROVER_enum_is_in_range();

src/goto-programs/builtin_functions.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,32 @@ exprt make_va_list(const exprt &expr)
610610
return result;
611611
}
612612

613+
void goto_convertt::do_enum_is_in_range(
614+
const exprt &lhs,
615+
const symbol_exprt &function,
616+
const exprt::operandst &arguments,
617+
goto_programt &dest)
618+
{
619+
PRECONDITION(arguments.size() == 1);
620+
const auto enum_expr = arguments.front();
621+
const auto enum_type =
622+
type_try_dynamic_cast<c_enum_tag_typet>(enum_expr.type());
623+
PRECONDITION(enum_type);
624+
const c_enum_typet &c_enum_type = ns.follow_tag(*enum_type);
625+
const c_enum_typet::memberst enum_values = c_enum_type.members();
626+
627+
exprt::operandst disjuncts;
628+
for(const auto &enum_value : enum_values)
629+
{
630+
constant_exprt val{enum_value.get_value(), *enum_type};
631+
disjuncts.push_back(equal_exprt(enum_expr, std::move(val)));
632+
}
633+
634+
code_assignt assignment(lhs, disjunction(disjuncts));
635+
assignment.add_source_location() = function.source_location();
636+
copy(assignment, ASSIGN, dest);
637+
}
638+
613639
/// add function calls to function queue for later processing
614640
void goto_convertt::do_function_call_symbol(
615641
const exprt &lhs,
@@ -734,6 +760,10 @@ void goto_convertt::do_function_call_symbol(
734760
throw 0;
735761
}
736762
}
763+
else if(identifier == CPROVER_PREFIX "enum_is_in_range")
764+
{
765+
do_enum_is_in_range(lhs, function, arguments, dest);
766+
}
737767
else if(
738768
identifier == CPROVER_PREFIX "assert" ||
739769
identifier == CPROVER_PREFIX "precondition" ||

src/goto-programs/goto_convert_class.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,26 @@ class goto_convertt:public messaget
588588
bool get_string_constant(const exprt &expr, irep_idt &);
589589
exprt get_constant(const exprt &expr);
590590

591+
/// \brief Converts calls to the built in `enum_is_in_range` function to a
592+
/// test that the given enum value is in the valid range of values for that
593+
/// enum type.
594+
///
595+
/// Note that the check for the range of values is done by creating a
596+
/// disjunction comparing the expression with each possible valid value.
597+
/// \param lhs: The destination for the generated assignment.
598+
/// \param function: The `enum_is_in_range` symbol of the source function call.
599+
/// \param arguments: A collection of arguments, which is expected to contain a
600+
/// single argument which is an expression that resolves to a value of
601+
/// enum type. The arguments are expected to have been prevalidated by the
602+
/// typechecking process.
603+
/// \param dest: The goto program into which the generated assignment is
604+
/// copied.
605+
void do_enum_is_in_range(
606+
const exprt &lhs,
607+
const symbol_exprt &function,
608+
const exprt::operandst &arguments,
609+
goto_programt &dest);
610+
591611
// some built-in functions
592612
void do_atomic_begin(
593613
const exprt &lhs,

0 commit comments

Comments
 (0)