[clang] [Clang] Suppress mismatch warning for same-size pointer/integer casts (PR #203955)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 24 03:46:24 PDT 2026


https://github.com/jxz-hw updated https://github.com/llvm/llvm-project/pull/203955

>From d97b09f260806fcc81d5622095d95b43b31d2317 Mon Sep 17 00:00:00 2001
From: jxz-hw <jiangxuezhi2 at huawei.com>
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        | 16 ++++++++--
 .../test/SemaCXX/warn-cast-function-type.cpp  | 30 +++++++++++++++--
 9 files changed, 158 insertions(+), 12 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 cc226403877e2..5f66f43eda429 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1064,6 +1064,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 729d79ff77fbf..29e36e96c8088 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(const std::string &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..3ced36bfdec91 100644
--- a/clang/test/SemaCXX/warn-cast-function-type-strict.cpp
+++ b/clang/test/SemaCXX/warn-cast-function-type-strict.cpp
@@ -40,15 +40,15 @@ 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}}
 
-  mf p1 = (mf)&S::foo; // expected-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function type}}
+  mf p1 = (mf)&S::foo; // strict-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function type}}
 
   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 +56,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..19c9177ad7efb 100644
--- a/clang/test/SemaCXX/warn-cast-function-type.cpp
+++ b/clang/test/SemaCXX/warn-cast-function-type.cpp
@@ -39,15 +39,15 @@ 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;
 
-  mf p1 = (mf)&S::foo; // expected-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function type}}
+  mf p1 = (mf)&S::foo; // ABI-compatible: int* and int same size on this target.
 
   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 +55,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}}
+}



More information about the cfe-commits mailing list