"than the function it overrides}1,2">;
def note_overridden_virtual_function : Note<
"overridden virtual function is here">;
+def err_conflicting_overriding_cc_attributes : Error<
+ "virtual function %0 has different calling convention attributes "
+ "%diff{($) than the function it overrides (which has calling convention $)|"
+ "than the function it overrides}1,2">;
def err_covariant_return_inaccessible_base : Error<
"invalid covariant return for virtual function: %1 is a "
bool isBigEndian() const { return BigEndian; }
+ enum CallingConvMethodType {
+ CCMT_Unknown,
+ CCMT_Member,
+ CCMT_NonMember
+ };
+
/// \brief Gets the default calling convention for the given target and
/// declaration context.
- virtual CallingConv getDefaultCallingConv() const {
+ virtual CallingConv getDefaultCallingConv(CallingConvMethodType MT) const {
// Not all targets will specify an explicit calling convention that we can
// express. This will always do the right thing, even though it's not
// an explicit calling convention.
- return CC_Default;
+ return CC_C;
}
enum CallingConvCheckResult {
void checkUnusedDeclAttributes(Declarator &D);
bool CheckRegparmAttr(const AttributeList &attr, unsigned &value);
- bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC);
+ bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC,
+ const FunctionDecl *FD = 0);
bool CheckNoReturnAttr(const AttributeList &attr);
/// \brief Stmt attributes - this routine is the top level dispatcher.
std::string getAmbiguousPathsDisplayString(CXXBasePaths &Paths);
+ bool CheckOverridingFunctionAttributes(const CXXMethodDecl *New,
+ const CXXMethodDecl *Old);
+
/// CheckOverridingFunctionReturnType - Checks whether the return types are
/// covariant, according to C++ [class.virtual]p5.
bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
CC == CC_X86Pascal) ? CCCR_OK : CCCR_Warning;
}
- virtual CallingConv getDefaultCallingConv() const {
- return CC_C;
+ virtual CallingConv getDefaultCallingConv(CallingConvMethodType MT) const {
+ return MT == CCMT_Member ? CC_X86ThisCall : CC_C;
}
};
return TargetInfo::checkCallingConvention(CC);
}
- virtual CallingConv getDefaultCallingConv() const {
- return CC_Default;
+ virtual CallingConv getDefaultCallingConv(CallingConvMethodType MT) const {
+ return CC_C;
}
};
if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) {
MD->addOverriddenMethod(OldMD->getCanonicalDecl());
if (!CheckOverridingFunctionReturnType(MD, OldMD) &&
+ !CheckOverridingFunctionAttributes(MD, OldMD) &&
!CheckOverridingFunctionExceptionSpec(MD, OldMD) &&
!CheckIfOverriddenFunctionIsMarkedFinal(MD, OldMD)) {
hasDeletedOverridenMethods |= OldMD->isDeleted();
static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (hasDeclarator(D)) return;
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
// Diagnostic is emitted elsewhere: here we store the (valid) Attr
// in the Decl node for syntactic reasoning, e.g., pretty-printing.
CallingConv CC;
- if (S.CheckCallingConvAttr(Attr, CC))
+ if (S.CheckCallingConvAttr(Attr, CC, FD))
return;
if (!isa<ObjCMethodDecl>(D)) {
D->addAttr(::new (S.Context) OpenCLKernelAttr(Attr.getRange(), S.Context));
}
-bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) {
+bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC,
+ const FunctionDecl *FD) {
if (attr.isInvalid())
return true;
TargetInfo::CallingConvCheckResult A = TI.checkCallingConvention(CC);
if (A == TargetInfo::CCCR_Warning) {
Diag(attr.getLoc(), diag::warn_cconv_ignored) << attr.getName();
- CC = TI.getDefaultCallingConv();
+
+ TargetInfo::CallingConvMethodType MT = TargetInfo::CCMT_Unknown;
+ if (FD)
+ MT = FD->isCXXInstanceMember() ? TargetInfo::CCMT_Member :
+ TargetInfo::CCMT_NonMember;
+ CC = TI.getDefaultCallingConv(MT);
}
return false;
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/CXXFieldCollector.h"
#include "clang/Sema/DeclSpec.h"
}
}
+bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,\r
+ const CXXMethodDecl *Old) {
+ const FunctionType *NewFT = New->getType()->getAs<FunctionType>();
+ const FunctionType *OldFT = Old->getType()->getAs<FunctionType>();
+
+ CallingConv NewCC = NewFT->getCallConv(), OldCC = OldFT->getCallConv();
+
+ // If the calling conventions match, everything is fine
+ if (NewCC == OldCC)
+ return false;
+
+ // If either of the calling conventions are set to "default", we need to pick
+ // something more sensible based on the target. This supports code where the
+ // one method explicitly sets thiscall, and another has no explicit calling
+ // convention.
+ CallingConv Default =
+ Context.getTargetInfo().getDefaultCallingConv(TargetInfo::CCMT_Member);
+ if (NewCC == CC_Default)
+ NewCC = Default;
+ if (OldCC == CC_Default)
+ OldCC = Default;
+
+ // If the calling conventions still don't match, then report the error
+ if (NewCC != OldCC) {
+ Diag(New->getLocation(),\r
+ diag::err_conflicting_overriding_cc_attributes)\r
+ << New->getDeclName() << New->getType() << Old->getType();\r
+ Diag(Old->getLocation(), diag::note_overridden_virtual_function);\r
+ return true;\r
+ }
+
+ return false;
+}
+
bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
const CXXMethodDecl *Old) {
QualType NewTy = New->getType()->getAs<FunctionType>()->getResultType();
--- /dev/null
+// RUN: %clang_cc1 -triple=x86_64-pc-unknown -fsyntax-only -verify %s
+
+// Non-x86 targets ignore the calling conventions by default (but will warn
+// when one is encountered), so we want to make sure the virtual overrides
+// continue to work.
+namespace PR14339 {
+ class A {
+ public:
+ virtual void __attribute__((thiscall)) f(); // expected-warning {{calling convention 'thiscall' ignored for this target}}
+ };
+
+ class B : public A {
+ public:
+ void __attribute__((cdecl)) f();
+ };
+
+ class C : public A {
+ public:
+ void __attribute__((thiscall)) f(); // expected-warning {{calling convention 'thiscall' ignored for this target}}
+ };
+
+ class D : public A {
+ public:
+ void f();
+ };
+
+ class E {
+ public:
+ virtual void __attribute__((stdcall)) g(); // expected-warning {{calling convention 'stdcall' ignored for this target}}
+ };
+
+ class F : public E {
+ public:
+ void g();
+ };
+}
--- /dev/null
+// RUN: %clang_cc1 -triple=i686-pc-unknown -fsyntax-only -verify %s -std=c++11
+
+namespace PR14339 {
+ class A {
+ public:
+ virtual void __attribute__((thiscall)) f(); // expected-note{{overridden virtual function is here}}
+ };
+
+ class B : public A {
+ public:
+ void __attribute__((cdecl)) f(); // expected-error{{virtual function 'f' has different calling convention attributes ('void () __attribute__((cdecl))') than the function it overrides (which has calling convention 'void () __attribute__((thiscall))'}}
+ };
+
+ class C : public A {
+ public:
+ void __attribute__((thiscall)) f(); // This override is correct
+ };
+
+ class D : public A {
+ public:
+ void f(); // This override is correct because thiscall is the default calling convention for class members
+ };
+
+ class E {
+ public:
+ virtual void __attribute__((stdcall)) g(); // expected-note{{overridden virtual function is here}}
+ };
+
+ class F : public E {
+ public:
+ void g(); // expected-error{{virtual function 'g' has different calling convention attributes ('void ()') than the function it overrides (which has calling convention 'void () __attribute__((stdcall))'}}
+ };
+}