diff --git a/clang/docs/DiagnosticsReference.rst b/clang/docs/DiagnosticsReference.rst --- a/clang/docs/DiagnosticsReference.rst +++ b/clang/docs/DiagnosticsReference.rst @@ -851,6 +851,13 @@ |: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 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -499,6 +499,7 @@ 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8386,6 +8386,9 @@ def warn_bad_function_cast : Warning< "cast from function call of type %0 to non-matching type %1">, InGroup, DefaultIgnore; +def warn_cast_function_type : Warning< + "cast from %0 to %1 converts to incompatible function types">, + InGroup, 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 --- 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 using namespace clang; @@ -1035,6 +1036,90 @@ << 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(); + DstFTy = DestType->getPointeeType()->castAs(); + } else if (SrcType->isFunctionType() && DestType->isFunctionReferenceType()) { + SrcFTy = SrcType->castAs(); + DstFTy = DestType.getNonReferenceType()->castAs(); + } else { + return true; + } + assert(SrcFTy && DstFTy); + + auto IsVoidVoid = [](const FunctionType *T) { + if (!T->getReturnType()->isVoidType()) + return false; + if (const auto *PT = T->getAs()) + 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(SrcFTy); + const auto *DstFPTy = cast(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\(SrcExpr) is /// valid. /// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code @@ -1072,6 +1157,10 @@ 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 @@ 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 @@ } } + 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 --- /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 --- /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(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}} */ +}