mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-05 04:38:37 +00:00
Complete support for the SD-6 standing document (based off N4200) with support for __has_cpp_attribute.
llvm-svn: 221991
This commit is contained in:
parent
c670688add
commit
a0344c5d7b
@ -109,6 +109,36 @@ following ``__`` (double underscore) to avoid interference from a macro with
|
||||
the same name. For instance, ``__cxx_rvalue_references__`` can be used instead
|
||||
of ``cxx_rvalue_references``.
|
||||
|
||||
``__has_cpp_attribute``
|
||||
-------------------
|
||||
|
||||
This function-like macro takes a single argument that is the name of a
|
||||
C++11-style attribute. The argument can either be a single identifier, or a
|
||||
scoped identifier. If the attribute is supported, a nonzero value is returned.
|
||||
If the attribute is a standards-based attribute, this macro returns a nonzero
|
||||
value based on the year and month in which the attribute was voted into the
|
||||
working draft. If the attribute is not supported by the current compliation
|
||||
target, this macro evaluates to 0. It can be used like this:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#ifndef __has_cpp_attribute // Optional of course.
|
||||
#define __has_cpp_attribute(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
...
|
||||
#if __has_cpp_attribute(clang::fallthrough)
|
||||
#define FALLTHROUGH [[clang::fallthrough]]
|
||||
#else
|
||||
#define FALLTHROUGH
|
||||
#endif
|
||||
...
|
||||
|
||||
The attribute identifier (but not scope) can also be specified with a preceding
|
||||
and following ``__`` (double underscore) to avoid interference from a macro with
|
||||
the same name. For instance, ``gnu::__const__`` can be used instead of
|
||||
``gnu::const``.
|
||||
|
||||
``__has_attribute``
|
||||
-------------------
|
||||
|
||||
|
@ -186,10 +186,11 @@ class Spelling<string name, string variety> {
|
||||
|
||||
class GNU<string name> : Spelling<name, "GNU">;
|
||||
class Declspec<string name> : Spelling<name, "Declspec">;
|
||||
class CXX11<string namespace, string name> : Spelling<name, "CXX11"> {
|
||||
class CXX11<string namespace, string name, int version = 1>
|
||||
: Spelling<name, "CXX11"> {
|
||||
string Namespace = namespace;
|
||||
}
|
||||
class Keyword<string name> : Spelling<name, "Keyword">;
|
||||
int Version = version;
|
||||
} class Keyword<string name> : Spelling<name, "Keyword">;
|
||||
class Pragma<string namespace, string name> : Spelling<name, "Pragma"> {
|
||||
string Namespace = namespace;
|
||||
}
|
||||
@ -452,7 +453,8 @@ def Bounded : IgnoredAttr {
|
||||
}
|
||||
|
||||
def CarriesDependency : InheritableParamAttr {
|
||||
let Spellings = [GNU<"carries_dependency">, CXX11<"","carries_dependency">];
|
||||
let Spellings = [GNU<"carries_dependency">,
|
||||
CXX11<"","carries_dependency", 200809>];
|
||||
let Subjects = SubjectList<[ParmVar, ObjCMethod, Function], ErrorDiag>;
|
||||
let Documentation = [CarriesDependencyDocs];
|
||||
}
|
||||
@ -593,7 +595,7 @@ def C11NoReturn : InheritableAttr {
|
||||
}
|
||||
|
||||
def CXX11NoReturn : InheritableAttr {
|
||||
let Spellings = [CXX11<"","noreturn">];
|
||||
let Spellings = [CXX11<"","noreturn", 200809>];
|
||||
let Subjects = SubjectList<[Function], ErrorDiag>;
|
||||
let Documentation = [CXX11NoReturnDocs];
|
||||
}
|
||||
@ -642,7 +644,7 @@ def OpenCLConstantAddressSpace : TypeAttr {
|
||||
|
||||
def Deprecated : InheritableAttr {
|
||||
let Spellings = [GCC<"deprecated">, Declspec<"deprecated">,
|
||||
CXX11<"","deprecated">];
|
||||
CXX11<"","deprecated", 201309>];
|
||||
let Args = [StringArgument<"Message", 1>];
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ enum class AttrSyntax {
|
||||
Pragma
|
||||
};
|
||||
|
||||
/// \brief Return true if we recognize and implement the attribute specified by
|
||||
/// the given information.
|
||||
bool hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
|
||||
const IdentifierInfo *Attr, const llvm::Triple &T,
|
||||
const LangOptions &LangOpts);
|
||||
/// \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 LangOptions &LangOpts);
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
|
@ -135,6 +135,7 @@ class Preprocessor : public RefCountedBase<Preprocessor> {
|
||||
IdentifierInfo *Ident__is_identifier; // __is_identifier
|
||||
IdentifierInfo *Ident__building_module; // __building_module
|
||||
IdentifierInfo *Ident__MODULE__; // __MODULE__
|
||||
IdentifierInfo *Ident__has_cpp_attribute; // __has_cpp_attribute
|
||||
|
||||
SourceLocation DATELoc, TIMELoc;
|
||||
unsigned CounterValue; // Next __COUNTER__ value.
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
using namespace clang;
|
||||
|
||||
bool clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
|
||||
int clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
|
||||
const IdentifierInfo *Attr, const llvm::Triple &T,
|
||||
const LangOptions &LangOpts) {
|
||||
StringRef Name = Attr->getName();
|
||||
@ -13,5 +13,5 @@ bool clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
|
||||
|
||||
#include "clang/Basic/AttrHasAttributeImpl.inc"
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
@ -96,6 +96,9 @@ void Preprocessor::RegisterBuiltinMacros() {
|
||||
Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
|
||||
Ident_Pragma = RegisterBuiltinMacro(*this, "_Pragma");
|
||||
|
||||
// C++ Standing Document Extensions.
|
||||
Ident__has_cpp_attribute = RegisterBuiltinMacro(*this, "__has_cpp_attribute");
|
||||
|
||||
// GCC Extensions.
|
||||
Ident__BASE_FILE__ = RegisterBuiltinMacro(*this, "__BASE_FILE__");
|
||||
Ident__INCLUDE_LEVEL__ = RegisterBuiltinMacro(*this, "__INCLUDE_LEVEL__");
|
||||
@ -1374,12 +1377,14 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
|
||||
II == Ident__has_extension ||
|
||||
II == Ident__has_builtin ||
|
||||
II == Ident__is_identifier ||
|
||||
II == Ident__has_attribute) {
|
||||
II == Ident__has_attribute ||
|
||||
II == Ident__has_cpp_attribute) {
|
||||
// The argument to these builtins should be a parenthesized identifier.
|
||||
SourceLocation StartLoc = Tok.getLocation();
|
||||
|
||||
bool IsValid = false;
|
||||
IdentifierInfo *FeatureII = nullptr;
|
||||
IdentifierInfo *ScopeII = nullptr;
|
||||
|
||||
// Read the '('.
|
||||
LexUnexpandedToken(Tok);
|
||||
@ -1387,14 +1392,26 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
|
||||
// Read the identifier
|
||||
LexUnexpandedToken(Tok);
|
||||
if ((FeatureII = Tok.getIdentifierInfo())) {
|
||||
// Read the ')'.
|
||||
// If we're checking __has_cpp_attribute, it is possible to receive a
|
||||
// scope token. Read the "::", if it's available.
|
||||
LexUnexpandedToken(Tok);
|
||||
if (Tok.is(tok::r_paren))
|
||||
bool IsScopeValid = true;
|
||||
if (II == Ident__has_cpp_attribute && Tok.is(tok::coloncolon)) {
|
||||
LexUnexpandedToken(Tok);
|
||||
// The first thing we read was not the feature, it was the scope.
|
||||
ScopeII = FeatureII;
|
||||
if (FeatureII = Tok.getIdentifierInfo())
|
||||
LexUnexpandedToken(Tok);
|
||||
else
|
||||
IsScopeValid = false;
|
||||
}
|
||||
// Read the closing paren.
|
||||
if (IsScopeValid && Tok.is(tok::r_paren))
|
||||
IsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Value = false;
|
||||
int Value = 0;
|
||||
if (!IsValid)
|
||||
Diag(StartLoc, diag::err_feature_check_malformed);
|
||||
else if (II == Ident__is_identifier)
|
||||
@ -1405,6 +1422,9 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
|
||||
} else if (II == Ident__has_attribute)
|
||||
Value = hasAttribute(AttrSyntax::Generic, nullptr, FeatureII,
|
||||
getTargetInfo().getTriple(), getLangOpts());
|
||||
else if (II == Ident__has_cpp_attribute)
|
||||
Value = hasAttribute(AttrSyntax::CXX, ScopeII, FeatureII,
|
||||
getTargetInfo().getTriple(), getLangOpts());
|
||||
else if (II == Ident__has_extension)
|
||||
Value = HasExtension(*this, FeatureII);
|
||||
else {
|
||||
@ -1412,7 +1432,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
|
||||
Value = HasFeature(*this, FeatureII);
|
||||
}
|
||||
|
||||
OS << (int)Value;
|
||||
OS << Value;
|
||||
if (IsValid)
|
||||
Tok.setKind(tok::numeric_constant);
|
||||
} else if (II == Ident__has_include ||
|
||||
|
53
clang/test/Preprocessor/has_attribute.cpp
Normal file
53
clang/test/Preprocessor/has_attribute.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
// RUN: %clang_cc1 -triple i386-unknown-unknown -std=c++11 -E %s -o - | FileCheck %s
|
||||
|
||||
// CHECK: has_cxx11_carries_dep
|
||||
#if __has_cpp_attribute(carries_dependency)
|
||||
int has_cxx11_carries_dep();
|
||||
#endif
|
||||
|
||||
// CHECK: has_clang_fallthrough
|
||||
#if __has_cpp_attribute(clang::fallthrough)
|
||||
int has_clang_fallthrough();
|
||||
#endif
|
||||
|
||||
// CHECK: does_not_have_selectany
|
||||
#if !__has_cpp_attribute(selectany)
|
||||
int does_not_have_selectany();
|
||||
#endif
|
||||
|
||||
// The attribute name can be bracketed with double underscores.
|
||||
// CHECK: has_clang_fallthrough_2
|
||||
#if __has_cpp_attribute(clang::__fallthrough__)
|
||||
int has_clang_fallthrough_2();
|
||||
#endif
|
||||
|
||||
// The scope cannot be bracketed with double underscores.
|
||||
// CHECK: does_not_have___clang___fallthrough
|
||||
#if !__has_cpp_attribute(__clang__::fallthrough)
|
||||
int does_not_have___clang___fallthrough();
|
||||
#endif
|
||||
|
||||
// Test that C++11, target-specific attributes behave properly.
|
||||
|
||||
// CHECK: does_not_have_mips16
|
||||
#if !__has_cpp_attribute(gnu::mips16)
|
||||
int does_not_have_mips16();
|
||||
#endif
|
||||
|
||||
// Test that the version numbers of attributes listed in SD-6 are supported
|
||||
// correctly.
|
||||
|
||||
// CHECK: has_cxx11_carries_dep_vers
|
||||
#if __has_cpp_attribute(carries_dependency) == 200809
|
||||
int has_cxx11_carries_dep_vers();
|
||||
#endif
|
||||
|
||||
// CHECK: has_cxx11_noreturn_vers
|
||||
#if __has_cpp_attribute(noreturn) == 200809
|
||||
int has_cxx11_noreturn_vers();
|
||||
#endif
|
||||
|
||||
// CHECK: has_cxx14_deprecated_vers
|
||||
#if __has_cpp_attribute(deprecated) == 201309
|
||||
int has_cxx14_deprecated_vers();
|
||||
#endif
|
@ -1820,6 +1820,27 @@ static void GenerateHasAttrSpellingStringSwitch(
|
||||
const std::vector<Record *> &Attrs, raw_ostream &OS,
|
||||
const std::string &Variety = "", const std::string &Scope = "") {
|
||||
for (const auto *Attr : Attrs) {
|
||||
// C++11-style attributes have specific version information associated with
|
||||
// them. If the attribute has no scope, the version information must not
|
||||
// have the default value (1), as that's incorrect. Instead, the unscoped
|
||||
// attribute version information should be taken from the SD-6 standing
|
||||
// document, which can be found at:
|
||||
// https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
|
||||
int Version = 1;
|
||||
|
||||
if (Variety == "CXX11") {
|
||||
std::vector<Record *> Spellings = Attr->getValueAsListOfDefs("Spellings");
|
||||
for (const auto &Spelling : Spellings) {
|
||||
if (Spelling->getValueAsString("Variety") == "CXX11") {
|
||||
Version = static_cast<int>(Spelling->getValueAsInt("Version"));
|
||||
if (Scope.empty() && Version == 1)
|
||||
PrintError(Spelling->getLoc(), "C++ standard attributes must "
|
||||
"have valid version information.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It is assumed that there will be an llvm::Triple object named T within
|
||||
// scope that can be used to determine whether the attribute exists in
|
||||
// a given target.
|
||||
@ -1858,16 +1879,16 @@ static void GenerateHasAttrSpellingStringSwitch(
|
||||
// C++11 mode should be checked against LangOpts, which is presumed to be
|
||||
// present in the caller.
|
||||
Test = "LangOpts.CPlusPlus11";
|
||||
else
|
||||
Test = "true";
|
||||
|
||||
std::string TestStr =
|
||||
!Test.empty() ? Test + " ? " + std::to_string(Version) + " : 0" : "1";
|
||||
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
|
||||
for (const auto &S : Spellings)
|
||||
if (Variety.empty() || (Variety == S.variety() &&
|
||||
(Scope.empty() || Scope == S.nameSpace())))
|
||||
OS << " .Case(\"" << S.name() << "\", " << Test << ")\n";
|
||||
OS << " .Case(\"" << S.name() << "\", " << TestStr << ")\n";
|
||||
}
|
||||
OS << " .Default(false);\n";
|
||||
OS << " .Default(0);\n";
|
||||
}
|
||||
|
||||
// Emits the list of spellings for attributes.
|
||||
@ -1899,16 +1920,16 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
||||
|
||||
OS << "switch (Syntax) {\n";
|
||||
OS << "case AttrSyntax::Generic:\n";
|
||||
OS << " return llvm::StringSwitch<bool>(Name)\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(Attrs, OS);
|
||||
OS << "case AttrSyntax::GNU:\n";
|
||||
OS << " return llvm::StringSwitch<bool>(Name)\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU");
|
||||
OS << "case AttrSyntax::Declspec:\n";
|
||||
OS << " return llvm::StringSwitch<bool>(Name)\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec");
|
||||
OS << "case AttrSyntax::Pragma:\n";
|
||||
OS << " return llvm::StringSwitch<bool>(Name)\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma");
|
||||
OS << "case AttrSyntax::CXX: {\n";
|
||||
// C++11-style attributes are further split out based on the Scope.
|
||||
@ -1921,7 +1942,7 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
||||
OS << "if (!Scope || Scope->getName() == \"\") {\n";
|
||||
else
|
||||
OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
|
||||
OS << " return llvm::StringSwitch<bool>(Name)\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first);
|
||||
OS << "}";
|
||||
}
|
||||
|
@ -590,8 +590,8 @@ Clang version they became available:</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="partial" align="center">
|
||||
SVN (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4200">N4200</a>): Partial <a href="#n4200">(1)</a>
|
||||
<td class="full" align="center">
|
||||
SVN (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4200">N4200</a>)</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -611,10 +611,6 @@ Clang version they became available:</p>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<span id="n4200">(1): <code>__has_cpp_attribute</code> is not yet supported.</span>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user