diff --git a/clang-tools-extra/pseudo/lib/grammar/CMakeLists.txt b/clang-tools-extra/pseudo/lib/grammar/CMakeLists.txt --- a/clang-tools-extra/pseudo/lib/grammar/CMakeLists.txt +++ b/clang-tools-extra/pseudo/lib/grammar/CMakeLists.txt @@ -1,10 +1,5 @@ set(LLVM_LINK_COMPONENTS Support) -# This library is used by the clang-pseudo-gen tool which runs at build time. -# Dependencies should be minimal to avoid long dep paths in the build graph. -# It does use clangBasic headers (tok::TokenKind), but linking is not needed. -# We have no transitive dependencies on tablegen files. -list(REMOVE_ITEM LLVM_COMMON_DEPENDS clang-tablegen-targets) add_clang_library(clangPseudoGrammar Grammar.cpp GrammarBNF.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -315,6 +315,13 @@ : Spelling { bit HasOwnParseRules = hasOwnParseRules; } + +// A keyword that can appear wherever a standard attribute can appear, +// and that appertains to whatever a standard attribute would appertain to. +// This is useful for things that affect semantics but that should otherwise +// be treated like standard attributes. +class RegularKeyword : Keyword {} + // A keyword that has its own individual parsing rules. class CustomKeyword : Keyword {} @@ -2417,6 +2424,11 @@ let Documentation = [AArch64SVEPcsDocs]; } +def ArmStreaming : TypeAttr, TargetSpecificAttr { + let Spellings = [RegularKeyword<"__arm_streaming">]; + let Documentation = [Undocumented]; +} + def Pure : InheritableAttr { let Spellings = [GCC<"pure">]; let Documentation = [Undocumented]; diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -77,6 +77,7 @@ unsigned SyntaxUsed : 4; unsigned SpellingIndex : 4; unsigned IsAlignas : 1; + unsigned IsRegularKeywordAttribute : 1; protected: static constexpr unsigned SpellingNotCalculated = 0xf; @@ -86,24 +87,29 @@ /// including its syntax and spelling. class Form { public: - constexpr Form(Syntax SyntaxUsed, unsigned SpellingIndex, bool IsAlignas) + constexpr Form(Syntax SyntaxUsed, unsigned SpellingIndex, bool IsAlignas, + bool IsRegularKeywordAttribute) : SyntaxUsed(SyntaxUsed), SpellingIndex(SpellingIndex), - IsAlignas(IsAlignas) {} + IsAlignas(IsAlignas), + IsRegularKeywordAttribute(IsRegularKeywordAttribute) {} constexpr Form(tok::TokenKind Tok) : SyntaxUsed(AS_Keyword), SpellingIndex(SpellingNotCalculated), - IsAlignas(Tok == tok::kw_alignas) {} + IsAlignas(Tok == tok::kw_alignas), + IsRegularKeywordAttribute(tok::isRegularKeywordAttribute(Tok)) {} Syntax getSyntax() const { return Syntax(SyntaxUsed); } unsigned getSpellingIndex() const { return SpellingIndex; } bool isAlignas() const { return IsAlignas; } + bool isRegularKeywordAttribute() const { return IsRegularKeywordAttribute; } static Form GNU() { return AS_GNU; } static Form CXX11() { return AS_CXX11; } static Form C2x() { return AS_C2x; } static Form Declspec() { return AS_Declspec; } static Form Microsoft() { return AS_Microsoft; } - static Form Keyword(bool IsAlignas) { - return Form(AS_Keyword, SpellingNotCalculated, IsAlignas); + static Form Keyword(bool IsAlignas, bool IsRegularKeywordAttribute) { + return Form(AS_Keyword, SpellingNotCalculated, IsAlignas, + IsRegularKeywordAttribute); } static Form Pragma() { return AS_Pragma; } static Form ContextSensitiveKeyword() { return AS_ContextSensitiveKeyword; } @@ -113,11 +119,12 @@ private: constexpr Form(Syntax SyntaxUsed) : SyntaxUsed(SyntaxUsed), SpellingIndex(SpellingNotCalculated), - IsAlignas(0) {} + IsAlignas(0), IsRegularKeywordAttribute(0) {} unsigned SyntaxUsed : 4; unsigned SpellingIndex : 4; unsigned IsAlignas : 1; + unsigned IsRegularKeywordAttribute : 1; }; AttributeCommonInfo(const IdentifierInfo *AttrName, @@ -127,7 +134,8 @@ ScopeLoc(ScopeLoc), AttrKind(AttrKind), SyntaxUsed(FormUsed.getSyntax()), SpellingIndex(FormUsed.getSpellingIndex()), - IsAlignas(FormUsed.isAlignas()) { + IsAlignas(FormUsed.isAlignas()), + IsRegularKeywordAttribute(FormUsed.isRegularKeywordAttribute()) { assert(SyntaxUsed >= AS_GNU && SyntaxUsed <= AS_Implicit && "Invalid syntax!"); } @@ -154,7 +162,10 @@ Kind getParsedKind() const { return Kind(AttrKind); } Syntax getSyntax() const { return Syntax(SyntaxUsed); } - Form getForm() const { return Form(getSyntax(), SpellingIndex, IsAlignas); } + Form getForm() const { + return Form(getSyntax(), SpellingIndex, IsAlignas, + IsRegularKeywordAttribute); + } const IdentifierInfo *getAttrName() const { return AttrName; } SourceLocation getLoc() const { return AttrRange.getBegin(); } SourceRange getRange() const { return AttrRange; } @@ -191,6 +202,8 @@ return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword; } + bool isRegularKeywordAttribute() const { return IsRegularKeywordAttribute; } + bool isContextSensitiveKeywordAttribute() const { return SyntaxUsed == AS_ContextSensitiveKeyword; } diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -35,6 +35,12 @@ SOURCE Attr.td TARGET ClangAttrSubjectMatchRuleList) +clang_tablegen(AttrTokenKinds.inc -gen-clang-attr-token-kinds + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ + SOURCE Attr.td + TARGET ClangAttrTokenKinds + ) + clang_tablegen(AttrHasAttributeImpl.inc -gen-clang-attr-has-attribute-impl -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ SOURCE Attr.td diff --git a/clang/include/clang/Basic/TokenKinds.h b/clang/include/clang/Basic/TokenKinds.h --- a/clang/include/clang/Basic/TokenKinds.h +++ b/clang/include/clang/Basic/TokenKinds.h @@ -99,6 +99,13 @@ /// Return true if this is an annotation token representing a pragma. bool isPragmaAnnotation(TokenKind K); +inline constexpr bool isRegularKeywordAttribute(TokenKind K) { + return (false +#define KEYWORD_ATTRIBUTE(X) || (K == tok::kw_##X) +#include "clang/Basic/AttrTokenKinds.inc" + ); +} + } // end namespace tok } // end namespace clang diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -747,6 +747,12 @@ KEYWORD(__builtin_available , KEYALL) KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL) +// Keywords defined by Attr.td. +#ifndef KEYWORD_ATTRIBUTE +#define KEYWORD_ATTRIBUTE(X) KEYWORD(X, KEYALL) +#endif +#include "clang/Basic/AttrTokenKinds.inc" + // Clang-specific keywords enabled only in testing. TESTING_KEYWORD(__unknown_anytype , KEYALL) diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -117,8 +117,13 @@ } /// Return true if this is any of tok::annot_* kind tokens. - bool isAnnotation() const { - return tok::isAnnotation(getKind()); + bool isAnnotation() const { return tok::isAnnotation(getKind()); } + + /// Return true if K is a keyword that is parsed in the same position as + /// a standard attribute, but that has semantic meaning and so cannot be + /// a true attribute. + bool isRegularKeywordAttribute() const { + return tok::isRegularKeywordAttribute(getKind()); } /// Return a source location identifier for the specified diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1721,6 +1721,11 @@ return; } + if (T->getAttrKind() == attr::ArmStreaming) { + OS << "__arm_streaming"; + return; + } + OS << " __attribute__(("; switch (T->getAttrKind()) { #define TYPE_ATTR(NAME) @@ -1761,6 +1766,7 @@ case attr::CmseNSCall: case attr::AnnotateType: case attr::WebAssemblyFuncref: + case attr::ArmStreaming: llvm_unreachable("This attribute should have been handled already"); case attr::NSReturnsRetained: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5258,6 +5258,9 @@ case ParsedAttr::AT_AArch64SVEPcs: CC = CC_AArch64SVEPCS; break; + case ParsedAttr::AT_ArmStreaming: + CC = CC_C; // Placeholder until real SME support is added. + break; case ParsedAttr::AT_AMDGPUKernelCall: CC = CC_AMDGPUKernelCall; break; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -125,6 +125,7 @@ case ParsedAttr::AT_VectorCall: \ case ParsedAttr::AT_AArch64VectorPcs: \ case ParsedAttr::AT_AArch64SVEPcs: \ + case ParsedAttr::AT_ArmStreaming: \ case ParsedAttr::AT_AMDGPUKernelCall: \ case ParsedAttr::AT_MSABI: \ case ParsedAttr::AT_SysVABI: \ @@ -4894,8 +4895,10 @@ // If we're supposed to infer nullability, do so now. if (inferNullability && !inferNullabilityInnerOnlyComplete) { ParsedAttr::Form form = - inferNullabilityCS ? ParsedAttr::Form::ContextSensitiveKeyword() - : ParsedAttr::Form::Keyword(false /*IsAlignAs*/); + inferNullabilityCS + ? ParsedAttr::Form::ContextSensitiveKeyword() + : ParsedAttr::Form::Keyword(false /*IsAlignAs*/, + false /*IsRegularKeywordAttribute*/); ParsedAttr *nullabilityAttr = Pool.create( S.getNullabilityKeyword(*inferNullability), SourceRange(pointerLoc), nullptr, SourceLocation(), nullptr, 0, form); @@ -7709,6 +7712,8 @@ return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_AArch64SVEPcs: return createSimpleAttr(Ctx, Attr); + case ParsedAttr::AT_ArmStreaming: + return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_AMDGPUKernelCall: return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_Pcs: { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -3094,11 +3094,12 @@ bool IsAlignas = (ParsedKind == AttributeCommonInfo::AT_Aligned && Syntax == AttributeCommonInfo::AS_Keyword && SpellingIndex == AlignedAttr::Keyword_alignas); + bool IsRegularKeywordAttribute = Record.readBool(); - AttributeCommonInfo Info( - AttrName, ScopeName, AttrRange, ScopeLoc, - AttributeCommonInfo::Kind(ParsedKind), - {AttributeCommonInfo::Syntax(Syntax), SpellingIndex, IsAlignas}); + AttributeCommonInfo Info(AttrName, ScopeName, AttrRange, ScopeLoc, + AttributeCommonInfo::Kind(ParsedKind), + {AttributeCommonInfo::Syntax(Syntax), SpellingIndex, + IsAlignas, IsRegularKeywordAttribute}); #include "clang/Serialization/AttrPCHRead.inc" diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4348,6 +4348,7 @@ Record.push_back(A->getParsedKind()); Record.push_back(A->getSyntax()); Record.push_back(A->getAttributeSpellingListIndexRaw()); + Record.push_back(A->isRegularKeywordAttribute()); #include "clang/Serialization/AttrPCHWrite.inc" } diff --git a/clang/unittests/AST/AttrTest.cpp b/clang/unittests/AST/AttrTest.cpp --- a/clang/unittests/AST/AttrTest.cpp +++ b/clang/unittests/AST/AttrTest.cpp @@ -168,4 +168,16 @@ } } +TEST(Attr, RegularKeywordAttribute) { + auto AST = clang::tooling::buildASTFromCode(""); + auto &Ctx = AST->getASTContext(); + auto Funcref = clang::WebAssemblyFuncrefAttr::CreateImplicit(Ctx); + EXPECT_EQ(Funcref->getSyntax(), clang::AttributeCommonInfo::AS_Keyword); + ASSERT_FALSE(Funcref->isRegularKeywordAttribute()); + + auto Streaming = clang::ArmStreamingAttr::CreateImplicit(Ctx); + EXPECT_EQ(Streaming->getSyntax(), clang::AttributeCommonInfo::AS_Keyword); + ASSERT_TRUE(Streaming->isRegularKeywordAttribute()); +} + } // namespace diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2378,6 +2378,11 @@ OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n"; } +static bool isRegularKeywordAttribute(const FlattenedSpelling &S) { + return (S.variety() == "Keyword" && + !S.getSpellingRecord().getValueAsBit("HasOwnParseRules")); +} + static void emitFormInitializer(raw_ostream &OS, const FlattenedSpelling &Spelling, StringRef SpellingIndex) { @@ -2385,7 +2390,9 @@ (Spelling.variety() == "Keyword" && Spelling.name() == "alignas"); OS << "{AttributeCommonInfo::AS_" << Spelling.variety() << ", " << SpellingIndex << ", " << (IsAlignas ? "true" : "false") - << " /*IsAlignas*/}"; + << " /*IsAlignas*/, " + << (isRegularKeywordAttribute(Spelling) ? "true" : "false") + << " /*IsRegularKeywordAttribute*/}"; } static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, @@ -3404,6 +3411,26 @@ OS << " .Default(0);\n"; } +// Emits the list of tokens for regular keyword attributes. +void EmitClangAttrTokenKinds(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("A list of tokens generated from the attribute" + " definitions", + OS); + // Assume for now that the same token is not used in multiple regular + // keyword attributes. + for (auto *R : Records.getAllDerivedDefinitions("Attr")) + for (const auto &S : GetFlattenedSpellings(*R)) + if (isRegularKeywordAttribute(S)) { + if (!R->getValueAsListOfDefs("Args").empty()) + PrintError(R->getLoc(), + "RegularKeyword attributes with arguments are not " + "yet supported"); + OS << "KEYWORD_ATTRIBUTE(" + << S.getSpellingRecord().getValueAsString("Name") << ")\n"; + } + OS << "#undef KEYWORD_ATTRIBUTE\n"; +} + // Emits the list of spellings for attributes. void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Code to implement the __has_attribute logic", OS); diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -35,6 +35,7 @@ GenClangAttrSubjectMatchRuleList, GenClangAttrPCHRead, GenClangAttrPCHWrite, + GenClangAttrTokenKinds, GenClangAttrHasAttributeImpl, GenClangAttrSpellingListIndex, GenClangAttrASTVisitor, @@ -128,6 +129,8 @@ "Generate clang PCH attribute reader"), clEnumValN(GenClangAttrPCHWrite, "gen-clang-attr-pch-write", "Generate clang PCH attribute writer"), + clEnumValN(GenClangAttrTokenKinds, "gen-clang-attr-token-kinds", + "Generate a list of attribute-related clang tokens"), clEnumValN(GenClangAttrHasAttributeImpl, "gen-clang-attr-has-attribute-impl", "Generate a clang attribute spelling list"), @@ -303,6 +306,9 @@ case GenClangAttrPCHWrite: EmitClangAttrPCHWrite(Records, OS); break; + case GenClangAttrTokenKinds: + EmitClangAttrTokenKinds(Records, OS); + break; case GenClangAttrHasAttributeImpl: EmitClangAttrHasAttrImpl(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -43,6 +43,8 @@ llvm::raw_ostream &OS); void EmitClangAttrPCHRead(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrPCHWrite(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangAttrTokenKinds(llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); void EmitClangAttrHasAttrImpl(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrSpellingListIndex(llvm::RecordKeeper &Records,