[clang] 217f0f7 - [Clang][Sema] Implement GCC -Wcast-function-type
Yuanfang Chen via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 24 16:04:55 PDT 2021
Author: Yuanfang Chen
Date: 2021-03-24T16:04:18-07:00
New Revision: 217f0f735afec57a51fa6f9ab863d4713a2f85e2
URL: https://github.com/llvm/llvm-project/commit/217f0f735afec57a51fa6f9ab863d4713a2f85e2
DIFF: https://github.com/llvm/llvm-project/commit/217f0f735afec57a51fa6f9ab863d4713a2f85e2.diff
LOG: [Clang][Sema] Implement GCC -Wcast-function-type
```
Warn when a function pointer is cast to an incompatible function
pointer. In a cast involving function types with a variable argument
list only the types of initial arguments that are provided are
considered. Any parameter of pointer-type matches any other
pointer-type. Any benign differences in integral types are ignored, like
int vs. long on ILP32 targets. Likewise type qualifiers are ignored. The
function type void (*) (void) is special and matches everything, which
can be used to suppress this warning. In a cast involving pointer to
member types this warning warns whenever the type cast is changing the
pointer to member type. This warning is enabled by -Wextra.
```
Reviewed By: rsmith
Differential Revision: https://reviews.llvm.org/D97831
Added:
clang/test/Sema/warn-cast-function-type.c
clang/test/Sema/warn-cast-function-type.cpp
Modified:
clang/docs/DiagnosticsReference.rst
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaCast.cpp
Removed:
################################################################################
diff --git a/clang/docs/DiagnosticsReference.rst b/clang/docs/DiagnosticsReference.rst
index 04d7f74d5bfc9..730077f33397a 100644
--- a/clang/docs/DiagnosticsReference.rst
+++ b/clang/docs/DiagnosticsReference.rst
@@ -851,6 +851,13 @@ This diagnostic is enabled by default.
|:warning:`warning:` |nbsp| :diagtext:`cast from function call of type` |nbsp| :placeholder:`A` |nbsp| :diagtext:`to non-matching type` |nbsp| :placeholder:`B`|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-Wcast-function-type
+-------------------
+**Diagnostic text:**
+
++--------------------------------------------------------------------------------------------------------------------------------------------------------------+
+|:warning:`warning:` |nbsp| :diagtext:`cast from` |nbsp| :placeholder:`A` |nbsp| :diagtext:`to` |nbsp| :placeholder:`B` |nbsp| :diagtext:`converts to incompatible function types`|
++--------------------------------------------------------------------------------------------------------------------------------------------------------------+
-Wbinary-literal
----------------
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 291cca02694fc..85f798013a3d4 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -499,6 +499,7 @@ def PrivateExtern : DiagGroup<"private-extern">;
def SelTypeCast : DiagGroup<"cast-of-sel-type">;
def FunctionDefInObjCContainer : DiagGroup<"function-def-in-objc-container">;
def BadFunctionCast : DiagGroup<"bad-function-cast">;
+def CastFunctionType : DiagGroup<"cast-function-type">;
def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
def ObjCPropertyAssignOnObjectType : DiagGroup<"objc-property-assign-on-object-type">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 58e221a004689..df2f79a4f3441 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8386,6 +8386,9 @@ def note_change_calling_conv_fixit : Note<
def warn_bad_function_cast : Warning<
"cast from function call of type %0 to non-matching type %1">,
InGroup<BadFunctionCast>, DefaultIgnore;
+def warn_cast_function_type : Warning<
+ "cast from %0 to %1 converts to incompatible function types">,
+ InGroup<CastFunctionType>, DefaultIgnore;
def err_cast_pointer_to_non_pointer_int : Error<
"pointer cannot be cast to type %0">;
def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">;
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 22ec2c7ed8bbf..719cbf46bd5c2 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -13,8 +13,8 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Sema/SemaInternal.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTStructuralEquivalence.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
@@ -23,6 +23,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h"
+#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/SmallVector.h"
#include <set>
using namespace clang;
@@ -1035,6 +1036,90 @@ static void DiagnoseReinterpretUpDownCast(Sema &Self, const Expr *SrcExpr,
<< FixItHint::CreateReplacement(BeginLoc, "static_cast");
}
+static bool argTypeIsABIEquivalent(QualType SrcType, QualType DestType,
+ ASTContext &Context) {
+ if (SrcType->isPointerType() && DestType->isPointerType())
+ return true;
+
+ // Allow integral type mismatch if their size are equal.
+ if (SrcType->isIntegralType(Context) && DestType->isIntegralType(Context))
+ if (Context.getTypeInfoInChars(SrcType).Width ==
+ Context.getTypeInfoInChars(DestType).Width)
+ return true;
+
+ return Context.hasSameUnqualifiedType(SrcType, DestType);
+}
+
+static bool checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr,
+ QualType DestType) {
+ if (Self.Diags.isIgnored(diag::warn_cast_function_type,
+ SrcExpr.get()->getExprLoc()))
+ return true;
+
+ QualType SrcType = SrcExpr.get()->getType();
+ const FunctionType *SrcFTy = nullptr;
+ const FunctionType *DstFTy = nullptr;
+ if (((SrcType->isBlockPointerType() || SrcType->isFunctionPointerType()) &&
+ DestType->isFunctionPointerType()) ||
+ (SrcType->isMemberFunctionPointerType() &&
+ DestType->isMemberFunctionPointerType())) {
+ SrcFTy = SrcType->getPointeeType()->castAs<FunctionType>();
+ DstFTy = DestType->getPointeeType()->castAs<FunctionType>();
+ } else if (SrcType->isFunctionType() && DestType->isFunctionReferenceType()) {
+ SrcFTy = SrcType->castAs<FunctionType>();
+ DstFTy = DestType.getNonReferenceType()->castAs<FunctionType>();
+ } else {
+ return true;
+ }
+ assert(SrcFTy && DstFTy);
+
+ auto IsVoidVoid = [](const FunctionType *T) {
+ if (!T->getReturnType()->isVoidType())
+ return false;
+ if (const auto *PT = T->getAs<FunctionProtoType>())
+ return !PT->isVariadic() && PT->getNumParams() == 0;
+ return false;
+ };
+
+ // Skip if either function type is void(*)(void)
+ if (IsVoidVoid(SrcFTy) || IsVoidVoid(DstFTy))
+ return true;
+
+ // Check return type.
+ if (!argTypeIsABIEquivalent(SrcFTy->getReturnType(), DstFTy->getReturnType(),
+ Self.Context))
+ return false;
+
+ // Check if either has unspecified number of parameters
+ if (SrcFTy->isFunctionNoProtoType() || DstFTy->isFunctionNoProtoType())
+ return true;
+
+ // Check parameter types.
+
+ const auto *SrcFPTy = cast<FunctionProtoType>(SrcFTy);
+ const auto *DstFPTy = cast<FunctionProtoType>(DstFTy);
+
+ // In a cast involving function types with a variable argument list only the
+ // types of initial arguments that are provided are considered.
+ unsigned NumParams = SrcFPTy->getNumParams();
+ unsigned DstNumParams = DstFPTy->getNumParams();
+ if (NumParams > DstNumParams) {
+ if (!DstFPTy->isVariadic())
+ return false;
+ NumParams = DstNumParams;
+ } else if (NumParams < DstNumParams) {
+ if (!SrcFPTy->isVariadic())
+ return false;
+ }
+
+ for (unsigned i = 0; i < NumParams; ++i)
+ if (!argTypeIsABIEquivalent(SrcFPTy->getParamType(i),
+ DstFPTy->getParamType(i), Self.Context))
+ return false;
+
+ return true;
+}
+
/// CheckReinterpretCast - Check that a reinterpret_cast\<DestType\>(SrcExpr) is
/// valid.
/// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code
@@ -1072,6 +1157,10 @@ void CastOperation::CheckReinterpretCast() {
if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers())
checkObjCConversion(Sema::CCK_OtherCast);
DiagnoseReinterpretUpDownCast(Self, SrcExpr.get(), DestType, OpRange);
+
+ if (!checkCastFunctionType(Self, SrcExpr, DestType))
+ Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
+ << SrcExpr.get()->getType() << DestType << OpRange;
} else {
SrcExpr = ExprError();
}
@@ -2645,6 +2734,11 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
if (isValidCast(tcr)) {
if (Kind == CK_BitCast)
checkCastAlign();
+
+ if (!checkCastFunctionType(Self, SrcExpr, DestType))
+ Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
+ << SrcExpr.get()->getType() << DestType << OpRange;
+
} else {
SrcExpr = ExprError();
}
@@ -2957,6 +3051,10 @@ void CastOperation::CheckCStyleCast() {
}
}
+ if (!checkCastFunctionType(Self, SrcExpr, DestType))
+ Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
+ << SrcType << DestType << OpRange;
+
DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType);
DiagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange);
DiagnoseBadFunctionCast(Self, SrcExpr, DestType);
diff --git a/clang/test/Sema/warn-cast-function-type.c b/clang/test/Sema/warn-cast-function-type.c
new file mode 100644
index 0000000000000..e2572210c1370
--- /dev/null
+++ b/clang/test/Sema/warn-cast-function-type.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -x c %s -fsyntax-only -Wcast-function-type -triple x86_64-- -verify
+
+int x(long);
+
+typedef int (f1)(long);
+typedef int (f2)(void*);
+typedef int (f3)();
+typedef void (f4)();
+typedef void (f5)(void);
+typedef int (f6)(long, int);
+typedef int (f7)(long,...);
+
+f1 *a;
+f2 *b;
+f3 *c;
+f4 *d;
+f5 *e;
+f6 *f;
+f7 *g;
+
+void foo(void) {
+ a = (f1 *)x;
+ b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function types}} */
+ c = (f3 *)x;
+ d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function types}} */
+ e = (f5 *)x;
+ f = (f6 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function types}} */
+ g = (f7 *)x;
+}
diff --git a/clang/test/Sema/warn-cast-function-type.cpp b/clang/test/Sema/warn-cast-function-type.cpp
new file mode 100644
index 0000000000000..d70657dad1793
--- /dev/null
+++ b/clang/test/Sema/warn-cast-function-type.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -x c++ %s -fblocks -fsyntax-only -Wcast-function-type -triple x86_64-- -verify
+
+int x(long);
+
+typedef int (f1)(long);
+typedef int (f2)(void*);
+typedef int (f3)(...);
+typedef void (f4)(...);
+typedef void (f5)(void);
+typedef int (f6)(long, int);
+typedef int (f7)(long,...);
+typedef int (&f8)(long, int);
+
+f1 *a;
+f2 *b;
+f3 *c;
+f4 *d;
+f5 *e;
+f6 *f;
+f7 *g;
+
+struct S
+{
+ void foo (int*);
+ void bar (int);
+};
+
+typedef void (S::*mf)(int);
+
+void foo() {
+ a = (f1 *)x;
+ b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function types}} */
+ b = reinterpret_cast<f2 *>(x); /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function types}} */
+ c = (f3 *)x;
+ d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)(...)') converts to incompatible function types}} */
+ e = (f5 *)x;
+ f = (f6 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function types}} */
+ 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 types}} */
+
+ f8 f2 = (f8)x; /* expected-warning {{cast from 'int (long)' to 'f8' (aka 'int (&)(long, int)') converts to incompatible function types}} */
+ (void)f2;
+
+ int (^y)(long);
+ f = (f6 *)y; /* expected-warning {{cast from 'int (^)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function types}} */
+}
More information about the cfe-commits
mailing list