From d1a32c328bce903fb1b17fc8147b646be818298e Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 8 Oct 2013 00:58:57 +0000 Subject: [PATCH] [ms-cxxabi] Fix the calling convention for operator new in records Summary: Operator new, new[], delete, and delete[] are all implicitly static when declared inside a record. CXXMethodDecl already knows this, but we need to account for that before we pick the calling convention for the function type. Fixes PR17371. Reviewers: rsmith CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D1761 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192150 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 12 +++++++ include/clang/Basic/OperatorKinds.h | 2 +- include/clang/Sema/DeclSpec.h | 10 ++++++ include/clang/Sema/Sema.h | 2 +- lib/AST/DeclCXX.cpp | 17 ++-------- lib/AST/MicrosoftMangle.cpp | 4 +-- lib/Sema/DeclSpec.cpp | 8 +++++ lib/Sema/SemaDecl.cpp | 6 ++-- lib/Sema/SemaType.cpp | 19 ++++++----- test/CodeGenCXX/mangle-ms.cpp | 52 +++++++++++++++++++++++++++++ 10 files changed, 100 insertions(+), 32 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 3c970059eb..effe58eac9 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1677,6 +1677,18 @@ public: bool isStatic() const; bool isInstance() const { return !isStatic(); } + /// Returns true if the given operator is implicitly static in a record + /// context. + static bool isStaticOverloadedOperator(OverloadedOperatorKind OOK) { + // [class.free]p1: + // Any allocation function for a class T is a static member + // (even if not explicitly declared static). + // [class.free]p6 Any deallocation function for a class X is a static member + // (even if not explicitly declared static). + return OOK == OO_New || OOK == OO_Array_New || OOK == OO_Delete || + OOK == OO_Array_Delete; + } + bool isConst() const { return getType()->castAs()->isConst(); } bool isVolatile() const { return getType()->castAs()->isVolatile(); } diff --git a/include/clang/Basic/OperatorKinds.h b/include/clang/Basic/OperatorKinds.h index 108014faab..2ceab9cbab 100644 --- a/include/clang/Basic/OperatorKinds.h +++ b/include/clang/Basic/OperatorKinds.h @@ -30,7 +30,7 @@ enum OverloadedOperatorKind { /// \brief Retrieve the spelling of the given overloaded operator, without /// the preceding "operator" keyword. const char *getOperatorSpelling(OverloadedOperatorKind Operator); - + } // end namespace clang #endif diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index dfb79325f2..a21bb3094b 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -2078,6 +2078,16 @@ public: return (FunctionDefinitionKind)FunctionDefinition; } + /// Returns true if this declares a real member and not a friend. + bool isFirstDeclarationOfMember() { + return getContext() == MemberContext && !getDeclSpec().isFriendSpecified(); + } + + /// Returns true if this declares a static member. This cannot be called on a + /// declarator outside of a MemberContext because we won't know until + /// redeclaration time if the decl is static. + bool isStaticMember(); + void setRedeclaration(bool Val) { Redeclaration = Val; } bool isRedeclaration() const { return Redeclaration; } }; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 82fa69cba0..ed0e9cc476 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2553,7 +2553,7 @@ public: /// Adjust the calling convention of a method to be the ABI default if it /// wasn't specified explicitly. This handles method types formed from /// function type typedefs and typename template arguments. - void adjustMemberFunctionCC(QualType &T); + void adjustMemberFunctionCC(QualType &T, bool IsStatic); /// Get the outermost AttributedType node that sets a calling convention. /// Valid types should not have multiple attributes with different CCs. diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index c6b6173efc..4c5028cc6f 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1327,21 +1327,8 @@ bool CXXMethodDecl::isStatic() const { if (MD->getStorageClass() == SC_Static) return true; - DeclarationName Name = getDeclName(); - // [class.free]p1: - // Any allocation function for a class T is a static member - // (even if not explicitly declared static). - if (Name.getCXXOverloadedOperator() == OO_New || - Name.getCXXOverloadedOperator() == OO_Array_New) - return true; - - // [class.free]p6 Any deallocation function for a class X is a static member - // (even if not explicitly declared static). - if (Name.getCXXOverloadedOperator() == OO_Delete || - Name.getCXXOverloadedOperator() == OO_Array_Delete) - return true; - - return false; + OverloadedOperatorKind OOK = getDeclName().getCXXOverloadedOperator(); + return isStaticOverloadedOperator(OOK); } static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD, diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index cd0063c4af..ab0db1acbf 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -316,9 +316,7 @@ void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { // We should never ever see a FunctionNoProtoType at this point. // We don't even know how to mangle their types anyway :). - TypeSourceInfo *TSI = FD->getTypeSourceInfo(); - QualType T = TSI ? TSI->getType() : FD->getType(); - const FunctionProtoType *FT = T->castAs(); + const FunctionProtoType *FT = FD->getType()->castAs(); // extern "C" functions can hold entities that must be mangled. // As it stands, these functions still need to get expressed in the full diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index e75dc7b1bc..5d4fce787e 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -13,6 +13,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TypeLoc.h" @@ -325,6 +326,13 @@ bool Declarator::isDeclarationOfFunction() const { llvm_unreachable("Invalid TypeSpecType!"); } +bool Declarator::isStaticMember() { + assert(getContext() == MemberContext); + return getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static || + CXXMethodDecl::isStaticOverloadedOperator( + getName().OperatorFunctionId.Operator); +} + /// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this /// declaration specifier includes. /// diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f2f203531f..1fe8a5f06b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6494,10 +6494,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, diag::err_invalid_thread) << DeclSpec::getSpecifierName(TSCS); - if (DC->isRecord() && - D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static && - !D.getDeclSpec().isFriendSpecified()) - adjustMemberFunctionCC(R); + if (D.isFirstDeclarationOfMember()) + adjustMemberFunctionCC(R, D.isStaticMember()); bool isFriend = false; FunctionTemplateDecl *FunctionTemplate = 0; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index accf2fdcaf..c327fffc67 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2494,12 +2494,11 @@ getCCForDeclaratorChunk(Sema &S, Declarator &D, assert(D.isFunctionDeclarator()); // If we're inside a record, we're declaring a method, but it could be - // static. + // explicitly or implicitly static. IsCXXInstanceMethod = - (D.getContext() == Declarator::MemberContext && - D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && - D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static && - !D.getDeclSpec().isFriendSpecified()); + D.isFirstDeclarationOfMember() && + D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && + !D.isStaticMember(); } } @@ -4580,13 +4579,17 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, return true; } -void Sema::adjustMemberFunctionCC(QualType &T) { +void Sema::adjustMemberFunctionCC(QualType &T, bool IsStatic) { const FunctionType *FT = T->castAs(); bool IsVariadic = (isa(FT) && cast(FT)->isVariadic()); CallingConv CC = FT->getCallConv(); + + // Only adjust types with the default convention. For example, on Windows we + // should adjust a __cdecl type to __thiscall for instance methods, and a + // __thiscall type to __cdecl for static methods. CallingConv DefaultCC = - Context.getDefaultCallingConvention(IsVariadic, /*IsCXXMethod=*/false); + Context.getDefaultCallingConvention(IsVariadic, IsStatic); if (CC != DefaultCC) return; @@ -4602,7 +4605,7 @@ void Sema::adjustMemberFunctionCC(QualType &T) { // FIXME: This loses sugar. This should probably be fixed with an implicit // AttributedType node that adjusts the convention. - CC = Context.getDefaultCallingConvention(IsVariadic, /*IsCXXMethod=*/true); + CC = Context.getDefaultCallingConvention(IsVariadic, !IsStatic); FT = Context.adjustFunctionType(FT, FT->getExtInfo().withCallingConv(CC)); FunctionTypeUnwrapper Unwrapped(*this, T); T = Unwrapped.wrap(*this, FT); diff --git a/test/CodeGenCXX/mangle-ms.cpp b/test/CodeGenCXX/mangle-ms.cpp index fd7c605c99..42d574fa53 100644 --- a/test/CodeGenCXX/mangle-ms.cpp +++ b/test/CodeGenCXX/mangle-ms.cpp @@ -304,3 +304,55 @@ inline int templated_inline_function_with_local_type() { int call_templated_inline_function_with_local_type() { return templated_inline_function_with_local_type(); } + +// PR17371 +struct OverloadedNewDelete { + // __cdecl + void *operator new(__SIZE_TYPE__); + void *operator new[](__SIZE_TYPE__); + void operator delete(void *); + void operator delete[](void *); + // __thiscall + int operator+(int); +}; + +void *OverloadedNewDelete::operator new(__SIZE_TYPE__ s) { return 0; } +void *OverloadedNewDelete::operator new[](__SIZE_TYPE__ s) { return 0; } +void OverloadedNewDelete::operator delete(void *) { } +void OverloadedNewDelete::operator delete[](void *) { } +int OverloadedNewDelete::operator+(int x) { return x; }; + +// CHECK-DAG: ??2OverloadedNewDelete@@SAPAXI@Z +// CHECK-DAG: ??_UOverloadedNewDelete@@SAPAXI@Z +// CHECK-DAG: ??3OverloadedNewDelete@@SAXPAX@Z +// CHECK-DAG: ??_VOverloadedNewDelete@@SAXPAX@Z +// CHECK-DAG: ??HOverloadedNewDelete@@QAEHH@Z + +// X64-DAG: ??2OverloadedNewDelete@@SAPEAX_K@Z +// X64-DAG: ??_UOverloadedNewDelete@@SAPEAX_K@Z +// X64-DAG: ??3OverloadedNewDelete@@SAXPEAX@Z +// X64-DAG: ??_VOverloadedNewDelete@@SAXPEAX@Z +// X64-DAG: ??HOverloadedNewDelete@@QEAAHH@Z + +// Indirecting the function type through a typedef will require a calling +// convention adjustment before building the method decl. + +typedef void *__thiscall OperatorNewType(__SIZE_TYPE__); +typedef void __thiscall OperatorDeleteType(void *); + +struct TypedefNewDelete { + OperatorNewType operator new; + OperatorNewType operator new[]; + OperatorDeleteType operator delete; + OperatorDeleteType operator delete[]; +}; + +void *TypedefNewDelete::operator new(__SIZE_TYPE__ s) { return 0; } +void *TypedefNewDelete::operator new[](__SIZE_TYPE__ s) { return 0; } +void TypedefNewDelete::operator delete(void *) { } +void TypedefNewDelete::operator delete[](void *) { } + +// CHECK-DAG: ??2TypedefNewDelete@@SAPAXI@Z +// CHECK-DAG: ??_UTypedefNewDelete@@SAPAXI@Z +// CHECK-DAG: ??3TypedefNewDelete@@SAXPAX@Z +// CHECK-DAG: ??_VTypedefNewDelete@@SAXPAX@Z -- 2.40.0