Skip to content

Commit 754c032

Browse files
committed
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 #86131
1 parent 2e09664 commit 754c032

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

clang/docs/ReleaseNotes.rst

+10
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,16 @@ Improvements to Clang's diagnostics
694694
match a template template parameter, in terms of the C++17 relaxed matching rules
695695
instead of the old ones.
696696

697+
- No longer diagnosing idiomatic function pointer casts on Windows under
698+
``-Wcast-function-type-mismatch`` (which is enabled by ``-Wextra``). Clang
699+
would previously warn on this construct, but will no longer do so on Windows:
700+
701+
.. code-block:: c
702+
703+
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
704+
HMODULE Lib = LoadLibrary("kernel32");
705+
PGNSI FnPtr = (PGNSI)GetProcAddress(Lib, "GetNativeSystemInfo");
706+
697707
- Don't emit duplicated dangling diagnostics. (#GH93386).
698708

699709
- Improved diagnostic when trying to befriend a concept. (#GH45182).

clang/lib/Sema/SemaCast.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -1151,10 +1151,33 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr,
11511151
return false;
11521152
};
11531153

1154+
auto IsFarProc = [](const FunctionType *T) {
1155+
// The definition of FARPROC depends on the platform in terms of its return
1156+
// type, which could be int, or long long, etc. We'll look for a source
1157+
// signature for: <integer type> (*)() and call that "close enough" to
1158+
// FARPROC to be sufficient to silence the diagnostic. This is similar to
1159+
// how we allow casts between function pointers and void * for supporting
1160+
// dlsym.
1161+
// Note: we could check for __stdcall on the function pointer as well, but
1162+
// that seems like splitting hairs.
1163+
if (!T->getReturnType()->isIntegerType())
1164+
return false;
1165+
if (const auto *PT = T->getAs<FunctionProtoType>())
1166+
return !PT->isVariadic() && PT->getNumParams() == 0;
1167+
return true;
1168+
};
1169+
11541170
// Skip if either function type is void(*)(void)
11551171
if (IsVoidVoid(SrcFTy) || IsVoidVoid(DstFTy))
11561172
return 0;
11571173

1174+
// On Windows, GetProcAddress() returns a FARPROC, which is a typedef for a
1175+
// function pointer type (with no prototype, in C). We don't want to diagnose
1176+
// this case so we don't diagnose idiomatic code on Windows.
1177+
if (Self.getASTContext().getTargetInfo().getTriple().isOSWindows() &&
1178+
IsFarProc(SrcFTy))
1179+
return 0;
1180+
11581181
// Check return type.
11591182
if (!argTypeIsABIEquivalent(SrcFTy->getReturnType(), DstFTy->getReturnType(),
11601183
Self.Context))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %clang_cc1 %s -triple x86_64-windows -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=windows
2+
// RUN: %clang_cc1 %s -triple x86_64-windows -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -x c++ -verify=windows
3+
// RUN: %clang_cc1 %s -triple x86_64-pc-linux -fsyntax-only -Wcast-function-type -Wno-cast-function-type-strict -verify=linux
4+
// 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
5+
// RUN: %clang_cc1 %s -triple x86_64-windows -fsyntax-only -Wcast-function-type -Wcast-function-type-strict -x c++ -verify=strict
6+
// windows-no-diagnostics
7+
8+
// On Windows targets, this is expected to compile fine, and on non-Windows
9+
// targets, this should diagnose the mismatch. This is to allow for idiomatic
10+
// use of GetProcAddress, similar to what we do for dlsym. On non-Windows
11+
// targets, this should be diagnosed.
12+
typedef int (*FARPROC1)();
13+
typedef unsigned long long (*FARPROC2)();
14+
15+
FARPROC1 GetProcAddress1(void);
16+
FARPROC2 GetProcAddress2(void);
17+
18+
typedef int (*test1_type)(int);
19+
typedef float(*test2_type)();
20+
21+
void test(void) {
22+
// This does not diagnose on Linux in C mode because FARPROC1 has a matching
23+
// return type to test1_type, but FARPROC1 has no prototype and so checking
24+
// is disabled for further compatibility issues. In C++ mode, all functions
25+
// have a prototype and so the check happens.
26+
test1_type t1 = (test1_type)GetProcAddress1();
27+
// linux-cpp-warning@-1 {{cast from 'FARPROC1' (aka 'int (*)()') to 'test1_type' (aka 'int (*)(int)') converts to incompatible function type}}
28+
// strict-warning@-2 {{cast from 'FARPROC1' (aka 'int (*)()') to 'test1_type' (aka 'int (*)(int)') converts to incompatible function type}}
29+
30+
// This case is diagnosed in both C and C++ modes on Linux because the return
31+
// type of FARPROC2 does not match the return type of test2_type.
32+
test2_type t2 = (test2_type)GetProcAddress2();
33+
// linux-warning@-1 {{cast from 'FARPROC2' (aka 'unsigned long long (*)()') to 'test2_type' (aka 'float (*)()') converts to incompatible function type}}
34+
// strict-warning@-2 {{cast from 'FARPROC2' (aka 'unsigned long long (*)()') to 'test2_type' (aka 'float (*)()') converts to incompatible function type}}
35+
}
36+

0 commit comments

Comments
 (0)