https://github.com/jxz-hw updated https://github.com/llvm/llvm-project/pull/203955
>From 58ff9c4017eb5749d4849f7936e849e3b1a7af9f Mon Sep 17 00:00:00 2001 From: jxz-hw <[email protected]> Date: Tue, 16 Jun 2026 01:27:47 +0800 Subject: [PATCH] [Clang] Suppress mismatch warning for same-size pointer/integer casts -Wcast-function-type-mismatch now considers pointer and integer types ABI-compatible when they have the same size and the target treats them as register-compatible. Specially,m68k overrides this because of its separate address and data registers. --- clang/include/clang/Basic/TargetInfo.h | 6 ++++ clang/lib/Basic/Targets/M68k.h | 1 + clang/lib/Sema/SemaCast.cpp | 23 ++++++++++--- .../Sema/warn-cast-function-type-ptr-int.c | 22 +++++++++++++ .../Sema/warn-cast-function-type-strict.c | 12 ++++++- clang/test/Sema/warn-cast-function-type.c | 32 ++++++++++++++++++- .../warn-cast-function-type-ptr-int.cpp | 28 ++++++++++++++++ .../warn-cast-function-type-strict.cpp | 18 +++++++++-- .../test/SemaCXX/warn-cast-function-type.cpp | 32 +++++++++++++++++-- 9 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 clang/test/Sema/warn-cast-function-type-ptr-int.c create mode 100644 clang/test/SemaCXX/warn-cast-function-type-ptr-int.cpp diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 968a0c1b129ef..2c7d964ad66e7 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1057,6 +1057,12 @@ class TargetInfo : public TransferrableTargetInfo, /// idea to avoid optimizing based on that undef behavior. virtual bool isCLZForZeroUndef() const { return true; } + /// Returns whether pointers and integers of the same size are considered + /// ABI-compatible on this target. Defaults to true; targets with separate + /// register files for pointers and integers (e.g., m68k) should override + /// to return false. + virtual bool arePointersAndIntegersABICompatible() const { return true; } + /// Returns the kind of __builtin_va_list type that should be used /// with this target. virtual BuiltinVaListKind getBuiltinVaListKind() const = 0; diff --git a/clang/lib/Basic/Targets/M68k.h b/clang/lib/Basic/Targets/M68k.h index 98add6a6221d5..41af0dd36a7ba 100644 --- a/clang/lib/Basic/Targets/M68k.h +++ b/clang/lib/Basic/Targets/M68k.h @@ -56,6 +56,7 @@ class LLVM_LIBRARY_VISIBILITY M68kTargetInfo : public TargetInfo { BuiltinVaListKind getBuiltinVaListKind() const override; bool setCPU(StringRef Name) override; CallingConvCheckResult checkCallingConvention(CallingConv CC) const override; + bool arePointersAndIntegersABICompatible() const override { return false; } std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override { return std::make_pair(32, 32); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 133837623a7a6..240e2b97848de 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1146,12 +1146,27 @@ static bool argTypeIsABIEquivalent(QualType SrcType, QualType DestType, if (SrcType->isPointerType() && DestType->isPointerType()) return true; + bool SrcIntOrEnum = + SrcType->isIntegralType(Context) || SrcType->isEnumeralType(); + bool DstIntOrEnum = + DestType->isIntegralType(Context) || DestType->isEnumeralType(); + // Allow integral type mismatch if their size are equal. - if ((SrcType->isIntegralType(Context) || SrcType->isEnumeralType()) && - (DestType->isIntegralType(Context) || DestType->isEnumeralType())) - if (Context.getTypeSizeInChars(SrcType) == - Context.getTypeSizeInChars(DestType)) + if (SrcIntOrEnum && DstIntOrEnum && + Context.getTypeSizeInChars(SrcType) == + Context.getTypeSizeInChars(DestType)) + return true; + + // Allow pointer/integral type mismatch if their sizes are equal and the + // target considers pointers and integers ABI-compatible. + if (Context.getTypeSizeInChars(SrcType) == + Context.getTypeSizeInChars(DestType) && + Context.getTargetInfo().arePointersAndIntegersABICompatible()) { + bool SrcPtr = SrcType->isPointerType(); + bool DstPtr = DestType->isPointerType(); + if ((SrcPtr && DstIntOrEnum) || (SrcIntOrEnum && DstPtr)) return true; + } return Context.hasSameUnqualifiedType(SrcType, DestType); } diff --git a/clang/test/Sema/warn-cast-function-type-ptr-int.c b/clang/test/Sema/warn-cast-function-type-ptr-int.c new file mode 100644 index 0000000000000..849a95a633a1d --- /dev/null +++ b/clang/test/Sema/warn-cast-function-type-ptr-int.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple arm-linux-gnueabi -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=arm %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=aarch64 %s +// RUN: %clang_cc1 -triple m68k-linux-gnu -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=m68k %s + +typedef void *(*ptr_ret)(void); +typedef unsigned long (*ul_ret)(void); +typedef int (*ptr_param)(void *); +typedef int (*ul_param)(unsigned long); + +ptr_ret pr; +ul_ret ur; +ptr_param pp; +ul_param up; + +void test(void) { + // arm-no-diagnostics + // aarch64-no-diagnostics + pr = (ptr_ret)ur; // m68k-warning {{cast from 'ul_ret' (aka 'unsigned long (*)(void)') to 'ptr_ret' (aka 'void *(*)(void)') converts to incompatible function type}} + ur = (ul_ret)pr; // m68k-warning {{cast from 'ptr_ret' (aka 'void *(*)(void)') to 'ul_ret' (aka 'unsigned long (*)(void)') converts to incompatible function type}} + pp = (ptr_param)up; // m68k-warning {{cast from 'ul_param' (aka 'int (*)(unsigned long)') to 'ptr_param' (aka 'int (*)(void *)') converts to incompatible function type}} + up = (ul_param)pp; // m68k-warning {{cast from 'ptr_param' (aka 'int (*)(void *)') to 'ul_param' (aka 'int (*)(unsigned long)') converts to incompatible function type}} +} diff --git a/clang/test/Sema/warn-cast-function-type-strict.c b/clang/test/Sema/warn-cast-function-type-strict.c index c43e0f2fcbc63..4a96491465be9 100644 --- a/clang/test/Sema/warn-cast-function-type-strict.c +++ b/clang/test/Sema/warn-cast-function-type-strict.c @@ -40,7 +40,7 @@ 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}} */ + b = (f2 *)x; /* strict-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}} */ e = (f5 *)x; /* strict-warning {{cast from 'int (*)(long)' to 'f5 *' (aka 'void (*)(void)') converts to incompatible function type}} */ @@ -51,3 +51,13 @@ void foo(void) { // FIXME: return type qualifier should not be included in the function type . Warning should be absent after this issue is fixed. https://github.com/llvm/llvm-project/issues/39494 . j = (f10 *)v; /* strict-warning {{cast from 'const int (*)(int)' to 'f10 *' (aka 'int (*)(int)') converts to incompatible function type}} */ } + +// Strict mode: pointer-vs-integral should still warn even when sizes match. +typedef void *(*ptr_ret_fn)(void); +typedef unsigned long (*ul_ret_fn)(void); +ptr_ret_fn pr2; +ul_ret_fn ur2; +void test_ptr_int_return_strict(void) { + pr2 = (ptr_ret_fn)ur2; /* strict-warning {{cast from 'ul_ret_fn' (aka 'unsigned long (*)(void)') to 'ptr_ret_fn' (aka 'void *(*)(void)') converts to incompatible function type}} */ + ur2 = (ul_ret_fn)pr2; /* strict-warning {{cast from 'ptr_ret_fn' (aka 'void *(*)(void)') to 'ul_ret_fn' (aka 'unsigned long (*)(void)') converts to incompatible function type}} */ +} diff --git a/clang/test/Sema/warn-cast-function-type.c b/clang/test/Sema/warn-cast-function-type.c index d474dbf48b897..514b73da38435 100644 --- a/clang/test/Sema/warn-cast-function-type.c +++ b/clang/test/Sema/warn-cast-function-type.c @@ -30,10 +30,40 @@ 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}} */ + b = (f2 *)x; // ABI-compatible: long and void* same size on this target. c = (f3 *)x; d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function type}} */ e = (f5 *)x; f = (f6 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function type}} */ g = (f7 *)x; } + +// Pointer-vs-integral return types: same size so ABI-compatible on default target. +typedef void *(*ptr_ret_fn)(void); +typedef unsigned long (*ul_ret_fn)(void); +ptr_ret_fn pr; +ul_ret_fn ur; +void test_ptr_int_return(void) { + pr = (ptr_ret_fn)ur; + ur = (ul_ret_fn)pr; +} + +// Pointer-vs-integral parameter types: same size so ABI-compatible. +typedef int (*ptr_param_fn)(void *); +typedef int (*ul_param_fn)(unsigned long); +ptr_param_fn pp; +ul_param_fn up; +void test_ptr_int_param(void) { + pp = (ptr_param_fn)up; + up = (ul_param_fn)pp; +} + +// Different sizes should still warn. +typedef short (*short_ret_fn)(void); +typedef void *(*ptr_ret_fn2)(void); +short_ret_fn sr; +ptr_ret_fn2 pr2; +void test_ptr_int_diff_size(void) { + pr2 = (ptr_ret_fn2)sr; /* expected-warning {{cast from 'short_ret_fn' (aka 'short (*)(void)') to 'ptr_ret_fn2' (aka 'void *(*)(void)') converts to incompatible function type}} */ + sr = (short_ret_fn)pr2; /* expected-warning {{cast from 'ptr_ret_fn2' (aka 'void *(*)(void)') to 'short_ret_fn' (aka 'short (*)(void)') converts to incompatible function type}} */ +} diff --git a/clang/test/SemaCXX/warn-cast-function-type-ptr-int.cpp b/clang/test/SemaCXX/warn-cast-function-type-ptr-int.cpp new file mode 100644 index 0000000000000..bfae0a45ad44e --- /dev/null +++ b/clang/test/SemaCXX/warn-cast-function-type-ptr-int.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple arm-linux-gnueabi -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=arm %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=aarch64 %s +// RUN: %clang_cc1 -triple m68k-linux-gnu -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=m68k %s + +// arm-no-diagnostics +// aarch64-no-diagnostics + +typedef void *(*ptr_ret)(void); +typedef unsigned long (*ul_ret)(void); +typedef int (*ptr_param)(void *); +typedef int (*ul_param)(unsigned long); + +ptr_ret pr; +ul_ret ur; +ptr_param pp; +ul_param up; + +void test() { + pr = (ptr_ret)ur; // m68k-warning {{cast from 'ul_ret' (aka 'unsigned long (*)()') to 'ptr_ret' (aka 'void *(*)()') converts to incompatible function type}} + ur = (ul_ret)pr; // m68k-warning {{cast from 'ptr_ret' (aka 'void *(*)()') to 'ul_ret' (aka 'unsigned long (*)()') converts to incompatible function type}} + pp = (ptr_param)up; // m68k-warning {{cast from 'ul_param' (aka 'int (*)(unsigned long)') to 'ptr_param' (aka 'int (*)(void *)') converts to incompatible function type}} + up = (ul_param)pp; // m68k-warning {{cast from 'ptr_param' (aka 'int (*)(void *)') to 'ul_param' (aka 'int (*)(unsigned long)') converts to incompatible function type}} +} + +void test_reinterpret_cast() { + pr = reinterpret_cast<ptr_ret>(ur); // m68k-warning {{cast from 'ul_ret' (aka 'unsigned long (*)()') to 'ptr_ret' (aka 'void *(*)()') converts to incompatible function type}} + ur = reinterpret_cast<ul_ret>(pr); // m68k-warning {{cast from 'ptr_ret' (aka 'void *(*)()') to 'ul_ret' (aka 'unsigned long (*)()') converts to incompatible function type}} +} diff --git a/clang/test/SemaCXX/warn-cast-function-type-strict.cpp b/clang/test/SemaCXX/warn-cast-function-type-strict.cpp index 9203c1aae8c90..bdc3bb88ce1fe 100644 --- a/clang/test/SemaCXX/warn-cast-function-type-strict.cpp +++ b/clang/test/SemaCXX/warn-cast-function-type-strict.cpp @@ -40,15 +40,19 @@ 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}} + b = (f2 *)x; // strict-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function type}} + b = reinterpret_cast<f2 *>(x); // strict-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}} e = (f5 *)x; // strict-warning {{cast from 'int (*)(long)' to 'f5 *' (aka 'void (*)()') converts to incompatible function type}} f = (f6 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function type}} g = (f7 *)x; // strict-warning {{cast from 'int (*)(long)' to 'f7 *' (aka 'int (*)(long, ...)') converts to incompatible function type}} +#if __SIZEOF_POINTER__ != __SIZEOF_INT__ mf p1 = (mf)&S::foo; // expected-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function type}} +#else + mf p1 = (mf)&S::foo; // strict-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function type}} +#endif f8 f2 = (f8)x; // expected-warning {{cast from 'int (long)' to 'f8' (aka 'int (&)(long, int)') converts to incompatible function type}} (void)f2; @@ -56,3 +60,13 @@ void foo() { int (^y)(long); f = (f6 *)y; // expected-warning {{cast from 'int (^)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function type}} } + +// Strict mode: pointer-vs-integral should still warn even when sizes match. +typedef void *(*ptr_ret_fn)(void); +typedef unsigned long (*ul_ret_fn)(void); +ptr_ret_fn pr2; +ul_ret_fn ur2; +void test_ptr_int_return_strict() { + pr2 = (ptr_ret_fn)ur2; // strict-warning {{cast from 'ul_ret_fn' (aka 'unsigned long (*)()') to 'ptr_ret_fn' (aka 'void *(*)()') converts to incompatible function type}} + ur2 = reinterpret_cast<ul_ret_fn>(pr2); // strict-warning {{cast from 'ptr_ret_fn' (aka 'void *(*)()') to 'ul_ret_fn' (aka 'unsigned long (*)()') converts to incompatible function type}} +} diff --git a/clang/test/SemaCXX/warn-cast-function-type.cpp b/clang/test/SemaCXX/warn-cast-function-type.cpp index 5f450f25f3f68..1e0069a1f3847 100644 --- a/clang/test/SemaCXX/warn-cast-function-type.cpp +++ b/clang/test/SemaCXX/warn-cast-function-type.cpp @@ -39,15 +39,19 @@ 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}} + b = (f2 *)x; // ABI-compatible: long and void* same size on this target. + b = reinterpret_cast<f2 *>(x); // ABI-compatible: long and void* same size on this target. c = (f3 *)x; d = (f4 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)(...)') converts to incompatible function type}} e = (f5 *)x; f = (f6 *)x; // expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function type}} g = (f7 *)x; +#if __SIZEOF_POINTER__ != __SIZEOF_INT__ mf p1 = (mf)&S::foo; // expected-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function type}} +#else + mf p1 = (mf)&S::foo; +#endif f8 f2 = (f8)x; // expected-warning {{cast from 'int (long)' to 'f8' (aka 'int (&)(long, int)') converts to incompatible function type}} (void)f2; @@ -55,3 +59,27 @@ void foo() { int (^y)(long); f = (f6 *)y; // expected-warning {{cast from 'int (^)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function type}} } + +// Pointer-vs-integral return types: same size so ABI-compatible on this target. +typedef void *(*ptr_ret_fn)(void); +typedef unsigned long (*ul_ret_fn)(void); +ptr_ret_fn pr; +ul_ret_fn ur; +void test_ptr_int_return() { + pr = (ptr_ret_fn)ur; + ur = (ul_ret_fn)pr; +} + +// Pointer-vs-integral with reinterpret_cast. +void test_ptr_int_reinterpret_cast() { + pr = reinterpret_cast<ptr_ret_fn>(ur); + ur = reinterpret_cast<ul_ret_fn>(pr); +} + +// Different sizes should still warn. +typedef short (*short_ret_fn)(void); +short_ret_fn sr; +void test_ptr_int_diff_size() { + void *(*pr3)(void); + pr3 = (void *(*)(void))sr; // expected-warning {{cast from 'short_ret_fn' (aka 'short (*)()') to 'void *(*)()' converts to incompatible function type}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
