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:
Aaron Ballman 2014-11-14 13:44:02 +00:00
parent c670688add
commit a0344c5d7b
9 changed files with 156 additions and 33 deletions

View File

@ -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``
-------------------

View File

@ -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];
}

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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 ||

View 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

View File

@ -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 << "}";
}

View File

@ -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>