[Mlir-commits] [mlir] [MLIR][ODS] Add enum operation format directive (PR #220412)
Mehdi Amini
llvmlistbot at llvm.org
Tue Sep 1 15:51:47 PDT 2026
https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/220412
>From 28963df93841d9219a9d63e1be6bd117637a7f93 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Tue, 1 Sep 2026 10:12:27 -0700
Subject: [PATCH] [MLIR][TableGen] Add enum operation format directive
Add enum($attr) to declarative operation assembly formats. The directive
parses and prints the symbolic enum value independently of the attribute's
custom assembly format. This lets standalone attributes retain delimiters
while operations use a bare value.
Validate directive operands during TableGen processing and retain keyword
and quoted-string handling. Add separator-aware parsing and unquoted
printing for comma- and vertical-bar-separated bit enums.
Document the directive and add generated-code, diagnostic, compatibility,
and runtime round-trip coverage.
Assisted-by: Codex
---
mlir/docs/DefiningDialects/Operations.md | 9 ++
mlir/test/IR/enum-attr-roundtrip.mlir | 18 +++
mlir/test/lib/Dialect/Test/TestEnumDefs.td | 10 ++
mlir/test/lib/Dialect/Test/TestOps.td | 23 ++++
mlir/test/mlir-tblgen/op-format-invalid.td | 25 ++++
mlir/test/mlir-tblgen/op-format.td | 54 +++++++++
mlir/tools/mlir-tblgen/FormatGen.cpp | 19 +--
mlir/tools/mlir-tblgen/FormatGen.h | 1 +
mlir/tools/mlir-tblgen/OpFormatGen.cpp | 133 +++++++++++++++++++--
9 files changed, 277 insertions(+), 15 deletions(-)
diff --git a/mlir/docs/DefiningDialects/Operations.md b/mlir/docs/DefiningDialects/Operations.md
index f8a8d5fe6f0f2..de32b2aa388db 100644
--- a/mlir/docs/DefiningDialects/Operations.md
+++ b/mlir/docs/DefiningDialects/Operations.md
@@ -846,6 +846,15 @@ The available directives are as follows:
declarative assembly format will print it instead as:
`vector.multi_reduction #vector.kind<minf>, ...`.
+* `enum ( attribute )`
+
+ - Represents the symbolic value of an enum-backed attribute without the
+ attribute's dialect prefix, mnemonic, or custom assembly format.
+ - The argument must be an enum attribute. For example, if `$kind` has the
+ complete form `#vector.kind<minf>`, `enum($kind)` prints `minf`, `$kind`
+ prints the attribute's assembly-format body `<minf>`, and
+ `qualified($kind)` prints the complete attribute `#vector.kind<minf>`.
+
#### Literals
A literal is either a keyword or punctuation surrounded by \`\`.
diff --git a/mlir/test/IR/enum-attr-roundtrip.mlir b/mlir/test/IR/enum-attr-roundtrip.mlir
index 113b5f07ee6f3..ddb84daa8e7cc 100644
--- a/mlir/test/IR/enum-attr-roundtrip.mlir
+++ b/mlir/test/IR/enum-attr-roundtrip.mlir
@@ -18,6 +18,15 @@ func.func @test_op_with_enum() -> () {
return
}
+// CHECK-LABEL: @test_op_with_enum_directive
+func.func @test_op_with_enum_directive() -> () {
+ // CHECK: test.op_with_enum_directive third
+ test.op_with_enum_directive third
+ // CHECK: test.op_with_enum_directive "non-keyword"
+ test.op_with_enum_directive "non-keyword"
+ return
+}
+
// CHECK-LABEL: @test_match_op_with_enum
func.func @test_match_op_with_enum() -> () {
// CHECK: test.op_with_enum third tag 0 : i32
@@ -36,6 +45,15 @@ func.func @test_match_op_with_bit_enum() -> () {
return
}
+// CHECK-LABEL: @test_op_with_bit_enum_directive
+func.func @test_op_with_bit_enum_directive() -> () {
+ // CHECK: test.op_with_bit_enum_directive read, execute
+ test.op_with_bit_enum_directive read, execute
+ // CHECK: test.op_with_bit_enum_vbar_directive user | other
+ test.op_with_bit_enum_vbar_directive user | other
+ return
+}
+
// CHECK-LABEL: @test_enum_prop
func.func @test_enum_prop() -> () {
// CHECK: test.op_with_enum_prop first
diff --git a/mlir/test/lib/Dialect/Test/TestEnumDefs.td b/mlir/test/lib/Dialect/Test/TestEnumDefs.td
index 5d7c60d53c2c0..6479eaebf6243 100644
--- a/mlir/test/lib/Dialect/Test/TestEnumDefs.td
+++ b/mlir/test/lib/Dialect/Test/TestEnumDefs.td
@@ -56,6 +56,16 @@ def TestOtherEnum
let cppNamespace = "test";
}
+def TestPrettyEnum
+ : I32Enum<"TestPrettyEnum", "an enum with bracketed attribute syntax", [
+ I32EnumCase<"First", 0, "first">,
+ I32EnumCase<"Second", 1, "second">,
+ I32EnumCase<"Third", 2, "third">,
+ I32EnumCase<"NonKeyword", 3, "non-keyword">,
+ ]> {
+ let cppNamespace = "test";
+}
+
def TestSimpleEnum : I32Enum<"SimpleEnum", "", [
I32EnumCase<"a", 0>,
I32EnumCase<"b", 1>,
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 29f11b8cc4ec7..ccdddbc3e3a0b 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -489,12 +489,24 @@ def PromisedInterfacesOp : TEST_Op<"promised_interfaces"> {
// Define the enum attribute.
def TestEnumAttr : EnumAttr<Test_Dialect, TestEnum, "enum">;
+def TestPrettyEnumAttr
+ : EnumAttr<Test_Dialect, TestPrettyEnum, "pretty_enum"> {
+ let assemblyFormat = "`<` $value `>`";
+}
+
// Define an op that contains the enum attribute.
def OpWithEnum : TEST_Op<"op_with_enum"> {
let arguments = (ins TestEnumAttr:$value, OptionalAttr<AnyAttr>:$tag);
let assemblyFormat = "$value (`tag` $tag^)? attr-dict";
}
+// Define an op that prints just the symbolic value of an enum attribute whose
+// own assembly format includes angle brackets.
+def OpWithEnumDirective : TEST_Op<"op_with_enum_directive"> {
+ let arguments = (ins TestPrettyEnumAttr:$value);
+ let assemblyFormat = "enum($value) attr-dict";
+}
+
// Define a pattern that matches and creates an enum attribute.
def : Pat<(OpWithEnum ConstantEnumCase<TestEnumAttr, "first">:$value,
ConstantAttr<I32Attr, "0">:$tag),
@@ -594,6 +606,17 @@ def OpWithBitEnumVerticalBar : TEST_Op<"op_with_bit_enum_vbar"> {
let assemblyFormat = "$value (`tag` $tag^)? attr-dict";
}
+def OpWithBitEnumDirective : TEST_Op<"op_with_bit_enum_directive"> {
+ let arguments = (ins TestBitEnumAttr:$value);
+ let assemblyFormat = "enum($value) attr-dict";
+}
+
+def OpWithBitEnumVerticalBarDirective
+ : TEST_Op<"op_with_bit_enum_vbar_directive"> {
+ let arguments = (ins TestBitEnumVerticalBarAttr:$value);
+ let assemblyFormat = "enum($value) attr-dict";
+}
+
// Define a pattern that matches and creates a bit enum attribute.
def : Pat<(OpWithBitEnum ConstantEnumCase<TestBitEnumAttr, "write|execute">,
ConstantAttr<I32Attr, "0">),
diff --git a/mlir/test/mlir-tblgen/op-format-invalid.td b/mlir/test/mlir-tblgen/op-format-invalid.td
index 38c8d57a342b6..3e71b3c876d00 100644
--- a/mlir/test/mlir-tblgen/op-format-invalid.td
+++ b/mlir/test/mlir-tblgen/op-format-invalid.td
@@ -3,6 +3,7 @@
// This file contains tests for the specification of the declarative op format.
include "mlir/IR/OpBase.td"
+include "mlir/IR/EnumAttr.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
def TestDialect : Dialect {
@@ -25,6 +26,30 @@ class TestStrictPropertiesFormat_Op<string fmt, list<Trait> traits = []>
// Directives
//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
+// enum
+//===----------------------------------------------------------------------===//
+
+def DirectiveEnumCase : I32EnumCase<"Case", 0>;
+def DirectiveEnum : I32Enum<"DirectiveEnum", "test enum",
+ [DirectiveEnumCase]>;
+def DirectiveEnumAttr : EnumAttr<TestDialect, DirectiveEnum, "enum">;
+
+// CHECK: error: 'enum' directive expects an enum attribute
+def DirectiveAEnumInvalidA : TestFormat_Op<[{
+ enum($attr) attr-dict
+}]>, Arguments<(ins I64Attr:$attr)>;
+
+// CHECK: error: 'enum' directive expects an enum attribute
+def DirectiveAEnumInvalidB : TestFormat_Op<[{
+ enum($operand) attr-dict
+}]>, Arguments<(ins I64:$operand)>;
+
+// CHECK: error: 'enum' and 'qualified' directives cannot be combined
+def DirectiveAEnumInvalidC : TestFormat_Op<[{
+ qualified(enum($attr)) attr-dict
+}]>, Arguments<(ins DirectiveEnumAttr:$attr)>;
+
//===----------------------------------------------------------------------===//
// attr-dict
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/mlir-tblgen/op-format.td b/mlir/test/mlir-tblgen/op-format.td
index 530005ca4973f..eb75f0662e8b8 100644
--- a/mlir/test/mlir-tblgen/op-format.td
+++ b/mlir/test/mlir-tblgen/op-format.td
@@ -109,6 +109,11 @@ def CustomStringLiteralD : TestFormat_Op<[{
custom<Foo>(prop-dict) attr-dict
}]>;
+// A directive keyword can still name a custom directive.
+def CustomEnumKeyword : TestFormat_Op<[{
+ custom<enum>($attr) attr-dict
+}]>, Arguments<(ins I64Attr:$attr)>;
+
//===----------------------------------------------------------------------===//
// EnumAttr formatting
//===----------------------------------------------------------------------===//
@@ -125,6 +130,21 @@ def TestEnum : I32Enum<"TestEnum", "a test enum", [TestEnumCase0, TestEnumCase1]
def TestEnumAttr : EnumAttr<TestDialect, TestEnum, "enum">;
+def TestPrettyEnumAttr : EnumAttr<TestDialect, TestEnum, "pretty_enum"> {
+ let assemblyFormat = "`<` $value `>`";
+}
+
+def TestNonKeywordEnumCase : I32EnumCase<"NonKeyword", 0, "non-keyword">;
+def TestNonKeywordEnum
+ : I32Enum<"TestNonKeywordEnum", "a non-keyword enum",
+ [TestNonKeywordEnumCase]> {
+ let cppNamespace = "::test";
+}
+def TestNonKeywordEnumAttr
+ : EnumAttr<TestDialect, TestNonKeywordEnum, "non_keyword_enum"> {
+ let assemblyFormat = "`<` $value `>`";
+}
+
// Default-valued optional attributes have a non-optional getter.
// CHECK-LABEL: DefaultOptionalEnumAttrOp::print
@@ -164,6 +184,40 @@ def TestBitEnumAttr : EnumAttr<TestDialect, TestBitEnum, "bit_enum">;
def EnumAttrUnquotedBitOp : TestFormat_Op<"$attr attr-dict">,
Arguments<(ins TestBitEnumAttr:$attr)>;
+// Non-keyword enum values use the quoted-string fallback.
+
+// CHECK-LABEL: EnumDirectiveNonKeywordOp::parse
+// CHECK: parser.parseOptionalKeyword(&attrStr, {})
+// CHECK: parser.parseOptionalAttribute
+// CHECK-LABEL: EnumDirectiveNonKeywordOp::print
+// CHECK: _odsPrinter << '"' << caseValueStr << '"'
+def EnumDirectiveNonKeywordOp : TestFormat_Op<"enum($attr) attr-dict">,
+ Arguments<(ins TestNonKeywordEnumAttr:$attr)>;
+
+// The enum directive formats the symbolic enum value independently of the
+// attribute's custom assembly format.
+
+// CHECK-LABEL: EnumDirectiveOp::parse
+// CHECK: symbolizeTestEnum
+// CHECK-NOT: parseCustomAttributeWithFallback
+// CHECK-LABEL: EnumDirectiveOp::print
+// CHECK: stringifyTestEnum
+// CHECK-NOT: printStrippedAttrOrType
+def EnumDirectiveOp : TestFormat_Op<"enum($attr) attr-dict">,
+ Arguments<(ins TestPrettyEnumAttr:$attr)>;
+
+// The enum directive preserves the separator-aware parser and unquoted printer
+// for an unquoted bit enum.
+
+// CHECK-LABEL: EnumDirectiveUnquotedBitOp::parse
+// CHECK: symbolizeTestBitEnum
+// CHECK: parseOptionalVerticalBar
+// CHECK-LABEL: EnumDirectiveUnquotedBitOp::print
+// CHECK: stringifyTestBitEnum
+// CHECK: _odsPrinter << caseValueStr
+def EnumDirectiveUnquotedBitOp : TestFormat_Op<"enum($attr) attr-dict">,
+ Arguments<(ins TestBitEnumAttr:$attr)>;
+
// Test that legacy EnumAttrInfo-based attributes (I32EnumAttr) also use the
// enum-keyword format path.
diff --git a/mlir/tools/mlir-tblgen/FormatGen.cpp b/mlir/tools/mlir-tblgen/FormatGen.cpp
index 04d3ed1f3b70d..31843fabd5f4d 100644
--- a/mlir/tools/mlir-tblgen/FormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/FormatGen.cpp
@@ -180,6 +180,7 @@ FormatToken FormatLexer::lexIdentifier(const char *tokStart) {
.Case("attr-dict-with-keyword", FormatToken::kw_attr_dict_w_keyword)
.Case("prop-dict", FormatToken::kw_prop_dict)
.Case("custom", FormatToken::kw_custom)
+ .Case("enum", FormatToken::kw_enum)
.Case("functional-type", FormatToken::kw_functional_type)
.Case("oilist", FormatToken::kw_oilist)
.Case("operands", FormatToken::kw_operands)
@@ -405,13 +406,17 @@ FailureOr<FormatElement *> FormatParser::parseCustomDirective(SMLoc loc,
"or within a `struct` directive");
}
- FailureOr<FormatToken> nameTok;
if (failed(parseToken(FormatToken::less,
- "expected '<' before custom directive name")) ||
- failed(nameTok =
- parseToken(FormatToken::identifier,
- "expected custom directive name identifier")) ||
- failed(parseToken(FormatToken::greater,
+ "expected '<' before custom directive name")))
+ return failure();
+ // Preserve `custom<enum>` now that `enum` is also a directive keyword.
+ if (!curToken.is(FormatToken::identifier) &&
+ !curToken.is(FormatToken::kw_enum))
+ return emitError(curToken.getLoc(),
+ "expected custom directive name identifier");
+ FormatToken nameTok = curToken;
+ consumeToken();
+ if (failed(parseToken(FormatToken::greater,
"expected '>' after custom directive name")) ||
failed(parseToken(FormatToken::l_paren,
"expected '(' before custom directive parameters")))
@@ -435,7 +440,7 @@ FailureOr<FormatElement *> FormatParser::parseCustomDirective(SMLoc loc,
if (failed(verifyCustomDirectiveArguments(loc, arguments)))
return failure();
- return create<CustomDirective>(nameTok->getSpelling(), std::move(arguments));
+ return create<CustomDirective>(nameTok.getSpelling(), std::move(arguments));
}
FailureOr<FormatElement *> FormatParser::parseRefDirective(SMLoc loc,
diff --git a/mlir/tools/mlir-tblgen/FormatGen.h b/mlir/tools/mlir-tblgen/FormatGen.h
index 8e7d49bb37e71..b9600b9761992 100644
--- a/mlir/tools/mlir-tblgen/FormatGen.h
+++ b/mlir/tools/mlir-tblgen/FormatGen.h
@@ -61,6 +61,7 @@ class FormatToken {
kw_attr_dict_w_keyword,
kw_prop_dict,
kw_custom,
+ kw_enum,
kw_functional_type,
kw_oilist,
kw_operands,
diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
index 9c849955ac5fd..8e86f64e51f9b 100644
--- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
@@ -77,8 +77,13 @@ struct AttributeVariable
shouldBeQualifiedFlag = qualified;
}
+ /// Indicate if this attribute is formatted using its underlying enum value.
+ bool shouldFormatAsEnum() const { return shouldFormatAsEnumFlag; }
+ void setShouldFormatAsEnum() { shouldFormatAsEnumFlag = true; }
+
private:
bool shouldBeQualifiedFlag = false;
+ bool shouldFormatAsEnumFlag = false;
};
/// This class represents a variable that refers to an operand argument.
@@ -587,6 +592,44 @@ const char *const enumAttrParserCode = R"(
}
)";
+/// The code snippet used to generate a parser call for an unquoted bit enum
+/// attribute.
+///
+/// {0}: The name of the attribute.
+/// {1}: The C++ namespace for the enum symbolize functions.
+/// {2}: The function to symbolize a string of the enum.
+/// {3}: The constant builder call to create an attribute of the enum type.
+/// {4}: The set of allowed enum keywords.
+/// {5}: The error message on failure when the enum isn't present.
+/// {6}: The attribute assignment expression.
+/// {7}: The C++ enum type.
+/// {8}: The optional parser for the enum separator.
+const char *const unquotedBitEnumAttrParserCode = R"(
+ {
+ ::llvm::StringRef attrStr;
+ auto loc = parser.getCurrentLocation();
+ if (parser.parseOptionalKeyword(&attrStr, {4})) {
+ {5}
+ } else {
+ {7} flags = {{};
+ while (true) {
+ auto flag = {1}::{2}(attrStr);
+ if (!flag)
+ return parser.emitError(loc, "invalid ")
+ << "{0} attribute specification: \"" << attrStr << '"';;
+ flags = flags | *flag;
+ if (failed(parser.{8}()))
+ break;
+ if (parser.parseKeyword(&attrStr))
+ return ::mlir::failure();
+ }
+ auto attrOptional = ::std::optional<{7}>(flags);
+ {0}Attr = {3};
+ {6}
+ }
+ }
+)";
+
/// The code snippet used to generate a parser call for a property.
/// {0}: The name of the property
/// {1}: The property access expression
@@ -1230,12 +1273,18 @@ static void genEnumAttrParser(const NamedAttribute *var, MethodBody &body,
}
// Build a string containing the cases that can be formatted as a keyword.
- std::string validCaseKeywordsStr = "{";
- llvm::raw_string_ostream validCaseKeywordsOS(validCaseKeywordsStr);
- for (const EnumCase &attrCase : cases)
+ SmallVector<StringRef> validCaseKeywords;
+ for (const EnumCase &attrCase : cases) {
if (canFormatStringAsKeyword(attrCase.getStr()))
- validCaseKeywordsOS << '"' << attrCase.getStr() << "\",";
- validCaseKeywordsOS.str().back() = '}';
+ validCaseKeywords.push_back(attrCase.getStr());
+ }
+ std::string validCaseKeywordsStr;
+ llvm::raw_string_ostream validCaseKeywordsOS(validCaseKeywordsStr);
+ validCaseKeywordsOS << '{';
+ llvm::interleaveComma(
+ validCaseKeywords, validCaseKeywordsOS,
+ [&](StringRef keyword) { validCaseKeywordsOS << '"' << keyword << '"'; });
+ validCaseKeywordsOS << '}';
// If the attribute is not optional, build an error message for the missing
// attribute.
@@ -1260,6 +1309,22 @@ static void genEnumAttrParser(const NamedAttribute *var, MethodBody &body,
formatv("result.addAttribute(\"{0}\", {0}Attr);", var->name);
}
+ if (enumInfo.isBitEnum() && !enumInfo.printBitEnumQuoted()) {
+ StringRef separator = enumInfo.getDef().getValueAsString("separator");
+ StringRef parseSeparatorFn = llvm::StringSwitch<StringRef>(separator.trim())
+ .Case("|", "parseOptionalVerticalBar")
+ .Case(",", "parseOptionalComma")
+ .Default("invalid bit enum separator");
+ std::string enumType =
+ (enumInfo.getCppNamespace() + "::" + enumInfo.getEnumClassName()).str();
+ body << formatv(unquotedBitEnumAttrParserCode, var->name,
+ enumInfo.getCppNamespace(),
+ enumInfo.getStringToSymbolFnName(), attrBuilderStr,
+ validCaseKeywordsStr, errorMessage, attrAssignment,
+ enumType, parseSeparatorFn);
+ return;
+ }
+
body << formatv(enumAttrParserCode, var->name, enumInfo.getCppNamespace(),
enumInfo.getStringToSymbolFnName(), attrBuilderStr,
validCaseKeywordsStr, errorMessage, attrAssignment);
@@ -1296,7 +1361,7 @@ static void genAttrParser(AttributeVariable *attr, MethodBody &body,
const NamedAttribute *var = attr->getVar();
// Check to see if we can parse this as an enum attribute.
- if (canFormatEnumAttr(var))
+ if (attr->shouldFormatAsEnum() || canFormatEnumAttr(var))
return genEnumAttrParser(var, body, attrTypeCtx, parseAsOptional,
useProperties, opCppClassName);
@@ -2852,6 +2917,12 @@ static void genEnumAttrPrinter(const NamedAttribute *var, const Operator &op,
body << formatv(enumAttrBeginPrinterCode, caseValue,
enumInfo.getSymbolToStringFnName());
+ if (enumInfo.isBitEnum() && !enumInfo.printBitEnumQuoted()) {
+ body << " _odsPrinter << caseValueStr;\n"
+ " }\n";
+ return;
+ }
+
// Get a string containing all of the cases that can't be represented with a
// keyword.
BitVector nonKeywordCases(cases.size());
@@ -3130,7 +3201,7 @@ void OperationFormat::genElementPrinter(FormatElement *element,
const NamedAttribute *var = attr->getVar();
// If we are formatting as an enum, symbolize the attribute as a string.
- if (canFormatEnumAttr(var))
+ if (attr->shouldFormatAsEnum() || canFormatEnumAttr(var))
return genEnumAttrPrinter(var, op, body);
// If we are formatting as a symbol name, handle it as a symbol name.
@@ -3375,6 +3446,7 @@ class OpFormatParser : public FormatParser {
FailureOr<FormatElement *> parsePropDictDirective(SMLoc loc, Context context);
FailureOr<FormatElement *> parseAttrDictDirective(SMLoc loc, Context context,
bool withKeyword);
+ FailureOr<FormatElement *> parseEnumDirective(SMLoc loc, Context context);
FailureOr<FormatElement *> parseFunctionalTypeDirective(SMLoc loc,
Context context);
FailureOr<FormatElement *> parseOIListDirective(SMLoc loc, Context context);
@@ -4057,6 +4129,8 @@ OpFormatParser::parseDirectiveImpl(SMLoc loc, FormatToken::Kind kind,
case FormatToken::kw_attr_dict_w_keyword:
return parseAttrDictDirective(loc, ctx,
/*withKeyword=*/true);
+ case FormatToken::kw_enum:
+ return parseEnumDirective(loc, ctx);
case FormatToken::kw_functional_type:
return parseFunctionalTypeDirective(loc, ctx);
case FormatToken::kw_operands:
@@ -4077,6 +4151,42 @@ OpFormatParser::parseDirectiveImpl(SMLoc loc, FormatToken::Kind kind,
}
}
+FailureOr<FormatElement *> OpFormatParser::parseEnumDirective(SMLoc loc,
+ Context context) {
+ if (context != TopLevelContext)
+ return emitError(loc, "'enum' directive can only be used at the top level");
+
+ FailureOr<FormatElement *> element;
+ if (failed(parseToken(FormatToken::l_paren,
+ "expected '(' before argument list")) ||
+ failed(element = parseElement(context)) ||
+ failed(
+ parseToken(FormatToken::r_paren, "expected ')' after argument list")))
+ return failure();
+
+ auto *attr = dyn_cast<AttributeVariable>(*element);
+ if (!attr)
+ return emitError(loc, "'enum' directive expects an enum attribute");
+
+ Attribute baseAttr = attr->getVar()->attr.getBaseAttr();
+ if (!baseAttr.isEnumAttr())
+ return emitError(loc, "'enum' directive expects an enum attribute");
+
+ EnumInfo enumInfo(getEnumInfoRecord(baseAttr));
+ if (enumInfo.getUnderlyingType().empty() ||
+ baseAttr.getConstBuilderTemplate().empty()) {
+ return emitError(
+ loc, "'enum' directive requires an enum attribute with an underlying "
+ "type and a constant builder");
+ }
+
+ if (attr->shouldBeQualified())
+ return emitError(loc,
+ "'enum' and 'qualified' directives cannot be combined");
+ attr->setShouldFormatAsEnum();
+ return attr;
+}
+
FailureOr<FormatElement *>
OpFormatParser::parseAttrDictDirective(SMLoc loc, Context context,
bool withKeyword) {
@@ -4338,7 +4448,14 @@ FailureOr<FormatElement *> OpFormatParser::parseTypeDirective(SMLoc loc,
LogicalResult OpFormatParser::markQualified(SMLoc loc, FormatElement *element) {
return TypeSwitch<FormatElement *, LogicalResult>(element)
- .Case<AttributeVariable, TypeDirective>([](auto *element) {
+ .Case([&](AttributeVariable *element) {
+ if (element->shouldFormatAsEnum())
+ return emitError(
+ loc, "'enum' and 'qualified' directives cannot be combined");
+ element->setShouldBeQualified();
+ return success();
+ })
+ .Case<TypeDirective>([](auto *element) {
element->setShouldBeQualified();
return success();
})
More information about the Mlir-commits
mailing list