Skip to content

Commit d0e4af8

Browse files
authored
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 637f352 commit d0e4af8

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,16 @@ Improvements to Clang's diagnostics
358358

359359
- Now correctly diagnose a tentative definition of an array with static
360360
storage duration in pedantic mode in C. (#GH50661)
361+
- No longer diagnosing idiomatic function pointer casts on Windows under
362+
``-Wcast-function-type-mismatch`` (which is enabled by ``-Wextra``). Clang
363+
would previously warn on this construct, but will no longer do so on Windows:
364+
365+
.. code-block:: c
366+
367+
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
368+
HMODULE Lib = LoadLibrary("kernel32");
369+
PGNSI FnPtr = (PGNSI)GetProcAddress(Lib, "GetNativeSystemInfo");
370+
361371
362372
- An error is now emitted when a ``musttail`` call is made to a function marked with the ``not_tail_called`` attribute. (#GH133509).
363373

clang/lib/Sema/SemaCast.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,10 +1153,33 @@ static unsigned int checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr,
11531153
return false;
11541154
};
11551155

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

1176+
// On Windows, GetProcAddress() returns a FARPROC, which is a typedef for a
1177+
// function pointer type (with no prototype, in C). We don't want to diagnose
1178+
// this case so we don't diagnose idiomatic code on Windows.
1179+
if (Self.getASTContext().getTargetInfo().getTriple().isOSWindows() &&
1180+
IsFarProc(SrcFTy))
1181+
return 0;
1182+
11601183
// Check return type.
11611184
if (!argTypeIsABIEquivalent(SrcFTy->getReturnType(), DstFTy->getReturnType(),
11621185
Self.Context))
Lines changed: 36 additions & 0 deletions
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)