Skip to content

Commit b3a6cc4

Browse files
authored
Merge pull request #5441 from danpoe/fixes/enum-underlying-types
Correctly interpret underlying type specification of enums
2 parents eefd06f + 5f3519a commit b3a6cc4

File tree

9 files changed

+137
-9
lines changed

9 files changed

+137
-9
lines changed

regression/ansi-c/enum9/test.desc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ main.c
33
--verbosity 2
44
^EXIT=0$
55
^SIGNAL=0$
6-
(main.c:1:\d+)|(main.c\(1\)): warning: ignoring specification of underlying type for enum
7-
(main.c:6:\d+)|(main.c\(6\)): warning: ignoring specification of underlying type for enum
86
(main.c:13:\d+)|(main.c\(13\)): warning: ignoring specification of underlying type for enum
97
(main.c:14:\d+)|(main.c\(14\)): warning: ignoring specification of underlying type for enum
108
(main.c:19:\d+)|(main.c\(19\)): warning: ignoring specification of underlying type for enum
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <assert.h>
2+
3+
enum enum1
4+
{
5+
E1
6+
};
7+
8+
enum enum2 : signed char
9+
{
10+
E2
11+
};
12+
13+
typedef signed char signed_char_t;
14+
15+
enum enum3 : signed_char_t
16+
{
17+
E3
18+
};
19+
20+
int main()
21+
{
22+
assert(sizeof(int) != sizeof(signed char));
23+
assert(sizeof(enum enum1) == sizeof(int));
24+
assert(sizeof(enum enum2) == sizeof(signed char));
25+
assert(sizeof(enum enum3) == sizeof(signed char));
26+
27+
enum enum2 e2 = 0xff;
28+
assert(e2 == -1);
29+
30+
enum enum3 e3 = 0xff;
31+
assert(e3 == -1);
32+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
Checks that the underlying type specification of an enum is correctly
11+
interpreted, i.e., the size and signedness of the integral type chosen for the
12+
enum is the one specified. Also checks that it works with typedef'd types.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <assert.h>
2+
3+
enum enum1 : unsigned char
4+
{
5+
E1 = 256
6+
};
7+
8+
int main()
9+
{
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=6$
5+
^SIGNAL=0$
6+
^file main.c line \d+: enumerator value is not representable in the underlying type 'unsigned_char'$
7+
--
8+
^warning: ignoring
9+
--
10+
Checks that values that cannot be represented by the specified underlying type
11+
are rejected
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <assert.h>
2+
3+
enum enum1 : float
4+
{
5+
E1
6+
};
7+
8+
int main()
9+
{
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=6$
5+
^SIGNAL=0$
6+
^file main.c line \d+: non-integral type 'float' is an invalid underlying type$
7+
--
8+
^warning: ignoring
9+
--
10+
Checks that non-integral types are rejected

src/ansi-c/c_typecheck_type.cpp

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,10 +1160,24 @@ void c_typecheck_baset::typecheck_c_enum_type(typet &type)
11601160
throw 0;
11611161
}
11621162

1163-
if(as_expr.find(ID_enum_underlying_type).is_not_nil())
1163+
const bool have_underlying_type =
1164+
type.find_type(ID_enum_underlying_type).is_not_nil();
1165+
1166+
if(have_underlying_type)
11641167
{
1165-
warning().source_location = source_location;
1166-
warning() << "ignoring specification of underlying type for enum" << eom;
1168+
typecheck_type(type.add_type(ID_enum_underlying_type));
1169+
1170+
const typet &underlying_type =
1171+
static_cast<const typet &>(type.find(ID_enum_underlying_type));
1172+
1173+
if(!is_signed_or_unsigned_bitvector(underlying_type))
1174+
{
1175+
std::ostringstream msg;
1176+
msg << source_location << ": non-integral type '"
1177+
<< underlying_type.get(ID_C_c_type)
1178+
<< "' is an invalid underlying type";
1179+
throw invalid_source_file_exceptiont{msg.str()};
1180+
}
11671181
}
11681182

11691183
// enums start at zero;
@@ -1211,8 +1225,27 @@ void c_typecheck_baset::typecheck_c_enum_type(typet &type)
12111225
if(value>max_value)
12121226
max_value=value;
12131227

1214-
typet constant_type=
1215-
enum_constant_type(min_value, max_value);
1228+
typet constant_type;
1229+
1230+
if(have_underlying_type)
1231+
{
1232+
constant_type = type.find_type(ID_enum_underlying_type);
1233+
const auto &tmp = to_integer_bitvector_type(constant_type);
1234+
1235+
if(value < tmp.smallest() || value > tmp.largest())
1236+
{
1237+
std::ostringstream msg;
1238+
msg
1239+
<< v.source_location()
1240+
<< ": enumerator value is not representable in the underlying type '"
1241+
<< constant_type.get(ID_C_c_type) << "'";
1242+
throw invalid_source_file_exceptiont{msg.str()};
1243+
}
1244+
}
1245+
else
1246+
{
1247+
constant_type = enum_constant_type(min_value, max_value);
1248+
}
12161249

12171250
v=from_integer(value, constant_type);
12181251

@@ -1245,8 +1278,17 @@ void c_typecheck_baset::typecheck_c_enum_type(typet &type)
12451278
bool is_packed=type.get_bool(ID_C_packed);
12461279

12471280
// We use a subtype to store the underlying type.
1248-
bitvector_typet underlying_type =
1249-
enum_underlying_type(min_value, max_value, is_packed);
1281+
bitvector_typet underlying_type(ID_nil);
1282+
1283+
if(have_underlying_type)
1284+
{
1285+
underlying_type =
1286+
to_bitvector_type(type.find_type(ID_enum_underlying_type));
1287+
}
1288+
else
1289+
{
1290+
underlying_type = enum_underlying_type(min_value, max_value, is_packed);
1291+
}
12501292

12511293
// Get the width to make the values have a bitvector type
12521294
std::size_t width = underlying_type.get_width();

src/ansi-c/parser.y

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,6 +1840,9 @@ enum_name:
18401840
basic_type_name_list:
18411841
basic_type_name
18421842
| basic_type_name_list basic_type_name
1843+
{
1844+
$$ = merge($1, $2);
1845+
}
18431846

18441847
enum_underlying_type:
18451848
basic_type_name_list

0 commit comments

Comments
 (0)