[Mlir-commits] [mlir] [mlir] Add support for parsing and printing cyclic aliases (PR #66663)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Sep 18 09:07:00 PDT 2023


Markus =?utf-8?q?Böck?= <markus.boeck02 at gmail.com>
Message-ID:
In-Reply-To: <llvm/llvm-project/pull/66663/mlir at github.com>


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-llvm

<details>
<summary>Changes</summary>

Final part of https://discourse.llvm.org/t/rfc-supporting-aliases-in-cyclic-types-and-attributes/73236

Up until now, the printing of mutable attributes and types as alias were disabled entirely as parsing them would end up in an infinite recursion.
This PR fixes this issue by using the recently added `tryStartCyclicParse` function to registering a mutable attribute or type parsed as part of an alias definition as soon as its immutable key has been parsed.
This makes it possible to break the recursion cycle and make parsing succeed. Combined with a previous patch that made the parser insensitive to the order of aliases in a row, we can also enable the printing of mutable attributes and types.

Depends on https://github.com/llvm/llvm-project/pull/65503. While we lack stacked PRs, please review the first commit on that PR.

---

Patch is 50.23 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/66663.diff


20 Files Affected:

- (modified) mlir/docs/LangRef.md (+28-3) 
- (modified) mlir/include/mlir/IR/OpImplementation.h (+4-4) 
- (modified) mlir/lib/AsmParser/AsmParserImpl.h (+3-2) 
- (modified) mlir/lib/AsmParser/AttributeParser.cpp (+41-11) 
- (modified) mlir/lib/AsmParser/DialectSymbolParser.cpp (+61-11) 
- (modified) mlir/lib/AsmParser/LocationParser.cpp (+15-2) 
- (modified) mlir/lib/AsmParser/Parser.cpp (+167-61) 
- (modified) mlir/lib/AsmParser/Parser.h (+24-7) 
- (modified) mlir/lib/AsmParser/ParserState.h (+21-1) 
- (modified) mlir/lib/AsmParser/TypeParser.cpp (+25-8) 
- (modified) mlir/lib/IR/AsmPrinter.cpp (+4-15) 
- (modified) mlir/test/Dialect/LLVMIR/types-typed-pointers.mlir (+1-1) 
- (modified) mlir/test/Dialect/SPIRV/IR/types.mlir (+10) 
- (added) mlir/test/IR/alias-def-groups.mlir (+25) 
- (renamed) mlir/test/IR/recursive-type-and-attr.mlir (+21-3) 
- (modified) mlir/test/lib/Dialect/Test/TestAttrDefs.td (+17) 
- (modified) mlir/test/lib/Dialect/Test/TestAttributes.cpp (+56) 
- (modified) mlir/test/lib/Dialect/Test/TestAttributes.h (+31) 
- (modified) mlir/test/lib/Dialect/Test/TestDialectInterfaces.cpp (+5) 
- (modified) mlir/test/lib/Dialect/Test/TestTypeDefs.td (+1-1) 


``````````diff
diff --git a/mlir/docs/LangRef.md b/mlir/docs/LangRef.md
index 0cfe845638c3cfb..a00f9f76c4253b2 100644
--- a/mlir/docs/LangRef.md
+++ b/mlir/docs/LangRef.md
@@ -183,12 +183,14 @@ starting with a `//` and going until the end of the line.
 
 ```
 // Top level production
-toplevel := (operation | attribute-alias-def | type-alias-def)*
+toplevel := (operation | alias-block-def)*
+alias-block-def := (attribute-alias-def | type-alias-def)*
 ```
 
 The production `toplevel` is the top level production that is parsed by any parsing
-consuming the MLIR syntax. [Operations](#operations),
-[Attribute aliases](#attribute-value-aliases), and [Type aliases](#type-aliases)
+consuming the MLIR syntax. [Operations](#operations) and 
+[Alias Blocks](#alias-block-definitions) consisting of 
+[Attribute aliases](#attribute-value-aliases) and [Type aliases](#type-aliases)
 can be declared on the toplevel.
 
 ### Identifiers and keywords
@@ -880,3 +882,26 @@ version using readAttribute and readType methods.
 There is no restriction on what kind of information a dialect is allowed to
 encode to model its versioning. Currently, versioning is supported only for
 bytecode formats.
+
+## Alias Block Definitions
+
+An alias block is a list of subsequent attribute or type alias definitions that
+are conceptually parsed as one unit.
+This allows any alias definition within the block to reference any other alias 
+definition within the block, regardless if defined lexically later or earlier in
+the block.
+
+```mlir
+// Alias block consisting of #array, !integer_type and #integer_attr.
+#array = [#integer_attr, !integer_type]
+!integer_type = i32
+#integer_attr = 8 : !integer_type
+
+// Illegal. !other_type is not part of this alias block and defined later 
+// in the file.
+!tuple = tuple<i32, !other_type>
+
+func.func @foo() { ... }
+
+!other_type = f32
+```
diff --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h
index 8864ef02cd3cbba..0893744c6a11960 100644
--- a/mlir/include/mlir/IR/OpImplementation.h
+++ b/mlir/include/mlir/IR/OpImplementation.h
@@ -1365,7 +1365,7 @@ class AsmParser {
                           AttrOrTypeT> ||
             std::is_base_of_v<TypeTrait::IsMutable<AttrOrTypeT>, AttrOrTypeT>,
         "Only mutable attributes or types can be cyclic");
-    if (failed(pushCyclicParsing(attrOrType.getAsOpaquePointer())))
+    if (failed(pushCyclicParsing(attrOrType)))
       return failure();
 
     return CyclicParseReset(this);
@@ -1377,11 +1377,11 @@ class AsmParser {
   virtual FailureOr<AsmDialectResourceHandle>
   parseResourceHandle(Dialect *dialect) = 0;
 
-  /// Pushes a new attribute or type in the form of a type erased pointer
-  /// into an internal set.
+  /// Pushes a new attribute or type into an internal set.
   /// Returns success if the type or attribute was inserted in the set or
   /// failure if it was already contained.
-  virtual LogicalResult pushCyclicParsing(const void *opaquePointer) = 0;
+  virtual LogicalResult
+  pushCyclicParsing(PointerUnion<Attribute, Type> attrOrType) = 0;
 
   /// Removes the element that was last inserted with a successful call to
   /// `pushCyclicParsing`. There must be exactly one `popCyclicParsing` call
diff --git a/mlir/lib/AsmParser/AsmParserImpl.h b/mlir/lib/AsmParser/AsmParserImpl.h
index 30c0079cda08611..1b88ca240c0eb21 100644
--- a/mlir/lib/AsmParser/AsmParserImpl.h
+++ b/mlir/lib/AsmParser/AsmParserImpl.h
@@ -570,8 +570,9 @@ class AsmParserImpl : public BaseT {
     return parser.parseXInDimensionList();
   }
 
-  LogicalResult pushCyclicParsing(const void *opaquePointer) override {
-    return success(parser.getState().cyclicParsingStack.insert(opaquePointer));
+  LogicalResult
+  pushCyclicParsing(PointerUnion<Attribute, Type> attrOrType) override {
+    return success(parser.getState().cyclicParsingStack.insert(attrOrType));
   }
 
   void popCyclicParsing() override {
diff --git a/mlir/lib/AsmParser/AttributeParser.cpp b/mlir/lib/AsmParser/AttributeParser.cpp
index 3437ac9addc5ff6..0ed385de8cf3b00 100644
--- a/mlir/lib/AsmParser/AttributeParser.cpp
+++ b/mlir/lib/AsmParser/AttributeParser.cpp
@@ -49,7 +49,7 @@ using namespace mlir::detail;
 ///                    | distinct-attribute
 ///                    | extended-attribute
 ///
-Attribute Parser::parseAttribute(Type type) {
+Attribute Parser::parseAttribute(Type type, StringRef aliasDefName) {
   switch (getToken().getKind()) {
   // Parse an AffineMap or IntegerSet attribute.
   case Token::kw_affine_map: {
@@ -117,7 +117,7 @@ Attribute Parser::parseAttribute(Type type) {
 
   // Parse an extended attribute, i.e. alias or dialect attribute.
   case Token::hash_identifier:
-    return parseExtendedAttr(type);
+    return parseExtendedAttr(type, aliasDefName);
 
   // Parse floating point and integer attributes.
   case Token::floatliteral:
@@ -145,6 +145,10 @@ Attribute Parser::parseAttribute(Type type) {
         parseLocationInstance(locAttr) ||
         parseToken(Token::r_paren, "expected ')' in inline location"))
       return Attribute();
+
+    if (syntaxOnly())
+      return state.syntaxOnlyAttr;
+
     return locAttr;
   }
 
@@ -430,6 +434,9 @@ Attribute Parser::parseDecOrHexAttr(Type type, bool isNegative) {
     return FloatAttr::get(floatType, *result);
   }
 
+  if (syntaxOnly())
+    return state.syntaxOnlyAttr;
+
   if (!isa<IntegerType, IndexType>(type))
     return emitError(loc, "integer literal not valid for specified type"),
            nullptr;
@@ -1003,7 +1010,9 @@ Attribute Parser::parseDenseElementsAttr(Type attrType) {
   auto type = parseElementsLiteralType(attrType);
   if (!type)
     return nullptr;
-  return literalParser.getAttr(loc, type);
+  if (syntaxOnly())
+    return state.syntaxOnlyAttr;
+  return literalParser.getAttr(loc, cast<ShapedType>(type));
 }
 
 Attribute Parser::parseDenseResourceElementsAttr(Type attrType) {
@@ -1030,6 +1039,9 @@ Attribute Parser::parseDenseResourceElementsAttr(Type attrType) {
       return nullptr;
   }
 
+  if (syntaxOnly())
+    return state.syntaxOnlyAttr;
+
   ShapedType shapedType = dyn_cast<ShapedType>(attrType);
   if (!shapedType) {
     emitError(typeLoc, "`dense_resource` expected a shaped type");
@@ -1044,7 +1056,7 @@ Attribute Parser::parseDenseResourceElementsAttr(Type attrType) {
 ///   elements-literal-type ::= vector-type | ranked-tensor-type
 ///
 /// This method also checks the type has static shape.
-ShapedType Parser::parseElementsLiteralType(Type type) {
+Type Parser::parseElementsLiteralType(Type type) {
   // If the user didn't provide a type, parse the colon type for the literal.
   if (!type) {
     if (parseToken(Token::colon, "expected ':'"))
@@ -1053,6 +1065,9 @@ ShapedType Parser::parseElementsLiteralType(Type type) {
       return nullptr;
   }
 
+  if (syntaxOnly())
+    return state.syntaxOnlyType;
+
   auto sType = dyn_cast<ShapedType>(type);
   if (!sType) {
     emitError("elements literal must be a shaped type");
@@ -1077,17 +1092,23 @@ Attribute Parser::parseSparseElementsAttr(Type attrType) {
   // of the type.
   Type indiceEltType = builder.getIntegerType(64);
   if (consumeIf(Token::greater)) {
-    ShapedType type = parseElementsLiteralType(attrType);
+    Type type = parseElementsLiteralType(attrType);
     if (!type)
       return nullptr;
 
+    if (syntaxOnly())
+      return state.syntaxOnlyAttr;
+
     // Construct the sparse elements attr using zero element indice/value
     // attributes.
+    ShapedType shapedType = cast<ShapedType>(type);
     ShapedType indicesType =
-        RankedTensorType::get({0, type.getRank()}, indiceEltType);
-    ShapedType valuesType = RankedTensorType::get({0}, type.getElementType());
+        RankedTensorType::get({0, shapedType.getRank()}, indiceEltType);
+    ShapedType valuesType =
+        RankedTensorType::get({0}, shapedType.getElementType());
     return getChecked<SparseElementsAttr>(
-        loc, type, DenseElementsAttr::get(indicesType, ArrayRef<Attribute>()),
+        loc, shapedType,
+        DenseElementsAttr::get(indicesType, ArrayRef<Attribute>()),
         DenseElementsAttr::get(valuesType, ArrayRef<Attribute>()));
   }
 
@@ -1114,6 +1135,11 @@ Attribute Parser::parseSparseElementsAttr(Type attrType) {
   if (!type)
     return nullptr;
 
+  if (syntaxOnly())
+    return state.syntaxOnlyAttr;
+
+  ShapedType shapedType = cast<ShapedType>(type);
+
   // If the indices are a splat, i.e. the literal parser parsed an element and
   // not a list, we set the shape explicitly. The indices are represented by a
   // 2-dimensional shape where the second dimension is the rank of the type.
@@ -1121,7 +1147,8 @@ Attribute Parser::parseSparseElementsAttr(Type attrType) {
   // indice and thus one for the first dimension.
   ShapedType indicesType;
   if (indiceParser.getShape().empty()) {
-    indicesType = RankedTensorType::get({1, type.getRank()}, indiceEltType);
+    indicesType =
+        RankedTensorType::get({1, shapedType.getRank()}, indiceEltType);
   } else {
     // Otherwise, set the shape to the one parsed by the literal parser.
     indicesType = RankedTensorType::get(indiceParser.getShape(), indiceEltType);
@@ -1131,7 +1158,7 @@ Attribute Parser::parseSparseElementsAttr(Type attrType) {
   // If the values are a splat, set the shape explicitly based on the number of
   // indices. The number of indices is encoded in the first dimension of the
   // indice shape type.
-  auto valuesEltType = type.getElementType();
+  auto valuesEltType = shapedType.getElementType();
   ShapedType valuesType =
       valuesParser.getShape().empty()
           ? RankedTensorType::get({indicesType.getDimSize(0)}, valuesEltType)
@@ -1139,7 +1166,7 @@ Attribute Parser::parseSparseElementsAttr(Type attrType) {
   auto values = valuesParser.getAttr(valuesLoc, valuesType);
 
   // Build the sparse elements attribute by the indices and values.
-  return getChecked<SparseElementsAttr>(loc, type, indices, values);
+  return getChecked<SparseElementsAttr>(loc, shapedType, indices, values);
 }
 
 Attribute Parser::parseStridedLayoutAttr() {
@@ -1260,6 +1287,9 @@ Attribute Parser::parseDistinctAttr(Type type) {
       return {};
   }
 
+  if (syntaxOnly())
+    return state.syntaxOnlyAttr;
+
   // Add the distinct attribute to the parser state, if it has not been parsed
   // before. Otherwise, check if the parsed reference attribute matches the one
   // found in the parser state.
diff --git a/mlir/lib/AsmParser/DialectSymbolParser.cpp b/mlir/lib/AsmParser/DialectSymbolParser.cpp
index 2b1b114b90e86af..2d78db35433ee5e 100644
--- a/mlir/lib/AsmParser/DialectSymbolParser.cpp
+++ b/mlir/lib/AsmParser/DialectSymbolParser.cpp
@@ -15,6 +15,7 @@
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/IR/DialectImplementation.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/SourceMgr.h"
 
 using namespace mlir;
@@ -28,18 +29,37 @@ namespace {
 /// hooking into the main MLIR parsing logic.
 class CustomDialectAsmParser : public AsmParserImpl<DialectAsmParser> {
 public:
-  CustomDialectAsmParser(StringRef fullSpec, Parser &parser)
+  CustomDialectAsmParser(StringRef fullSpec, Parser &parser,
+                         StringRef aliasDefName)
       : AsmParserImpl<DialectAsmParser>(parser.getToken().getLoc(), parser),
-        fullSpec(fullSpec) {}
+        fullSpec(fullSpec), aliasDefName(aliasDefName) {}
   ~CustomDialectAsmParser() override = default;
 
   /// Returns the full specification of the symbol being parsed. This allows
   /// for using a separate parser if necessary.
   StringRef getFullSymbolSpec() const override { return fullSpec; }
 
+  LogicalResult
+  pushCyclicParsing(PointerUnion<Attribute, Type> attrOrType) override {
+    // If this is an alias definition, register the mutable attribute or type.
+    if (!aliasDefName.empty()) {
+      if (auto attr = dyn_cast<Attribute>(attrOrType))
+        parser.getState().symbols.attributeAliasDefinitions[aliasDefName] =
+            attr;
+      else
+        parser.getState().symbols.typeAliasDefinitions[aliasDefName] =
+            cast<Type>(attrOrType);
+    }
+    return AsmParserImpl::pushCyclicParsing(attrOrType);
+  }
+
 private:
   /// The full symbol specification.
   StringRef fullSpec;
+
+  /// If this parser is used to parse an alias definition, the name of the alias
+  /// definition. Empty otherwise.
+  StringRef aliasDefName;
 };
 } // namespace
 
@@ -156,9 +176,11 @@ ParseResult Parser::parseDialectSymbolBody(StringRef &body,
 }
 
 /// Parse an extended dialect symbol.
-template <typename Symbol, typename SymbolAliasMap, typename CreateFn>
+template <typename Symbol, typename SymbolAliasMap, typename ParseAliasFn,
+          typename CreateFn>
 static Symbol parseExtendedSymbol(Parser &p, AsmParserState *asmState,
                                   SymbolAliasMap &aliases,
+                                  ParseAliasFn &parseAliasFn,
                                   CreateFn &&createSymbol) {
   Token tok = p.getToken();
 
@@ -185,12 +207,32 @@ static Symbol parseExtendedSymbol(Parser &p, AsmParserState *asmState,
   // If there is no '<' token following this, and if the typename contains no
   // dot, then we are parsing a symbol alias.
   if (!hasTrailingData && !isPrettyName) {
+
+    // Don't check the validity of alias reference in syntax-only mode.
+    if (p.syntaxOnly()) {
+      if constexpr (std::is_same_v<Symbol, Type>)
+        return p.getState().syntaxOnlyType;
+      else
+        return p.getState().syntaxOnlyAttr;
+    }
+
     // Check for an alias for this type.
     auto aliasIt = aliases.find(identifier);
-    if (aliasIt == aliases.end())
-      return (p.emitWrongTokenError("undefined symbol alias id '" + identifier +
-                                    "'"),
-              nullptr);
+    if (aliasIt == aliases.end()) {
+      FailureOr<Symbol> symbol = failure();
+      // Try the parse alias function if set.
+      if (parseAliasFn)
+        symbol = parseAliasFn(identifier);
+
+      if (failed(symbol)) {
+        p.emitWrongTokenError("undefined symbol alias id '" + identifier + "'");
+        return nullptr;
+      }
+      if (!*symbol)
+        return nullptr;
+
+      aliasIt = aliases.insert({identifier, *symbol}).first;
+    }
     if (asmState) {
       if constexpr (std::is_same_v<Symbol, Type>)
         asmState->addTypeAliasUses(identifier, range);
@@ -237,16 +279,20 @@ static Symbol parseExtendedSymbol(Parser &p, AsmParserState *asmState,
 ///                        | `#` alias-name pretty-dialect-sym-body? (`:` type)?
 ///   attribute-alias    ::= `#` alias-name
 ///
-Attribute Parser::parseExtendedAttr(Type type) {
+Attribute Parser::parseExtendedAttr(Type type, StringRef aliasDefName) {
   MLIRContext *ctx = getContext();
   Attribute attr = parseExtendedSymbol<Attribute>(
       *this, state.asmState, state.symbols.attributeAliasDefinitions,
+      state.symbols.parseUnknownAttributeAlias,
       [&](StringRef dialectName, StringRef symbolData, SMLoc loc) -> Attribute {
         // Parse an optional trailing colon type.
         Type attrType = type;
         if (consumeIf(Token::colon) && !(attrType = parseType()))
           return Attribute();
 
+        if (syntaxOnly())
+          return state.syntaxOnlyAttr;
+
         // If we found a registered dialect, then ask it to parse the attribute.
         if (Dialect *dialect =
                 builder.getContext()->getOrLoadDialect(dialectName)) {
@@ -255,7 +301,7 @@ Attribute Parser::parseExtendedAttr(Type type) {
           resetToken(symbolData.data());
 
           // Parse the attribute.
-          CustomDialectAsmParser customParser(symbolData, *this);
+          CustomDialectAsmParser customParser(symbolData, *this, aliasDefName);
           Attribute attr = dialect->parseAttribute(customParser, attrType);
           resetToken(curLexerPos);
           return attr;
@@ -284,11 +330,15 @@ Attribute Parser::parseExtendedAttr(Type type) {
 ///   dialect-type  ::= `!` alias-name pretty-dialect-attribute-body?
 ///   type-alias    ::= `!` alias-name
 ///
-Type Parser::parseExtendedType() {
+Type Parser::parseExtendedType(StringRef aliasDefName) {
   MLIRContext *ctx = getContext();
   return parseExtendedSymbol<Type>(
       *this, state.asmState, state.symbols.typeAliasDefinitions,
+      state.symbols.parseUnknownTypeAlias,
       [&](StringRef dialectName, StringRef symbolData, SMLoc loc) -> Type {
+        if (syntaxOnly())
+          return state.syntaxOnlyType;
+
         // If we found a registered dialect, then ask it to parse the type.
         if (auto *dialect = ctx->getOrLoadDialect(dialectName)) {
           // Temporarily reset the lexer to let the dialect parse the type.
@@ -296,7 +346,7 @@ Type Parser::parseExtendedType() {
           resetToken(symbolData.data());
 
           // Parse the type.
-          CustomDialectAsmParser customParser(symbolData, *this);
+          CustomDialectAsmParser customParser(symbolData, *this, aliasDefName);
           Type type = dialect->parseType(customParser);
           resetToken(curLexerPos);
           return type;
diff --git a/mlir/lib/AsmParser/LocationParser.cpp b/mlir/lib/AsmParser/LocationParser.cpp
index 61b20179800c6cc..8139f188c32a740 100644
--- a/mlir/lib/AsmParser/LocationParser.cpp
+++ b/mlir/lib/AsmParser/LocationParser.cpp
@@ -53,6 +53,9 @@ ParseResult Parser::parseCallSiteLocation(LocationAttr &loc) {
   if (parseToken(Token::r_paren, "expected ')' in callsite location"))
     return failure();
 
+  if (syntaxOnly())
+    return success();
+
   // Return the callsite location.
   loc = CallSiteLoc::get(calleeLoc, callerLoc);
   return success();
@@ -79,6 +82,9 @@ ParseResult Parser::parseFusedLocation(LocationAttr &loc) {
     LocationAttr newLoc;
     if (parseLocationInstance(newLoc))
       return failure();
+    if (syntaxOnly())
+      return success();
+
     locations.push_back(newLoc);
     return success();
   };
@@ -135,12 +141,15 @@ ParseResult Parser::parseNameOrFileLineColLocation(LocationAttr &loc) {
     if (parseLocationInstance(childLoc))
       return failure();
 
-    loc = NameLoc::get(StringAttr::get(ctx, str), childLoc);
-
     // Parse the closing ')'.
     if (parseToken(Token::r_paren,
                    "expected ')' after child location of NameLoc"))
       return failure();
+
+    if (syntaxOnly())
+      return success();
+
+    loc = NameLoc::get(StringAttr::get(ctx, str), childLoc);
   } else {
     loc = NameLoc::get(StringAttr::get(ctx, str));
   }
@@ -154,6 +163,10 @@ ParseResult Parser::parseLocationInstance(LocationAttr &loc) {
     Attribute locAttr = parseExtendedAttr(Type());
     if (!locAttr)
       return failure();
+
+    if (syntaxOnly())
+      return success();
+
     if (!(loc = dyn_cast<LocationAttr>(locAttr)))
       return emitError("expected location attribute, but got") << locAttr;
     return success();
diff --git a/mlir/lib/AsmParser/Parser.cpp b/mlir/lib/AsmParser/Parser.cpp
index 84f44dba806df01..4bae575c1c82acb 100644
--- a/mlir/lib/AsmParser/Parser.cpp
+++ b/mlir/lib/AsmParser/Parser.cpp
@@ -29,6 +29,7 @@
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/SourceMgr.h"
 #include <algorithm>
 #include <memory>
@@ -2417,17 +2418,12 @@ class TopLevelOperationParser : public Parser {
   ParseResult parse(Block *topLevelBlock, Location parserLoc);
 
 private:
-  /// Parse an attribute alias declaration.
+  /// Parse an alias block definition.
   ///
   ///   attribute-alias-def ::= '#' alias-name `=` attribute-value
-  ///
-  ParseResult parseAttributeAliasDef();
-
-  /// Parse a type alias declaration.
-  ///
   ///   type-alias-def ::= '!' alias-name `=` type
-  ///
-  ParseResult parseTypeAliasDef();
+  ///   alias-block-def ::= (type-alias-def | attribute-alias-def)+
+  ParseResult parseAliasBlockDef();
 
   /// Parse a top-level file metadata dictionary.
   ///
@@ -2528,69 +2524,184 @@ class ParsedResourceEntry : public AsmParsedResourceEntry {
   Token value;
   Parser &p;
 };
+
+/// Convenient subclass of `ParserState` which configures the parser for
+/// syntax-only parsing. This only copies config and other required state for
+/// parsing but does not copy side-effecting state such as the code completion
+/// context.
+struct SyntaxParserStat...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/66663


More information about the Mlir-commits mailing list