From 7c73083bd3d7b9be23004a97259b630ae563b6d2 Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Mon, 20 Jul 2015 22:57:31 +0000 Subject: [PATCH] Ignore the "novtable" declspec when not using the Microsoft C++ ABI. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang used to silently ignore __declspec(novtable). It is implemented now, but leaving the vtable uninitialized does not work when using the Itanium ABI, where the class layout for complex class hierarchies is stored in the vtable. It might be possible to honor the novtable attribute in some simple cases and either report an error or ignore it in more complex situations, but it’s not clear if that would be worthwhile. There is also value in having a simple and predictable behavior, so this changes clang to simply ignore novtable when not using the Microsoft C++ ABI. llvm-svn: 242730 --- clang/include/clang/Basic/Attr.td | 8 +++-- clang/include/clang/Basic/Attributes.h | 4 +-- clang/include/clang/Sema/AttributeList.h | 4 +-- clang/lib/Basic/Attributes.cpp | 4 +-- clang/lib/Lex/PPMacroExpansion.cpp | 6 ++-- clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Parse/ParseDeclCXX.cpp | 2 +- clang/lib/Sema/AttributeList.cpp | 7 ++-- clang/lib/Sema/SemaDeclAttr.cpp | 2 +- clang/test/Parser/MicrosoftExtensions.cpp | 2 +- clang/test/SemaCXX/ms-novtable.cpp | 2 +- clang/test/SemaCXX/ms-unsupported.cpp | 5 +++ clang/utils/TableGen/ClangAttrEmitter.cpp | 39 +++++++++++++++++++++-- 13 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 clang/test/SemaCXX/ms-unsupported.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 11b3263d705e..753e42c950ae 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -241,14 +241,18 @@ def COnly : LangOpt<"CPlusPlus", 1>; class TargetArch arches> { list Arches = arches; list OSes; + list CXXABIs; } def TargetARM : TargetArch<["arm", "thumb"]>; +def TargetMips : TargetArch<["mips", "mipsel"]>; def TargetMSP430 : TargetArch<["msp430"]>; def TargetX86 : TargetArch<["x86"]>; def TargetWindows : TargetArch<["x86", "x86_64", "arm", "thumb"]> { let OSes = ["Win32"]; } -def TargetMips : TargetArch<["mips", "mipsel"]>; +def TargetMicrosoftCXXABI : TargetArch<["x86", "x86_64", "arm", "thumb"]> { + let CXXABIs = ["Microsoft"]; +} class Attr { // The various ways in which an attribute can be spelled in source @@ -1820,7 +1824,7 @@ def TypeTagForDatatype : InheritableAttr { // Microsoft-related attributes -def MSNoVTable : InheritableAttr { +def MSNoVTable : InheritableAttr, TargetSpecificAttr { let Spellings = [Declspec<"novtable">]; let Subjects = SubjectList<[CXXRecord]>; let Documentation = [MSNoVTableDocs]; diff --git a/clang/include/clang/Basic/Attributes.h b/clang/include/clang/Basic/Attributes.h index a64dd5666b73..a2b86841cddf 100644 --- a/clang/include/clang/Basic/Attributes.h +++ b/clang/include/clang/Basic/Attributes.h @@ -11,7 +11,7 @@ #define LLVM_CLANG_BASIC_ATTRIBUTES_H #include "clang/Basic/LangOptions.h" -#include "llvm/ADT/Triple.h" +#include "clang/Basic/TargetInfo.h" namespace clang { @@ -31,7 +31,7 @@ enum class AttrSyntax { /// \brief Return the version number associated with the attribute if we /// recognize and implement the attribute specified by the given information. int hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope, - const IdentifierInfo *Attr, const llvm::Triple &T, + const IdentifierInfo *Attr, const TargetInfo &Target, const LangOptions &LangOpts); } // end namespace clang diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index 2fedc79401f0..ca456b2f9bb9 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -16,11 +16,11 @@ #define LLVM_CLANG_SEMA_ATTRIBUTELIST_H #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Basic/VersionTuple.h" #include "clang/Sema/Ownership.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/Triple.h" #include "llvm/Support/Allocator.h" #include @@ -464,7 +464,7 @@ public: bool hasVariadicArg() const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; bool diagnoseLangOpts(class Sema &S) const; - bool existsInTarget(const llvm::Triple &T) const; + bool existsInTarget(const TargetInfo &Target) const; bool isKnownToGCC() const; /// \brief If the parsed attribute has a semantic equivalent, and it would diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index da9ac793f163..c215366fc398 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -4,8 +4,8 @@ using namespace clang; int clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope, - const IdentifierInfo *Attr, const llvm::Triple &T, - const LangOptions &LangOpts) { + const IdentifierInfo *Attr, const TargetInfo &Target, + const LangOptions &LangOpts) { StringRef Name = Attr->getName(); // Normalize the attribute name, __foo__ becomes foo. if (Name.size() >= 4 && Name.startswith("__") && Name.endswith("__")) diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 1324ca93451f..42b1d048c40a 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1633,13 +1633,13 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Value = FeatureII->getBuiltinID() != 0; } else if (II == Ident__has_attribute) Value = hasAttribute(AttrSyntax::GNU, nullptr, FeatureII, - getTargetInfo().getTriple(), getLangOpts()); + getTargetInfo(), getLangOpts()); else if (II == Ident__has_cpp_attribute) Value = hasAttribute(AttrSyntax::CXX, ScopeII, FeatureII, - getTargetInfo().getTriple(), getLangOpts()); + getTargetInfo(), getLangOpts()); else if (II == Ident__has_declspec) Value = hasAttribute(AttrSyntax::Declspec, nullptr, FeatureII, - getTargetInfo().getTriple(), getLangOpts()); + getTargetInfo(), getLangOpts()); else if (II == Ident__has_extension) Value = HasExtension(*this, FeatureII); else { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f46af889e25d..acfe0ca0e82c 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -387,7 +387,7 @@ bool Parser::ParseMicrosoftDeclSpecArgs(IdentifierInfo *AttrName, // If the attribute isn't known, we will not attempt to parse any // arguments. if (!hasAttribute(AttrSyntax::Declspec, nullptr, AttrName, - getTargetInfo().getTriple(), getLangOpts())) { + getTargetInfo(), getLangOpts())) { // Eat the left paren, then skip to the ending right paren. ConsumeParen(); SkipUntil(tok::r_paren); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index e347d4e27e26..0fb1de94e8ea 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3612,7 +3612,7 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, // If the attribute isn't known, we will not attempt to parse any // arguments. if (!hasAttribute(AttrSyntax::CXX, ScopeName, AttrName, - getTargetInfo().getTriple(), getLangOpts())) { + getTargetInfo(), getLangOpts())) { // Eat the left paren, then skip to the ending right paren. ConsumeParen(); SkipUntil(tok::r_paren); diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp index 34af6cf63c87..1ea40606f44b 100644 --- a/clang/lib/Sema/AttributeList.cpp +++ b/clang/lib/Sema/AttributeList.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Sema/SemaInternal.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -155,7 +156,7 @@ struct ParsedAttrInfo { bool (*DiagAppertainsToDecl)(Sema &S, const AttributeList &Attr, const Decl *); bool (*DiagLangOpts)(Sema &S, const AttributeList &Attr); - bool (*ExistsInTarget)(const llvm::Triple &T); + bool (*ExistsInTarget)(const TargetInfo &Target); unsigned (*SpellingIndexToSemanticSpelling)(const AttributeList &Attr); }; @@ -195,8 +196,8 @@ bool AttributeList::isTypeAttr() const { return getInfo(*this).IsType; } -bool AttributeList::existsInTarget(const llvm::Triple &T) const { - return getInfo(*this).ExistsInTarget(T); +bool AttributeList::existsInTarget(const TargetInfo &Target) const { + return getInfo(*this).ExistsInTarget(Target); } bool AttributeList::isKnownToGCC() const { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9ec3356d048f..3395381b6a36 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4583,7 +4583,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, // which do not apply to the current target architecture are treated as // though they were unknown attributes. if (Attr.getKind() == AttributeList::UnknownAttribute || - !Attr.existsInTarget(S.Context.getTargetInfo().getTriple())) { + !Attr.existsInTarget(S.Context.getTargetInfo())) { S.Diag(Attr.getLoc(), Attr.isDeclspecAttribute() ? diag::warn_unhandled_ms_attribute_ignored : diag::warn_unknown_attribute_ignored) diff --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp index e674d0101e97..efb9490697e7 100644 --- a/clang/test/Parser/MicrosoftExtensions.cpp +++ b/clang/test/Parser/MicrosoftExtensions.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -triple i386-mingw32 -std=c++14 -fsyntax-only -Wno-unused-getter-return-value -Wno-unused-value -Wmicrosoft -verify -fms-extensions -fms-compatibility -fdelayed-template-parsing +// RUN: %clang_cc1 %s -triple i386-pc-win32 -std=c++14 -fsyntax-only -Wno-unused-getter-return-value -Wno-unused-value -Wmicrosoft -verify -fms-extensions -fms-compatibility -fdelayed-template-parsing /* Microsoft attribute tests */ [repeatable][source_annotation_attribute( Parameter|ReturnValue )] diff --git a/clang/test/SemaCXX/ms-novtable.cpp b/clang/test/SemaCXX/ms-novtable.cpp index 2d55c48df3c7..31975b3b4a84 100644 --- a/clang/test/SemaCXX/ms-novtable.cpp +++ b/clang/test/SemaCXX/ms-novtable.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify -fms-extensions -Wno-microsoft -std=c++11 +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify -fms-extensions -Wno-microsoft -std=c++11 struct __declspec(novtable) S {}; enum __declspec(novtable) E {}; // expected-warning{{'novtable' attribute only applies to classes}} diff --git a/clang/test/SemaCXX/ms-unsupported.cpp b/clang/test/SemaCXX/ms-unsupported.cpp new file mode 100644 index 000000000000..559d703f3673 --- /dev/null +++ b/clang/test/SemaCXX/ms-unsupported.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -triple x86_64-pc-windows-gnu %s -fsyntax-only -verify -fms-extensions -Wno-microsoft -std=c++11 + +// "novtable" is ignored except with the Microsoft C++ ABI. +// MinGW uses the Itanium C++ ABI so check that it is ignored there. +struct __declspec(novtable) S {}; // expected-warning{{__declspec attribute 'novtable' is not supported}} diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 5dc33a000a42..c96777d1e89a 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1885,7 +1885,8 @@ static void GenerateHasAttrSpellingStringSwitch( } } - // It is assumed that there will be an llvm::Triple object named T within + // It is assumed that there will be an llvm::Triple object + // named "T" and a TargetInfo object named "Target" within // scope that can be used to determine whether the attribute exists in // a given target. std::string Test; @@ -1902,6 +1903,7 @@ static void GenerateHasAttrSpellingStringSwitch( } Test += ")"; + // If the attribute is specific to particular OSes, check those. std::vector OSes; if (!R->isValueUnset("OSes")) { Test += " && ("; @@ -1916,6 +1918,21 @@ static void GenerateHasAttrSpellingStringSwitch( Test += ")"; } + // If one or more CXX ABIs are specified, check those as well. + if (!R->isValueUnset("CXXABIs")) { + Test += " && ("; + std::vector CXXABIs = + R->getValueAsListOfStrings("CXXABIs"); + for (auto AI = CXXABIs.begin(), AE = CXXABIs.end(); AI != AE; ++AI) { + std::string Part = *AI; + + Test += "Target.getCXXABI().getKind() == TargetCXXABI::" + Part; + if (AI + 1 != AE) + Test += " || "; + } + Test += ")"; + } + // If this is the C++11 variety, also add in the LangOpts test. if (Variety == "CXX11") Test += " && LangOpts.CPlusPlus11"; @@ -1962,6 +1979,7 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { } } + OS << "const llvm::Triple &T = Target.getTriple();\n"; OS << "switch (Syntax) {\n"; OS << "case AttrSyntax::GNU:\n"; OS << " return llvm::StringSwitch(Name)\n"; @@ -2464,7 +2482,7 @@ static std::string GenerateLangOptRequirements(const Record &R, } static void GenerateDefaultTargetRequirements(raw_ostream &OS) { - OS << "static bool defaultTargetRequirements(const llvm::Triple &) {\n"; + OS << "static bool defaultTargetRequirements(const TargetInfo &) {\n"; OS << " return true;\n"; OS << "}\n\n"; } @@ -2533,6 +2551,20 @@ static std::string GenerateTargetRequirements(const Record &Attr, Test += ")"; } + // Test for the C++ ABI, if specified. + if (!R->isValueUnset("CXXABIs")) { + Test += " && ("; + std::vector CXXABIs = R->getValueAsListOfStrings("CXXABIs"); + for (auto I = CXXABIs.begin(), E = CXXABIs.end(); I != E; ++I) { + std::string Part = *I; + Test += "Target.getCXXABI().getKind() == TargetCXXABI::" + Part; + if (I + 1 != E) + Test += " || "; + FnName += Part; + } + Test += ")"; + } + // If this code has already been generated, simply return the previous // instance of it. static std::set CustomTargetSet; @@ -2540,7 +2572,8 @@ static std::string GenerateTargetRequirements(const Record &Attr, if (I != CustomTargetSet.end()) return *I; - OS << "static bool " << FnName << "(const llvm::Triple &T) {\n"; + OS << "static bool " << FnName << "(const TargetInfo &Target) {\n"; + OS << " const llvm::Triple &T = Target.getTriple();\n"; OS << " llvm::Triple::ArchType Arch = T.getArch();\n"; if (UsesOS) OS << " llvm::Triple::OSType OS = T.getOS();\n";