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
Parsing of the attribute is determined by the various syntactic forms attributes
can take, such as GNU, C++11, and Microsoft style attributes, as well as other
information provided by the table definition of the attribute. Ultimately, the
-parsed representation of an attribute object is an ``ParsedAttr`` object.
+parsed representation of an attribute object is a ``ParsedAttr`` object.
These parsed attributes chain together as a list of parsed attributes attached
to a declarator or declaration specifier. The parsing of attributes is handled
-automatically by Clang, except for attributes spelled as keywords. When
-implementing a keyword attribute, the parsing of the keyword and creation of the
-``ParsedAttr`` object must be done manually.
+automatically by Clang, except for attributes spelled as so-called “custom”
+keywords. When implementing a custom keyword attribute, the parsing of the
+keyword and creation of the ``ParsedAttr`` object must be done manually.
Eventually, ``Sema::ProcessDeclAttributeList()`` is called with a ``Decl`` and
a ``ParsedAttr``, at which point the parsed attribute can be transformed
empty spelling list is also permissible and may be useful for attributes which
are created implicitly. The following spellings are accepted:
- ============ ================================================================
- Spelling Description
- ============ ================================================================
- ``GNU`` Spelled with a GNU-style ``__attribute__((attr))`` syntax and
- placement.
- ``CXX11`` Spelled with a C++-style ``[[attr]]`` syntax with an optional
- vendor-specific namespace.
- ``C2x`` Spelled with a C-style ``[[attr]]`` syntax with an optional
- vendor-specific namespace.
- ``Declspec`` Spelled with a Microsoft-style ``__declspec(attr)`` syntax.
- ``Keyword`` The attribute is spelled as a keyword, and required custom
- parsing.
- ``GCC`` Specifies two or three spellings: the first is a GNU-style
- spelling, the second is a C++-style spelling with the ``gnu``
- namespace, and the third is an optional C-style spelling with
- the ``gnu`` namespace. Attributes should only specify this
- spelling for attributes supported by GCC.
- ``Clang`` Specifies two or three spellings: the first is a GNU-style
- spelling, the second is a C++-style spelling with the ``clang``
- namespace, and the third is an optional C-style spelling with
- the ``clang`` namespace. By default, a C-style spelling is
- provided.
- ``Pragma`` The attribute is spelled as a ``#pragma``, and requires custom
- processing within the preprocessor. If the attribute is meant to
- be used by Clang, it should set the namespace to ``"clang"``.
- Note that this spelling is not used for declaration attributes.
- ============ ================================================================
+ ================== =========================================================
+ Spelling Description
+ ================== =========================================================
+ ``GNU`` Spelled with a GNU-style ``__attribute__((attr))``
+ syntax and placement.
+ ``CXX11`` Spelled with a C++-style ``[[attr]]`` syntax with an
+ optional vendor-specific namespace.
+ ``C2x`` Spelled with a C-style ``[[attr]]`` syntax with an
+ optional vendor-specific namespace.
+ ``Declspec`` Spelled with a Microsoft-style ``__declspec(attr)``
+ syntax.
+ ``CustomKeyword`` The attribute is spelled as a keyword, and requires
+ custom parsing.
+ ``RegularKeyword`` The attribute is spelled as a keyword. It can be
+ used in exactly the places that the standard
+ ``[[attr]]`` syntax can be used, and appertains to
+ exactly the same thing that a standard attribute
+ would appertain to. Lexing and parsing of the keyword
+ are handled automatically.
+ ``GCC`` Specifies two or three spellings: the first is a
+ GNU-style spelling, the second is a C++-style spelling
+ with the ``gnu`` namespace, and the third is an optional
+ C-style spelling with the ``gnu`` namespace. Attributes
+ should only specify this spelling for attributes
+ supported by GCC.
+ ``Clang`` Specifies two or three spellings: the first is a
+ GNU-style spelling, the second is a C++-style spelling
+ with the ``clang`` namespace, and the third is an
+ optional C-style spelling with the ``clang`` namespace.
+ By default, a C-style spelling is provided.
+ ``Pragma`` The attribute is spelled as a ``#pragma``, and requires
+ custom processing within the preprocessor. If the
+ attribute is meant to be used by Clang, it should
+ set the namespace to ``"clang"``. Note that this
+ spelling is not used for declaration attributes.
+ ================== =========================================================
+
+The C++ standard specifies that “any [non-standard attribute] that is not
+recognized by the implementation is ignored” (``[dcl.attr.grammar]``).
+The rule for C is similar. This makes ``CXX11`` and ``C2x`` spellings
+unsuitable for attributes that affect the type system, that change the
+binary interface of the code, or that have other similar semantic meaning.
+
+``RegularKeyword`` provides an alternative way of spelling such attributes.
+It reuses the production rules for standard attributes, but it applies them
+to plain keywords rather than to ``[[…]]`` sequences. Compilers that don't
+recognize the keyword are likely to report an error of some kind.
+
+For example, the ``ArmStreaming`` function type attribute affects
+both the type system and the binary interface of the function.
+It cannot therefore be spelled ``[[arm::streaming]]``, since compilers
+that don't understand ``arm::streaming`` would ignore it and miscompile
+the code. ``ArmStreaming`` is instead spelled ``__arm_streaming``, but it
+can appear wherever a hypothetical ``[[arm::streaming]]`` could appear.
Subjects
~~~~~~~~
: Spelling<name, "Keyword"> {
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<string name> : Keyword<name, 0> {}
+
// A keyword that has its own individual parsing rules.
class CustomKeyword<string name> : Keyword<name, 1> {}
let Documentation = [AArch64SVEPcsDocs];
}
+def ArmStreaming : TypeAttr, TargetSpecificAttr<TargetAArch64> {
+ let Spellings = [RegularKeyword<"__arm_streaming">];
+ let Documentation = [ArmStreamingDocs];
+}
+
def Pure : InheritableAttr {
let Spellings = [GCC<"pure">];
let Documentation = [Undocumented];
}];
}
+def ArmStreamingDocs : Documentation {
+ let Category = DocCatType;
+ let Content = [{
+.. Note:: This attribute has not been implemented yet, but once it is
+ implemented, it will behave as described below.
+
+The ``__arm_streaming`` keyword is only available on AArch64 targets.
+It applies to function types and specifies that the function has a
+“streaming interface”. This means that:
+
+* the function requires the Scalable Matrix Extension (SME)
+
+* the function must be entered in streaming mode (that is, with PSTATE.SM
+ set to 1)
+
+* the function must return in streaming mode
+
+See `Procedure Call Standard for the Arm® 64-bit Architecture (AArch64)
+<https://github.com/ARM-software/abi-aa>`_ for more details about
+streaming-interface functions.
+
+Clang manages PSTATE.SM automatically; it is not the source code's
+responsibility to do this. For example, if a normal non-streaming
+function calls an ``__arm_streaming`` function, Clang generates code
+that switches into streaming mode before calling the function and
+switches back to non-streaming mode on return.
+
+``__arm_streaming`` can appear anywhere that a standard ``[[…]]`` type
+attribute can appear.
+
+See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_
+for more details about this extension, and for other related SME features.
+ }];
+}
+
def AlwaysInlineDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
unsigned SyntaxUsed : 4;
unsigned SpellingIndex : 4;
unsigned IsAlignas : 1;
+ unsigned IsRegularKeywordAttribute : 1;
protected:
static constexpr unsigned SpellingNotCalculated = 0xf;
/// 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; }
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,
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!");
}
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; }
return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword;
}
+ bool isRegularKeywordAttribute() const { return IsRegularKeywordAttribute; }
+
bool isContextSensitiveKeywordAttribute() const {
return SyntaxUsed == AS_ContextSensitiveKeyword;
}
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
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)
/// 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
}
/// 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 the token 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
return;
}
+ if (T->getAttrKind() == attr::ArmStreaming) {
+ OS << "__arm_streaming";
+ return;
+ }
+
OS << " __attribute__((";
switch (T->getAttrKind()) {
#define TYPE_ATTR(NAME)
case attr::CmseNSCall:
case attr::AnnotateType:
case attr::WebAssemblyFuncref:
+ case attr::ArmStreaming:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:
case ParsedAttr::AT_AArch64SVEPcs:
CC = CC_AArch64SVEPCS;
break;
+ case ParsedAttr::AT_ArmStreaming:
+ CC = CC_C; // FIXME: placeholder until real SME support is added.
+ break;
case ParsedAttr::AT_AMDGPUKernelCall:
CC = CC_AMDGPUKernelCall;
break;
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: \
// 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);
return createSimpleAttr<AArch64VectorPcsAttr>(Ctx, Attr);
case ParsedAttr::AT_AArch64SVEPcs:
return createSimpleAttr<AArch64SVEPcsAttr>(Ctx, Attr);
+ case ParsedAttr::AT_ArmStreaming:
+ return createSimpleAttr<ArmStreamingAttr>(Ctx, Attr);
case ParsedAttr::AT_AMDGPUKernelCall:
return createSimpleAttr<AMDGPUKernelCallAttr>(Ctx, Attr);
case ParsedAttr::AT_Pcs: {
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"
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"
}
}
}
+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
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) {
(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,
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);
GenClangAttrSubjectMatchRuleList,
GenClangAttrPCHRead,
GenClangAttrPCHWrite,
+ GenClangAttrTokenKinds,
GenClangAttrHasAttributeImpl,
GenClangAttrSpellingListIndex,
GenClangAttrASTVisitor,
"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"),
case GenClangAttrPCHWrite:
EmitClangAttrPCHWrite(Records, OS);
break;
+ case GenClangAttrTokenKinds:
+ EmitClangAttrTokenKinds(Records, OS);
+ break;
case GenClangAttrHasAttributeImpl:
EmitClangAttrHasAttrImpl(Records, OS);
break;
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,