Skip to content

Commit

Permalink
[clang][Sema] Don't issue -Wcast-function-type-mismatch for enums wit…
Browse files Browse the repository at this point in the history
…h 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.
  • Loading branch information
tambry committed Jun 3, 2024
1 parent a088c61 commit 2bc098b
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 3 deletions.
7 changes: 4 additions & 3 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1093,9 +1093,10 @@ static bool argTypeIsABIEquivalent(QualType SrcType, QualType DestType,
return true;

// Allow integral type mismatch if their size are equal.
if (SrcType->isIntegralType(Context) && DestType->isIntegralType(Context))
if (Context.getTypeInfoInChars(SrcType).Width ==
Context.getTypeInfoInChars(DestType).Width)
if ((SrcType->isIntegralType(Context) || SrcType->isEnumeralType()) &&
(DestType->isIntegralType(Context) || DestType->isEnumeralType()))
if (Context.getTypeSizeInChars(SrcType) ==
Context.getTypeSizeInChars(DestType))
return true;

return Context.hasSameUnqualifiedType(SrcType, DestType);
Expand Down
9 changes: 9 additions & 0 deletions clang/test/Sema/warn-cast-function-type-strict.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,17 @@ f8 *h;
f9 *i;
f10 *j;

enum E : long;
int efunc(enum E);

// Produce the underlying `long` type implicitly.
enum E2 { big = __LONG_MAX__ };
int e2func(enum E2);

void foo(void) {
a = (f1 *)x;
a = (f1 *)efunc; // strict-warning {{cast from 'int (*)(enum E)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
a = (f1 *)e2func; // strict-warning {{cast from 'int (*)(enum E2)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}} */
c = (f3 *)x; /* strict-warning {{cast from 'int (*)(long)' to 'f3 *' (aka 'int (*)()') converts to incompatible function type}} */
d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function type}} */
Expand Down
9 changes: 9 additions & 0 deletions clang/test/Sema/warn-cast-function-type.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,17 @@ f5 *e;
f6 *f;
f7 *g;

enum E : long;
int efunc(enum E);

// Produce the underlying `long` type implicitly.
enum E2 { big = __LONG_MAX__ };
int e2func(enum E2);

void foo(void) {
a = (f1 *)x;
a = (f1 *)efunc; // enum is just type system sugar, still passed as a long.
a = (f1 *)e2func; // enum is just type system sugar, still passed as a long.
b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}} */
c = (f3 *)x;
d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function type}} */
Expand Down
9 changes: 9 additions & 0 deletions clang/test/SemaCXX/warn-cast-function-type-strict.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,17 @@ struct S

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

enum E : long;
int efunc(E);

// Produce the underlying `long` type implicitly.
enum E2 { big = __LONG_MAX__ };
int e2func(E2);

void foo() {
a = (f1 *)x;
a = (f1 *)efunc; // strict-warning {{cast from 'int (*)(E)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
a = (f1 *)e2func; // strict-warning {{cast from 'int (*)(E2)' to 'f1 *' (aka 'int (*)(long)') converts to incompatible function type}}
b = (f2 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
b = reinterpret_cast<f2 *>(x); // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
c = (f3 *)x; // strict-warning {{cast from 'int (*)(long)' to 'f3 *' (aka 'int (*)(...)') converts to incompatible function type}}
Expand Down
9 changes: 9 additions & 0 deletions clang/test/SemaCXX/warn-cast-function-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@ struct S

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

enum E : long;
int efunc(E);

// Produce the underlying `long` type implicitly.
enum E2 { big = __LONG_MAX__ };
int e2func(E2);

void foo() {
a = (f1 *)x;
a = (f1 *)efunc; // enum is just type system sugar, still passed as a long.
a = (f1 *)e2func; // enum is just type system sugar, still passed as a long.
b = (f2 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
b = reinterpret_cast<f2 *>(x); // expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}}
c = (f3 *)x;
Expand Down

0 comments on commit 2bc098b

Please sign in to comment.