Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -291,6 +291,7 @@ def OverloadedVirtual : DiagGroup<"overloaded-virtual">; def PrivateExtern : DiagGroup<"private-extern">; def SelTypeCast : DiagGroup<"cast-of-sel-type">; +def CastCallingConvention : DiagGroup<"cast-calling-convention">; def FunctionDefInObjCContainer : DiagGroup<"function-def-in-objc-container">; def BadFunctionCast : DiagGroup<"bad-function-cast">; def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6510,7 +6510,12 @@ def warn_function_def_in_objc_container : Warning< "function definition inside an Objective-C container is deprecated">, InGroup; - + +def warn_cast_calling_conv : Warning< + "cast between incompatible calling conventions '%0' and '%1'">, + InGroup, DefaultIgnore; +def note_change_calling_conv_fixit : Note< + "consider defining %0 with the '%1' calling convention">; def warn_bad_function_cast : Warning< "cast from function call of type %0 to non-matching type %1">, InGroup, DefaultIgnore; Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -1724,6 +1724,57 @@ } } +/// Diagnose casts that change the calling convention of a pointer to a function +/// defined in the current TU. +static void diagnoseCallingConvCast(Sema &Self, const ExprResult &SrcExpr, + QualType DstType, SourceRange OpRange) { + if (Self.Diags.isIgnored(diag::warn_cast_calling_conv, OpRange.getBegin())) + return; + + // Check if this cast would change the calling convention of a function + // pointer type. + QualType SrcType = SrcExpr.get()->getType(); + if (Self.Context.hasSameType(SrcType, DstType) || + !SrcType->isFunctionPointerType() || !DstType->isFunctionPointerType()) + return; + const auto *SrcFTy = + SrcType->castAs()->getPointeeType()->castAs(); + const auto *DstFTy = + DstType->castAs()->getPointeeType()->castAs(); + CallingConv SrcCC = SrcFTy->getCallConv(); + CallingConv DstCC = DstFTy->getCallConv(); + if (SrcCC == DstCC) + return; + + // We have a calling convention cast. Check if the source is a pointer to a + // known, specific function that has already been defined. + Expr *Src = SrcExpr.get()->IgnoreParenImpCasts(); + if (auto *AddrOf = dyn_cast(Src)) + Src = AddrOf->getSubExpr()->IgnoreParenImpCasts(); + auto *DRE = dyn_cast(Src); + if (!DRE) + return; + auto *FD = dyn_cast(DRE->getDecl()); + const FunctionDecl *Definition; + if (!FD || !FD->hasBody(Definition)) + return; + + // The source expression is a pointer to a known function defined in this TU. + // Diagnose this cast, as it is probably bad. + StringRef SrcCCName = FunctionType::getNameForCallConv(SrcCC); + StringRef DstCCName = FunctionType::getNameForCallConv(DstCC); + Self.Diag(OpRange.getBegin(), diag::warn_cast_calling_conv) + << SrcCCName << DstCCName << OpRange; + SourceLocation NameLoc = Definition->getNameInfo().getLoc(); + SmallString<64> CCAttrText; + if (Self.getLangOpts().MicrosoftExt) + (Twine("__") + DstCCName + " ").toVector(CCAttrText); + else + (Twine("__attribute__((") + DstCCName + ")) ").toVector(CCAttrText); + Self.Diag(NameLoc, diag::note_change_calling_conv_fixit) + << FD << DstCCName << FixItHint::CreateInsertion(NameLoc, CCAttrText); +} + static void checkIntToPointerCast(bool CStyle, SourceLocation Loc, const Expr *SrcExpr, QualType DestType, Sema &Self) { @@ -2008,7 +2059,9 @@ } if (CStyle) DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType); - + + diagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange); + // Not casting away constness, so the only remaining check is for compatible // pointer categories. @@ -2427,6 +2480,7 @@ } DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType); + diagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange); DiagnoseBadFunctionCast(Self, SrcExpr, DestType); Kind = Self.PrepareScalarCast(SrcExpr, DestType); if (SrcExpr.isInvalid()) Index: test/Sema/callingconv-cast.c =================================================================== --- /dev/null +++ test/Sema/callingconv-cast.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -verify -triple i686-pc-windows-msvc -Wcast-calling-convention -x c %s +// RUN: %clang_cc1 -verify -triple i686-pc-windows-msvc -Wcast-calling-convention -x c++ %s + +// expected-note@+1 2 {{consider defining 'mismatched' with the 'stdcall' calling convention}} +void mismatched(int x) {} + +typedef void (__stdcall *callback_t)(int); +void take_callback(callback_t callback); + +int main() { + // expected-warning@+1 {{cast between incompatible calling conventions 'cdecl' and 'stdcall'}} + take_callback((callback_t)mismatched); + + // expected-warning@+1 {{cast between incompatible calling conventions 'cdecl' and 'stdcall'}} + callback_t callback = (callback_t)mismatched; // warns + (void)callback; + + // Probably a bug, but we don't warn. + void (*callback2)(int) = mismatched; + take_callback((callback_t)callback2); + + // Another way to suppress the warning. + take_callback((callback_t)(void*)mismatched); +}