Index: include/clang/Sema/ParsedAttr.h =================================================================== --- include/clang/Sema/ParsedAttr.h +++ include/clang/Sema/ParsedAttr.h @@ -383,6 +383,11 @@ IdentifierInfo *getScopeName() const { return ScopeName; } SourceLocation getScopeLoc() const { return ScopeLoc; } + bool isGNUScope() const { + return ScopeName && + (ScopeName->isStr("gnu") || ScopeName->isStr("__gnu__")); + } + bool hasParsedType() const { return HasParsedType; } /// Is this the Microsoft __declspec(property) attribute? Index: lib/Basic/Attributes.cpp =================================================================== --- lib/Basic/Attributes.cpp +++ lib/Basic/Attributes.cpp @@ -12,6 +12,11 @@ if (Name.size() >= 4 && Name.startswith("__") && Name.endswith("__")) Name = Name.substr(2, Name.size() - 4); + // Normalize the scope name, but only for gnu attributes. + StringRef ScopeName = Scope ? Scope->getName() : ""; + if (ScopeName == "__gnu__") + ScopeName = ScopeName.slice(2, ScopeName.size() - 2); + #include "clang/Basic/AttrHasAttributeImpl.inc" return 0; Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -3868,7 +3868,8 @@ return false; } - if (ScopeName && ScopeName->getName() == "gnu") { + if (ScopeName && + (ScopeName->getName() == "gnu" || ScopeName->getName() == "__gnu__")) { // GNU-scoped attributes have some special cases to handle GNU-specific // behaviors. ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, Index: lib/Sema/ParsedAttr.cpp =================================================================== --- lib/Sema/ParsedAttr.cpp +++ lib/Sema/ParsedAttr.cpp @@ -103,14 +103,25 @@ #include "clang/Sema/AttrParsedAttrKinds.inc" -static StringRef normalizeAttrName(StringRef AttrName, StringRef ScopeName, +static StringRef normalizeAttrScopeName(StringRef ScopeName, + ParsedAttr::Syntax SyntaxUsed) { + // We currently only normalize the "__gnu__" scope name to be "gnu". + if ((SyntaxUsed == ParsedAttr::AS_CXX11 || + SyntaxUsed == ParsedAttr::AS_C2x) && + ScopeName == "__gnu__") + ScopeName = ScopeName.slice(2, ScopeName.size() - 2); + return ScopeName; +} + +static StringRef normalizeAttrName(StringRef AttrName, + StringRef NormalizedScopeName, ParsedAttr::Syntax SyntaxUsed) { // Normalize the attribute name, __foo__ becomes foo. This is only allowable - // for GNU attributes. + // for GNU attributes, and attributes using the double square bracket syntax. bool IsGNU = SyntaxUsed == ParsedAttr::AS_GNU || ((SyntaxUsed == ParsedAttr::AS_CXX11 || SyntaxUsed == ParsedAttr::AS_C2x) && - ScopeName == "gnu"); + NormalizedScopeName == "gnu"); if (IsGNU && AttrName.size() >= 4 && AttrName.startswith("__") && AttrName.endswith("__")) AttrName = AttrName.slice(2, AttrName.size() - 2); @@ -125,7 +136,7 @@ SmallString<64> FullName; if (ScopeName) - FullName += ScopeName->getName(); + FullName += normalizeAttrScopeName(ScopeName->getName(), SyntaxUsed); AttrName = normalizeAttrName(AttrName, FullName, SyntaxUsed); @@ -141,9 +152,10 @@ unsigned ParsedAttr::getAttributeSpellingListIndex() const { // Both variables will be used in tablegen generated // attribute spell list index matching code. - StringRef Scope = ScopeName ? ScopeName->getName() : ""; - StringRef Name = normalizeAttrName(AttrName->getName(), Scope, - (ParsedAttr::Syntax)SyntaxUsed); + auto Syntax = static_cast(SyntaxUsed); + StringRef Scope = + ScopeName ? normalizeAttrScopeName(ScopeName->getName(), Syntax) : ""; + StringRef Name = normalizeAttrName(AttrName->getName(), Scope, Syntax); #include "clang/Sema/AttrSpellingListIndex.inc" Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -5834,10 +5834,8 @@ !S.checkStringLiteralArgumentAttr(AL, 1, Replacement)) return; - if (!S.getLangOpts().CPlusPlus14) - if (AL.isCXX11Attribute() && - !(AL.hasScope() && AL.getScopeName()->isStr("gnu"))) - S.Diag(AL.getLoc(), diag::ext_cxx14_attr) << AL; + if (!S.getLangOpts().CPlusPlus14 && AL.isCXX11Attribute() && !AL.isGNUScope()) + S.Diag(AL.getLoc(), diag::ext_cxx14_attr) << AL; D->addAttr(::new (S.Context) DeprecatedAttr(AL.getRange(), S.Context, Str, Replacement, Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -7273,7 +7273,7 @@ // not appertain to a DeclaratorChunk. If we handle them as type // attributes, accept them in that position and diagnose the GCC // incompatibility. - if (attr.getScopeName() && attr.getScopeName()->isStr("gnu")) { + if (attr.isGNUScope()) { bool IsTypeAttr = attr.isTypeAttr(); if (TAL == TAL_DeclChunk) { state.getSema().Diag(attr.getLoc(), Index: test/Preprocessor/has_attribute.cpp =================================================================== --- test/Preprocessor/has_attribute.cpp +++ test/Preprocessor/has_attribute.cpp @@ -21,11 +21,15 @@ int has_clang_fallthrough_2(); #endif -// The scope cannot be bracketed with double underscores. +// The scope cannot be bracketed with double underscores unless it is for gnu. // CHECK: does_not_have___clang___fallthrough #if !__has_cpp_attribute(__clang__::fallthrough) int does_not_have___clang___fallthrough(); #endif +// CHECK: has_gnu_const +#if __has_cpp_attribute(__gnu__::__const__) + int has_gnu_const(); +#endif // Test that C++11, target-specific attributes behave properly. Index: test/SemaCXX/attr-gnu.cpp =================================================================== --- test/SemaCXX/attr-gnu.cpp +++ test/SemaCXX/attr-gnu.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=gnu++11 -fsyntax-only -fms-compatibility -verify %s +// RUN: %clang_cc1 -std=gnu++17 -fsyntax-only -fms-compatibility -verify %s void f() { // GNU-style attributes are prohibited in this position. @@ -43,3 +43,10 @@ tuTest1(x); // expected-error {{no matching function for call to 'tuTest1'}} tuTest2(x); // expected-error {{no matching function for call to 'tuTest2'}} } + +[[gnu::__const__]] int f2() { return 12; } +[[__gnu__::__const__]] int f3() { return 12; } +[[using __gnu__ : __const__]] int f4() { return 12; } + +static_assert(__has_cpp_attribute(gnu::__const__)); +static_assert(__has_cpp_attribute(__gnu__::__const__)); Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -2935,9 +2935,9 @@ if (I != List.cbegin()) OS << " else "; if (I->first.empty()) - OS << "if (!Scope || Scope->getName() == \"\") {\n"; + OS << "if (ScopeName == \"\") {\n"; else - OS << "if (Scope->getName() == \"" << I->first << "\") {\n"; + OS << "if (ScopeName == \"" << I->first << "\") {\n"; OS << " return llvm::StringSwitch(Name)\n"; GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first); OS << "}";