From 3e08f66bb0f46d1065671afb6357d9162213606e Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Fri, 29 Sep 2017 21:06:00 +0000 Subject: [PATCH] [Sema] Correct IUnknown to support Unknwnbase.h Header. Apparently, the MSVC SDK has a strange implementation that causes a number of implicit functions as well as a template member function of the IUnknown type. This patch allows these as InterfaceLike types as well. Additionally, it corrects the behavior where extern-C++ wrapped around an Interface-Like type would permit an interface-like type to exist in a namespace. Differential Revision: https://reviews.llvm.org/D38303 llvm-svn: 314557 --- clang/lib/AST/DeclCXX.cpp | 18 ++++++++-- .../test/SemaCXX/ms-iunknown-template-function.cpp | 39 ++++++++++++++++++++++ clang/test/SemaCXX/ms-iunknown.cpp | 19 ++++++++--- 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 clang/test/SemaCXX/ms-iunknown-template-function.cpp diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index e85ab41..a9759b4 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1470,6 +1470,15 @@ bool CXXRecordDecl::isAnyDestructorNoReturn() const { return false; } +static bool isDeclContextInNamespace(const DeclContext *DC) { + while (!DC->isTranslationUnit()) { + if (DC->isNamespace()) + return true; + DC = DC->getParent(); + } + return false; +} + bool CXXRecordDecl::isInterfaceLike() const { assert(hasDefinition() && "checking for interface-like without a definition"); // All __interfaces are inheritently interface-like. @@ -1486,13 +1495,16 @@ bool CXXRecordDecl::isInterfaceLike() const { // No interface-like type can have a method with a definition. for (const auto *const Method : methods()) - if (Method->isDefined()) + if (Method->isDefined() && !Method->isImplicit()) return false; // Check "Special" types. const auto *Uuid = getAttr(); - if (Uuid && isStruct() && (getDeclContext()->isTranslationUnit() || - getDeclContext()->isExternCXXContext()) && + // MS SDK declares IUnknown/IDispatch both in the root of a TU, or in an + // extern C++ block directly in the TU. These are only valid if in one + // of these two situations. + if (Uuid && isStruct() && !getDeclContext()->isExternCContext() && + !isDeclContextInNamespace(getDeclContext()) && ((getName() == "IUnknown" && Uuid->getGuid() == "00000000-0000-0000-C000-000000000046") || (getName() == "IDispatch" && diff --git a/clang/test/SemaCXX/ms-iunknown-template-function.cpp b/clang/test/SemaCXX/ms-iunknown-template-function.cpp new file mode 100644 index 0000000..dc19223 --- /dev/null +++ b/clang/test/SemaCXX/ms-iunknown-template-function.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fms-extensions %s +typedef long HRESULT; +typedef unsigned long ULONG; +typedef struct _GUID { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +} GUID; +typedef GUID IID; + +// remove stdcall, since the warnings have nothing to do with +// what is being tested. +#define __stdcall + +extern "C" { +extern "C++" { +// expected-warning@+1 {{__declspec attribute 'novtable'}} +struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) __declspec(novtable) + IUnknown { +public: + virtual HRESULT __stdcall QueryInterface( + const IID &riid, + void **ppvObject) = 0; + + virtual ULONG __stdcall AddRef(void) = 0; + + virtual ULONG __stdcall Release(void) = 0; + + template + HRESULT __stdcall QueryInterface(Q **pp) { + return QueryInterface(__uuidof(Q), (void **)pp); + } +}; +} +} + +__interface ISfFileIOPropertyPage : public IUnknown{}; + diff --git a/clang/test/SemaCXX/ms-iunknown.cpp b/clang/test/SemaCXX/ms-iunknown.cpp index f73864d..df761d5 100644 --- a/clang/test/SemaCXX/ms-iunknown.cpp +++ b/clang/test/SemaCXX/ms-iunknown.cpp @@ -2,7 +2,11 @@ extern "C++" struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) IUnknown { void foo(); + // Definitions aren't allowed, unless they are a template. + template + void bar(T t){} }; + struct IPropertyPageBase : public IUnknown {}; struct IPropertyPage : public IPropertyPageBase {}; __interface ISfFileIOPropertyPage : public IPropertyPage {}; @@ -11,10 +15,17 @@ __interface ISfFileIOPropertyPage : public IPropertyPage {}; namespace NS { struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) IUnknown {}; // expected-error@+1 {{interface type cannot inherit from}} - __interface IPropertyPageBase : public IUnknown {}; + __interface IPropertyPageBase : public IUnknown {}; } + +namespace NS2 { +extern "C++" struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) IUnknown {}; +// expected-error@+1 {{interface type cannot inherit from}} +__interface IPropertyPageBase : public IUnknown{}; +} + // expected-error@+1 {{interface type cannot inherit from}} -__interface IPropertyPageBase2 : public NS::IUnknown {}; +__interface IPropertyPageBase2 : public NS::IUnknown {}; __interface temp_iface {}; struct bad_base : temp_iface {}; @@ -32,8 +43,8 @@ __interface PropertyPage : public Page4 {}; struct Page5 : public Page3, Page4{}; // expected-error@+1 {{interface type cannot inherit from}} -__interface PropertyPage2 : public Page5 {}; +__interface PropertyPage2 : public Page5 {}; __interface IF1 {}; -__interface PP : IUnknown, IF1{}; +__interface PP : IUnknown, IF1{}; __interface PP2 : PP, Page3, Page4{}; -- 2.7.4