[llvm-branch-commits] [clang] Silence -Wcast-function-type warnings on idiomatic Windows code (#135… (PR #135798)

Aaron Ballman via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Apr 15 08:53:39 PDT 2025


https://github.com/AaronBallman created https://github.com/llvm/llvm-project/pull/135798

…660)

On Windows, GetProcAddress() is the API used to dynamically load function pointers (similar to dlsym on Linux). This API returns a function pointer (a typedef named FARPROC), which means that casting from the call to the eventual correct type is technically a function type mismatch on the cast. However, because this is idiomatic code on Windows, we should accept it unless -Wcast-function-type-strict is passed.

This was brought up in post-commit review feedback on https://github.com/llvm/llvm-project/pull/86131

>From 754c032f3ccea10c14c3a2789b0e3ada6cc4c914 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Tue, 15 Apr 2025 11:19:19 -0400
Subject: [PATCH] Silence -Wcast-function-type warnings on idiomatic Windows
 code (#135660)

On Windows, GetProcAddress() is the API used to dynamically load
function pointers (similar to dlsym on Linux). This API returns a
function pointer (a typedef named FARPROC), which means that casting
from the call to the eventual correct type is technically a function
type mismatch on the cast. However, because this is idiomatic code on
Windows, we should accept it unless -Wcast-function-type-strict is
passed.

This was brought up in post-commit review feedback on
https://github.com/llvm/llvm-project/pull/86131
---
 clang/docs/ReleaseNotes.rst                   | 10 ++++++
 clang/lib/Sema/SemaCast.cpp                   | 23 ++++++++++++
 clang/test/Sema/warn-cast-function-type-win.c | 36 +++++++++++++++++++
 3 files changed, 69 insertions(+)
 create mode 100644 clang/test/Sema/warn-cast-function-type-win.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f4befc242f28b..b8f26ec9a5447 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -694,6 +694,16 @@ Improvements to Clang's diagnostics
   match a template template parameter, in terms of the C++17 relaxed matching rules
   instead of the old ones.
 
+- No longer diagnosing idiomatic function pointer casts on Windows under
+  ``-Wcast-function-type-mismatch`` (which is enabled by ``-Wextra``). Clang
+  would previously warn on this construct, but will no longer do so on Windows:
+
+  .. code-block:: c
+
+    typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
+    HMODULE Lib = LoadLibrary("kernel32");
+    PGNSI FnPtr = (PGNSI)GetProcAddress(Lib, "GetNativeSystemInfo");
+
 - Don't emit duplicated dangling diagnostics. (#GH93386).
 
 - Improved diagnostic when trying to befriend a concept. (#GH45182).
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 54bc52fa2ac40..d0d44e8899133 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -1151,10 +1151,33 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr,
     return false;
   };
 
+  auto IsFarProc = [](const FunctionType *T) {
+    // The definition of FARPROC depends on the platform in terms of its return
+    // type, which could be int, or long long, etc. We'll look for a source
+    // signature for: <integer type> (*)() and call that "close enough" to
+    // FARPROC to be sufficient to silence the diagnostic. This is similar to
+    // how we allow casts between function pointers and void * for supporting
+    // dlsym.
+    // Note: we could check for __stdcall on the function pointer as well, but
+    // that seems like splitting hairs.
+    if (!T->getReturnType()->isIntegerType())
+      return false;
+    if (const auto *PT = T->getAs<FunctionProtoType>())
+      return !PT->isVariadic() && PT->getNumParams() == 0;
+    return true;
+  };
+
   // Skip if either function type is void(*)(void)
   if (IsVoidVoid(SrcFTy) || IsVoidVoid(DstFTy))
     return 0;
 
+  // On Windows, GetProcAddress() returns a FARPROC, which is a typedef for a
+  // function pointer type (with no prototype, in C). We don't want to diagnose
+  // this case so we don't diagnose idiomatic code on Windows.
+  if (Self.getASTContext().getTargetInfo().getTriple().isOSWindows() &&
+      IsFarProc(SrcFTy))
+    return 0;
+
   // Check return type.
   if (!argTypeIsABIEquivalent(SrcFTy->getReturnType(), DstFTy->getReturnType(),
                               Self.Context))
diff --git a/clang/test/Sema/warn-cast-function-type-win.c b/clang/test/Sema/warn-cast-function-type-win.c
new file mode 100644
index 0000000000000..4e7ba33b258d8
--- /dev/null
+++ b/clang/test/Sema/warn-cast-function-type-win.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 %s -triple x86_64-windows -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=windows
+// RUN: %clang_cc1 %s -triple x86_64-windows -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -x c++ -verify=windows
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=linux
+// RUN: %clang_cc1 %s -triple x86_64-pc-linux -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -x c++ -verify=linux,linux-cpp
+// RUN: %clang_cc1 %s -triple x86_64-windows -fsyntax-only -Wcast-function-type -Wcast-function-type-strict -x c++ -verify=strict
+// windows-no-diagnostics
+
+// On Windows targets, this is expected to compile fine, and on non-Windows
+// targets, this should diagnose the mismatch. This is to allow for idiomatic
+// use of GetProcAddress, similar to what we do for dlsym. On non-Windows
+// targets, this should be diagnosed.
+typedef int (*FARPROC1)();
+typedef unsigned long long (*FARPROC2)();
+
+FARPROC1 GetProcAddress1(void);
+FARPROC2 GetProcAddress2(void);
+
+typedef int (*test1_type)(int);
+typedef float(*test2_type)();
+
+void test(void) {
+  // This does not diagnose on Linux in C mode because FARPROC1 has a matching
+  // return type to test1_type, but FARPROC1 has no prototype and so checking
+  // is disabled for further compatibility issues. In C++ mode, all functions
+  // have a prototype and so the check happens.
+  test1_type t1 = (test1_type)GetProcAddress1();
+  // linux-cpp-warning at -1 {{cast from 'FARPROC1' (aka 'int (*)()') to 'test1_type' (aka 'int (*)(int)') converts to incompatible function type}}
+  // strict-warning at -2 {{cast from 'FARPROC1' (aka 'int (*)()') to 'test1_type' (aka 'int (*)(int)') converts to incompatible function type}}
+  
+  // This case is diagnosed in both C and C++ modes on Linux because the return
+  // type of FARPROC2 does not match the return type of test2_type.
+  test2_type t2 = (test2_type)GetProcAddress2();
+  // linux-warning at -1 {{cast from 'FARPROC2' (aka 'unsigned long long (*)()') to 'test2_type' (aka 'float (*)()') converts to incompatible function type}}
+  // strict-warning at -2 {{cast from 'FARPROC2' (aka 'unsigned long long (*)()') to 'test2_type' (aka 'float (*)()') converts to incompatible function type}}
+}
+



More information about the llvm-branch-commits mailing list