Skip to content

Commit 2bc098b

Browse files
authored
[clang][Sema] Don't issue -Wcast-function-type-mismatch for enums with a matching underlying type (#87793)
Enums are passed as their underlying integral type so they're ABI compatible if the size matches. Useful with C APIs that pass user-controlled values to callbacks that can be made type safe by using enumerations (e.g. GStreamer). Discovered internally in some code after 999d4f8.
1 parent a088c61 commit 2bc098b

File tree

5 files changed

+40
-3
lines changed

5 files changed

+40
-3
lines changed

clang/lib/Sema/SemaCast.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,9 +1093,10 @@ static bool argTypeIsABIEquivalent(QualType SrcType, QualType DestType,
10931093
return true;
10941094

10951095
// Allow integral type mismatch if their size are equal.
1096-
if (SrcType->isIntegralType(Context) && DestType->isIntegralType(Context))
1097-
if (Context.getTypeInfoInChars(SrcType).Width ==
1098-
Context.getTypeInfoInChars(DestType).Width)
1096+
if ((SrcType->isIntegralType(Context) || SrcType->isEnumeralType()) &&
1097+
(DestType->isIntegralType(Context) || DestType->isEnumeralType()))
1098+
if (Context.getTypeSizeInChars(SrcType) ==
1099+
Context.getTypeSizeInChars(DestType))
10991100
return true;
11001101

11011102
return Context.hasSameUnqualifiedType(SrcType, DestType);

clang/test/Sema/warn-cast-function-type-strict.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,17 @@ f8 *h;
2929
f9 *i;
3030
f10 *j;
3131

32+
enum E : long;
33+
int efunc(enum E);
34+
35+
// Produce the underlying `long` type implicitly.
36+
enum E2 { big = __LONG_MAX__ };
37+
int e2func(enum E2);
38+
3239
void foo(void) {
3340
a = (f1 *)x;
41+
a = (f1 *)efunc; // strict-warning {{cast from 'int (*)(enum E)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
42+
a = (f1 *)e2func; // strict-warning {{cast from 'int (*)(enum E2)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
3443
b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}} */
3544
c = (f3 *)x; /* strict-warning {{cast from 'int (*)(long)' to 'f3 *' (aka 'int (*)()') converts to incompatible function type}} */
3645
d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function type}} */

clang/test/Sema/warn-cast-function-type.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,17 @@ f5 *e;
1919
f6 *f;
2020
f7 *g;
2121

22+
enum E : long;
23+
int efunc(enum E);
24+
25+
// Produce the underlying `long` type implicitly.
26+
enum E2 { big = __LONG_MAX__ };
27+
int e2func(enum E2);
28+
2229
void foo(void) {
2330
a = (f1 *)x;
31+
a = (f1 *)efunc; // enum is just type system sugar, still passed as a long.
32+
a = (f1 *)e2func; // enum is just type system sugar, still passed as a long.
2433
b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}} */
2534
c = (f3 *)x;
2635
d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function type}} */

clang/test/SemaCXX/warn-cast-function-type-strict.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,17 @@ struct S
2929

3030
typedef void (S::*mf)(int);
3131

32+
enum E : long;
33+
int efunc(E);
34+
35+
// Produce the underlying `long` type implicitly.
36+
enum E2 { big = __LONG_MAX__ };
37+
int e2func(E2);
38+
3239
void foo() {
3340
a = (f1 *)x;
41+
a = (f1 *)efunc; // strict-warning {{cast from 'int (*)(E)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
42+
a = (f1 *)e2func; // strict-warning {{cast from 'int (*)(E2)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
3443
b = (f2 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
3544
b = reinterpret_cast<f2 *>(x); // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
3645
c = (f3 *)x; // strict-warning {{cast from 'int (*)(long)' to 'f3 *' (aka 'int (*)(...)') converts to incompatible function type}}

clang/test/SemaCXX/warn-cast-function-type.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,17 @@ struct S
2828

2929
typedef void (S::*mf)(int);
3030

31+
enum E : long;
32+
int efunc(E);
33+
34+
// Produce the underlying `long` type implicitly.
35+
enum E2 { big = __LONG_MAX__ };
36+
int e2func(E2);
37+
3138
void foo() {
3239
a = (f1 *)x;
40+
a = (f1 *)efunc; // enum is just type system sugar, still passed as a long.
41+
a = (f1 *)e2func; // enum is just type system sugar, still passed as a long.
3342
b = (f2 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
3443
b = reinterpret_cast<f2 *>(x); // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
3544
c = (f3 *)x;

0 commit comments

Comments
 (0)