[Mlir-commits] [mlir] [mlir][ODS] Allow a space-only separator for bit enums (PR #237149)
Henrich Lauko
llvmlistbot at llvm.org
Tue Nov 3 01:43:27 PST 2026
https://github.com/xlauko updated https://github.com/llvm/llvm-project/pull/237149
>From 490e576e24ea4d3d380c8fe6909e16d6967544ce Mon Sep 17 00:00:00 2001
From: Henrich Lauko <hlauko at nvidia.com>
Date: Mon, 2 Nov 2026 15:46:51 +0000
Subject: [PATCH] [mlir][ODS] Allow a space-only separator for bit enums
Bit enums required a separator containing '|' or ',', so a flag set
could not use the bare keyword syntax of LLVM IR, e.g. `nuw nsw`.
Dialects that want it, such as ClangIR's overflow flags, had to hand-write
a parser and printer for an EnumProp.
Allow `separator = " "` on new-style BitEnums. The generated FieldParser
then reads case keywords until the next token is not one, symbolize
splits on spaces, and the op-format `enum` directive parses the same
way. Like the existing check for a separator literal, the format
verifier rejects a case literal right after such an enum. The separator
must now be '|', ',', or a space ' ', where '|' and ',' may still be
surrounded by spaces, so strings such as "||" are rejected. Legacy
BitEnumAttr keeps requiring '|' or ','.
Assisted-by: Claude Code (Claude Opus 5.5).
---
mlir/docs/DefiningDialects/Operations.md | 11 +++++-
mlir/include/mlir/IR/EnumAttr.td | 21 +++++++----
mlir/include/mlir/TableGen/EnumInfo.h | 7 ++--
mlir/lib/TableGen/EnumInfo.cpp | 9 ++++-
mlir/test/IR/attribute.mlir | 23 +++++++++++
mlir/test/IR/enum-attr-roundtrip.mlir | 24 ++++++++++++
mlir/test/lib/Dialect/Test/TestEnumDefs.td | 12 ++++++
mlir/test/lib/Dialect/Test/TestOps.td | 15 ++++++++
mlir/test/lib/Dialect/Test/TestOpsSyntax.td | 10 +++++
mlir/test/mlir-tblgen/enums-gen.td | 20 ++++++++++
mlir/test/mlir-tblgen/op-format-invalid.td | 14 +++++++
mlir/tools/mlir-tblgen/EnumsGen.cpp | 42 ++++++++++++++++++++-
mlir/tools/mlir-tblgen/OpFormatGen.cpp | 37 ++++++++++++++----
13 files changed, 222 insertions(+), 23 deletions(-)
diff --git a/mlir/docs/DefiningDialects/Operations.md b/mlir/docs/DefiningDialects/Operations.md
index f991f5308c5ae4..bce44909d0d321 100644
--- a/mlir/docs/DefiningDialects/Operations.md
+++ b/mlir/docs/DefiningDialects/Operations.md
@@ -869,7 +869,9 @@ The available directives are as follows:
`qualified($kind)` prints the complete attribute `#vector.kind<minf>`.
- Bit enums must define a zero-valued case so every valid bitmask has a
symbolic spelling. An unquoted comma-separated bit enum cannot be
- followed by a comma literal because the two uses would be ambiguous.
+ followed by a comma literal, and a space-separated bit enum cannot be
+ followed by a literal that spells one of its cases, because the two
+ uses would be ambiguous.
#### Literals
@@ -1675,6 +1677,13 @@ def MyBitEnum: I32BitEnum<"MyBitEnum", "An example bit enum",
}
```
+The `separator` is `"|"`, `","`, or a space `" "`. Spaces around `|` or `,`,
+e.g. `" | "`, are printed but optional when parsing. With a space separator, the
+cases are printed as a sequence of keywords, e.g. `Bit1 Bit2` (like LLVM IR's
+`nuw nsw`), and parsed until the next token is not a case keyword. Only
+`BitEnum` supports a space separator; the legacy `BitEnumAttr` requires `|` or
+`,`.
+
We can have:
```c++
diff --git a/mlir/include/mlir/IR/EnumAttr.td b/mlir/include/mlir/IR/EnumAttr.td
index 1c3a223dbb8496..ea01e71eda67eb 100644
--- a/mlir/include/mlir/IR/EnumAttr.td
+++ b/mlir/include/mlir/IR/EnumAttr.td
@@ -362,14 +362,15 @@ class BitEnumBase<list<BitEnumCaseBase> cases> {
// Determine "valid" bits from enum cases for error checking
int validBits = !foldl(0, cases, value, bitcase, !or(value, bitcase.value));
- // The delimiter used to separate bit enum cases in strings. Only "|" and
- // "," (along with optional spaces) are supported due to the use of the
- // parseSeparatorFn in parameterParser below.
- // Spaces in the separator string are used for printing, but will be optional
- // for parsing.
+ // The delimiter used to separate bit enum cases in strings: "|", ",", or a
+ // space " ". Spaces around "|" or ",", e.g. " | ", are used for printing but
+ // are optional for parsing. With a space separator, the cases are printed as
+ // a sequence of keywords, e.g. `nuw nsw`, and parsed until the next token is
+ // not a case keyword. Only BitEnum supports a space separator; BitEnumAttr
+ // requires "|" or ",".
string separator = "|";
- assert !or(!ge(!find(separator, "|"), 0), !ge(!find(separator, ","), 0)),
- "separator must contain '|' or ',' for parameter parsing";
+ assert !match(separator, "^( *[|,] *| +)$"),
+ "separator must be '|', ',', or a space ' '";
// Print the "primary group" only for bits that are members of case groups
// that have all bits present. When the value is 0, printing will display both
@@ -440,7 +441,11 @@ class BitEnumAttr<I intType, string name, string summary,
let symbolToStringFnRetType = "std::string";
// Parsing function that corresponds to the enum separator. Only
- // "," and "|" are supported by this definition.
+ // "," and "|" are supported by this definition; use BitEnum for a space
+ // separator.
+ assert !ne(!subst(" ", "", separator), ""),
+ "BitEnumAttr separator must contain '|' or ','; use BitEnum for a "
+ "space separator";
string parseSeparatorFn = !if(!ge(!find(separator, "|"), 0),
"parseOptionalVerticalBar",
"parseOptionalComma");
diff --git a/mlir/include/mlir/TableGen/EnumInfo.h b/mlir/include/mlir/TableGen/EnumInfo.h
index 9a46ac0c3cdb7d..6053611061f56e 100644
--- a/mlir/include/mlir/TableGen/EnumInfo.h
+++ b/mlir/include/mlir/TableGen/EnumInfo.h
@@ -138,12 +138,13 @@ class EnumInfo {
StringRef getBitEnumSeparator() const;
// The token that separates the cases of a bit enum, ignoring the spaces
- // around it.
- enum class BitEnumSeparatorKind { VerticalBar, Comma };
+ // around it. `Space` means the separator is a space " ".
+ enum class BitEnumSeparatorKind { VerticalBar, Comma, Space };
BitEnumSeparatorKind getBitEnumSeparatorKind() const;
// Returns the name of the `AsmParser` method that parses the separator
- // between the cases of a bit enum.
+ // between the cases of a bit enum. Must not be called for a space
+ // separator.
StringRef getBitEnumSeparatorParser() const;
// Returns the TableGen definition this EnumAttrCase was constructed from.
diff --git a/mlir/lib/TableGen/EnumInfo.cpp b/mlir/lib/TableGen/EnumInfo.cpp
index 54c6f7320f09a2..832ef68d9d234c 100644
--- a/mlir/lib/TableGen/EnumInfo.cpp
+++ b/mlir/lib/TableGen/EnumInfo.cpp
@@ -158,8 +158,10 @@ EnumInfo::BitEnumSeparatorKind EnumInfo::getBitEnumSeparatorKind() const {
return BitEnumSeparatorKind::VerticalBar;
if (separator == ",")
return BitEnumSeparatorKind::Comma;
- PrintFatalError(def->getLoc(), "bit enum separator must be '|' or ',', "
- "optionally surrounded by spaces");
+ if (separator.empty())
+ return BitEnumSeparatorKind::Space;
+ PrintFatalError(def->getLoc(),
+ "bit enum separator must be '|', ',', or a space ' '");
}
StringRef EnumInfo::getBitEnumSeparatorParser() const {
@@ -168,6 +170,9 @@ StringRef EnumInfo::getBitEnumSeparatorParser() const {
return "parseOptionalVerticalBar";
case BitEnumSeparatorKind::Comma:
return "parseOptionalComma";
+ case BitEnumSeparatorKind::Space:
+ // Whitespace is not a token, so there is nothing to parse between cases.
+ llvm_unreachable("a space separator has no parser");
}
llvm_unreachable("unknown bit enum separator kind");
}
diff --git a/mlir/test/IR/attribute.mlir b/mlir/test/IR/attribute.mlir
index ea378439c0f3bb..a931eabd695709 100644
--- a/mlir/test/IR/attribute.mlir
+++ b/mlir/test/IR/attribute.mlir
@@ -542,6 +542,29 @@ func.func @allowed_cases_pass() {
// -----
+// CHECK-LABEL: func @space_separated_cases_pass
+func.func @space_separated_cases_pass() {
+ // CHECK: test.op_with_bit_enum_space <user group>
+ "test.op_with_bit_enum_space"() {
+ value = #test.bit_enum_space<group user>
+ } : () -> ()
+ // CHECK: test.op_with_bit_enum_space <user group other> tag 1 : i32
+ test.op_with_bit_enum_space <other user group> tag 1 : i32
+ // CHECK: test.op_with_bit_enum_space_directive user other tag 1 : i32
+ test.op_with_bit_enum_space_directive other user tag 1 : i32
+ return
+}
+
+// -----
+
+func.func @space_separated_case_missing() {
+ // expected-error at +2 {{expected one of [none, user, group, other] for a space-separated test bit enum}}
+ // expected-error at +1 {{failed to parse TestBitEnumSpaceAttr}}
+ "test.op_with_bit_enum_space"() {value = #test.bit_enum_space<sticky>} : () -> ()
+}
+
+// -----
+
func.func @disallowed_case_sticky_fail() {
// expected-error at +2 {{expected one of [none, read, write, execute] for a test bit enum, got: sticky}}
// expected-error at +1 {{failed to parse TestBitEnumAttr}}
diff --git a/mlir/test/IR/enum-attr-roundtrip.mlir b/mlir/test/IR/enum-attr-roundtrip.mlir
index 9b4b8de898942f..c3a0e1446f851f 100644
--- a/mlir/test/IR/enum-attr-roundtrip.mlir
+++ b/mlir/test/IR/enum-attr-roundtrip.mlir
@@ -123,6 +123,30 @@ func.func @test_two_default_bit_enum_props() -> () {
return
}
+// CHECK-LABEL: @test_space_bit_enum_prop
+func.func @test_space_bit_enum_prop() -> () {
+ // CHECK: test.op_with_default_space_bit_enum_prop{{$}}
+ test.op_with_default_space_bit_enum_prop
+
+ // CHECK: test.op_with_default_space_bit_enum_prop user group{{$}}
+ test.op_with_default_space_bit_enum_prop group user
+
+ // Parsing stops at the first keyword that is not a case.
+ // CHECK: test.op_with_default_space_bit_enum_prop other tag 1 : i32{{$}}
+ test.op_with_default_space_bit_enum_prop other tag 1 : i32
+
+ // CHECK: test.op_with_default_space_bit_enum_prop tag 1 : i32{{$}}
+ test.op_with_default_space_bit_enum_prop tag 1 : i32
+
+ // An explicit `none` is the default and so is elided.
+ // CHECK: test.op_with_default_space_bit_enum_prop{{$}}
+ test.op_with_default_space_bit_enum_prop none
+
+ // CHECK: test.op_with_default_space_bit_enum_prop user other{{$}}
+ "test.op_with_default_space_bit_enum_prop"() <{value = 5 : i32}> : () -> ()
+ return
+}
+
// CHECK-LABEL @test_bit_enum_prop()
func.func @test_bit_enum_prop() -> () {
// CHECK: test.op_with_bit_enum_prop read : ()
diff --git a/mlir/test/lib/Dialect/Test/TestEnumDefs.td b/mlir/test/lib/Dialect/Test/TestEnumDefs.td
index daf898ca7ee061..c0a600120a3871 100644
--- a/mlir/test/lib/Dialect/Test/TestEnumDefs.td
+++ b/mlir/test/lib/Dialect/Test/TestEnumDefs.td
@@ -163,6 +163,18 @@ def TestBitEnumVerticalBar
let separator = " | ";
}
+// Define an enum whose cases are separated by whitespace only.
+def TestBitEnumSpace
+ : I32BitEnum<"TestBitEnumSpace", "a space-separated test bit enum", [
+ TestBitEnumNone,
+ I32BitEnumCaseBit<"User", 0, "user">,
+ I32BitEnumCaseBit<"Group", 1, "group">,
+ I32BitEnumCaseBit<"Other", 2, "other">,
+ ]> {
+ let cppNamespace = "test";
+ let separator = " ";
+}
+
def TestStructBitEnum
: I32BitEnum<"TestStructBitEnum", "a bit enum nested in a struct", [
TestBitEnumNone,
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index fc096a9e61d710..07bb7d706f98ac 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -612,6 +612,21 @@ def OpWithBitEnumVerticalBarDirective
let assemblyFormat = "enum($value) attr-dict";
}
+def TestBitEnumSpaceAttr
+ : EnumAttr<Test_Dialect, TestBitEnumSpace, "bit_enum_space">;
+
+def OpWithBitEnumSpace : TEST_Op<"op_with_bit_enum_space"> {
+ let arguments = (ins TestBitEnumSpaceAttr:$value,
+ OptionalAttr<AnyAttr>:$tag);
+ let assemblyFormat = "$value (`tag` $tag^)? attr-dict";
+}
+
+def OpWithBitEnumSpaceDirective : TEST_Op<"op_with_bit_enum_space_directive"> {
+ let arguments = (ins TestBitEnumSpaceAttr:$value,
+ OptionalAttr<AnyAttr>:$tag);
+ let assemblyFormat = "enum($value) (`tag` $tag^)? 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/lib/Dialect/Test/TestOpsSyntax.td b/mlir/test/lib/Dialect/Test/TestOpsSyntax.td
index f2d6ba3c5855e5..115f8734b03e42 100644
--- a/mlir/test/lib/Dialect/Test/TestOpsSyntax.td
+++ b/mlir/test/lib/Dialect/Test/TestOpsSyntax.td
@@ -484,6 +484,16 @@ def OpWithTwoDefaultBitEnumProps : TEST_Op<"op_with_two_default_bit_enum_props">
let assemblyFormat = "($value1^)? ($value2^)? attr-dict";
}
+def TestBitEnumSpacePropDefault : EnumProp<TestBitEnumSpace> {
+ let defaultValue = TestBitEnumSpace.cppType # "::None";
+}
+def OpWithDefaultSpaceBitEnumProp
+ : TEST_Op<"op_with_default_space_bit_enum_prop"> {
+ let arguments = (ins TestBitEnumSpacePropDefault:$value,
+ OptionalAttr<AnyAttr>:$tag);
+ let assemblyFormat = "($value^)? (`tag` $tag^)? attr-dict";
+}
+
def FormatOptionalWithElse : TEST_Op<"format_optional_else"> {
let arguments = (ins UnitAttr:$isFirstBranchPresent);
let assemblyFormat = "(`then` $isFirstBranchPresent^):(`else`)? attr-dict";
diff --git a/mlir/test/mlir-tblgen/enums-gen.td b/mlir/test/mlir-tblgen/enums-gen.td
index dc0caab3652d9f..97500e165b42e1 100644
--- a/mlir/test/mlir-tblgen/enums-gen.td
+++ b/mlir/test/mlir-tblgen/enums-gen.td
@@ -175,6 +175,26 @@ def MyCommaSeparatedBitEnum
let separator = ", ";
}
+def MySpaceSeparatedBitEnum
+ : I32BitEnum<"MySpaceSeparatedBitEnum", "Space-separated bit enum",
+ [None, Bit0, Bit1]> {
+ let separator = " ";
+}
+
+// DEF-LABEL: ::std::optional<MySpaceSeparatedBitEnum> symbolizeMySpaceSeparatedBitEnum(::llvm::StringRef str) {
+// DEF: str.split(symbols, ' ', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+
+// DEF-LABEL: FailureOr<::MySpaceSeparatedBitEnum> FieldParser<::MySpaceSeparatedBitEnum, ::MySpaceSeparatedBitEnum>::parse(AsmParser &parser) {
+// DEF: FailureOr<std::optional<::MySpaceSeparatedBitEnum>> flags =
+// DEF-NEXT: FieldParser<std::optional<::MySpaceSeparatedBitEnum>>::parse(parser);
+// DEF: if (!*flags)
+// DEF-NEXT: return parser.emitError(loc, "expected one of [none, tagged, Bit1] for Space-separated bit enum");
+// DEF: FieldParser<std::optional<::MySpaceSeparatedBitEnum>, std::optional<::MySpaceSeparatedBitEnum>>::parse(AsmParser &parser) {
+// DEF: std::optional<::MySpaceSeparatedBitEnum> flags;
+// DEF: while (::mlir::succeeded(parser.parseOptionalKeyword(&enumKeyword, {"none", "tagged", "Bit1"})))
+// DEF-NEXT: flags = flags.value_or(::MySpaceSeparatedBitEnum{}) | *::symbolizeEnum<::MySpaceSeparatedBitEnum>(enumKeyword);
+// DEF-NEXT: return flags;
+
// DECL: inline ::llvm::raw_ostream &operator<<(::llvm::raw_ostream &p, ::MyNonQuotedPrintBitEnum value) {
// DECL: auto valueStr = stringifyEnum(value);
// DECL-NEXT: return p << valueStr;
diff --git a/mlir/test/mlir-tblgen/op-format-invalid.td b/mlir/test/mlir-tblgen/op-format-invalid.td
index 1b78d8177091fb..a8901fe96ecd0e 100644
--- a/mlir/test/mlir-tblgen/op-format-invalid.td
+++ b/mlir/test/mlir-tblgen/op-format-invalid.td
@@ -50,6 +50,15 @@ def DirectiveCommaBitEnum
def DirectiveCommaBitEnumAttr
: EnumAttr<TestDialect, DirectiveCommaBitEnum, "comma_bit_enum">;
+def DirectiveSpaceBitEnum
+ : I32BitEnum<"DirectiveSpaceBitEnum", "space bit enum",
+ [DirectiveBitEnumNone, DirectiveBitEnumCase]> {
+ let cppNamespace = "::test";
+ let separator = " ";
+}
+def DirectiveSpaceBitEnumAttr
+ : EnumAttr<TestDialect, DirectiveSpaceBitEnum, "space_bit_enum">;
+
def DirectiveNoZeroBitEnum
: I32BitEnum<"DirectiveNoZeroBitEnum", "bit enum without zero",
[DirectiveBitEnumCase]> {
@@ -98,6 +107,11 @@ def DirectiveAEnumInvalidH : TestFormat_Op<[{
enum($attr) `,` attr-dict
}]>, Arguments<(ins DirectiveCommaBitEnumAttr:$attr)>;
+// CHECK: error: space-separated bit enum cannot be followed by its 'case' case literal
+def DirectiveAEnumInvalidI : TestFormat_Op<[{
+ enum($attr) `case` attr-dict
+}]>, Arguments<(ins DirectiveSpaceBitEnumAttr:$attr)>;
+
//===----------------------------------------------------------------------===//
// attr-dict
//===----------------------------------------------------------------------===//
diff --git a/mlir/tools/mlir-tblgen/EnumsGen.cpp b/mlir/tools/mlir-tblgen/EnumsGen.cpp
index dba18803c86969..aafd87d0682aba 100644
--- a/mlir/tools/mlir-tblgen/EnumsGen.cpp
+++ b/mlir/tools/mlir-tblgen/EnumsGen.cpp
@@ -225,9 +225,41 @@ FieldParser<std::optional<{0}>, std::optional<{0}>>::parse(AsmParser &parser) {{
return std::optional<{0}>{{flags};
}
} // namespace mlir
+)";
+
+ // With a space separator, the flags are a run of case keywords that
+ // ends at the first token that is not one.
+ const char *const fieldParserDefsSpaceBitEnum = R"(
+namespace mlir {
+FailureOr<{0}> FieldParser<{0}, {0}>::parse(AsmParser &parser) {{
+ auto loc = parser.getCurrentLocation();
+ FailureOr<std::optional<{0}>> flags =
+ FieldParser<std::optional<{0}>>::parse(parser);
+ if (failed(flags))
+ return failure();
+ if (!*flags)
+ return parser.emitError(loc, "expected one of {3} for {2}");
+ return **flags;
+}
+
+FailureOr<std::optional<{0}>>
+FieldParser<std::optional<{0}>, std::optional<{0}>>::parse(AsmParser &parser) {{
+ std::optional<{0}> flags;
+ ::llvm::StringRef enumKeyword;
+ while (::mlir::succeeded(parser.parseOptionalKeyword(&enumKeyword, {4})))
+ flags = flags.value_or({0}{{}) | *{1}::symbolizeEnum<{0}>(enumKeyword);
+ return flags;
+}
+} // namespace mlir
)";
if (enumInfo.isNewStyleBitEnum()) {
+ if (enumInfo.getBitEnumSeparatorKind() ==
+ EnumInfo::BitEnumSeparatorKind::Space) {
+ os << formatv(fieldParserDefsSpaceBitEnum, qualName, cppNamespace,
+ enumInfo.getSummary(), casesList, casesInitList);
+ return;
+ }
StringRef parseSeparatorFn = enumInfo.getBitEnumSeparatorParser();
os << formatv(fieldParserDefsUnquotedBitEnum, qualName, cppNamespace,
enumInfo.getSummary(), casesList,
@@ -576,8 +608,14 @@ static void emitStrToSymFnForBitEnum(const Record &enumDef, raw_ostream &os) {
// Split the string to get symbols for all the bits.
os << " ::llvm::SmallVector<::llvm::StringRef, 2> symbols;\n";
- // Remove whitespace from the separator string when parsing.
- os << formatv(" str.split(symbols, \"{0}\");\n\n", separatorTrimmed);
+ // Remove whitespace from the separator string when parsing. A space
+ // separator splits on spaces and drops the empty pieces.
+ if (enumInfo.getBitEnumSeparatorKind() ==
+ EnumInfo::BitEnumSeparatorKind::Space)
+ os << " str.split(symbols, ' ', /*MaxSplit=*/-1, "
+ "/*KeepEmpty=*/false);\n\n";
+ else
+ os << formatv(" str.split(symbols, \"{0}\");\n\n", separatorTrimmed);
os << formatv(" {0} val = 0;\n", underlyingType);
os << " for (auto symbol : symbols) {\n";
diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
index dadbfc6222ada9..791e0df16509bb 100644
--- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
@@ -614,7 +614,8 @@ const char *const enumAttrParserCode = R"(
/// {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.
+/// {8}: The code that parses the next case keyword into `attrStr`, breaking
+/// out of the loop when there is none.
const char *const unquotedBitEnumAttrParserCode = R"(
{
::llvm::StringRef attrStr;
@@ -629,10 +630,7 @@ const char *const unquotedBitEnumAttrParserCode = R"(
return parser.emitError(loc, "invalid ")
<< "{0} attribute specification: \"" << attrStr << '"';;
flags = flags | *flag;
- if (failed(parser.{8}()))
- break;
- if (parser.parseKeyword(&attrStr))
- return ::mlir::failure();
+ {8}
}
auto attrOptional = ::std::optional<{7}>(flags);
{0}Attr = {3};
@@ -1311,14 +1309,27 @@ static void genEnumAttrParser(const NamedAttribute *var, MethodBody &body,
if (formatAsEnumDirective && enumInfo.isBitEnum() &&
(formatBitEnumAsUnquoted || !enumInfo.printBitEnumQuoted())) {
- StringRef parseSeparatorFn = enumInfo.getBitEnumSeparatorParser();
+ // With a space separator, the cases are a run of keywords;
+ // otherwise each further case follows a separator token.
+ std::string parseNextCase;
+ if (enumInfo.getBitEnumSeparatorKind() ==
+ EnumInfo::BitEnumSeparatorKind::Space) {
+ parseNextCase =
+ formatv("if (parser.parseOptionalKeyword(&attrStr, {0})) break;",
+ validCaseKeywordsStr);
+ } else {
+ parseNextCase = formatv("if (failed(parser.{0}())) break;\n"
+ " if (parser.parseKeyword(&attrStr))\n"
+ " return ::mlir::failure();",
+ enumInfo.getBitEnumSeparatorParser());
+ }
std::string enumType =
(enumInfo.getCppNamespace() + "::" + enumInfo.getEnumClassName()).str();
body << formatv(unquotedBitEnumAttrParserCode, var->name,
enumInfo.getCppNamespace(),
enumInfo.getStringToSymbolFnName(), attrBuilderStr,
validCaseKeywordsStr, errorMessage, attrAssignment,
- enumType, parseSeparatorFn);
+ enumType, parseNextCase);
return;
}
@@ -4003,6 +4014,18 @@ OpFormatParser::verifyEnumAdjacentLiterals(SMLoc loc,
return emitError(loc, "unquoted bit enum cannot be followed by its '" +
separator + "' separator literal");
}
+
+ // With a space separator, the enum parser would take a following
+ // case literal as part of the enum.
+ if (enumInfo.getBitEnumSeparatorKind() ==
+ EnumInfo::BitEnumSeparatorKind::Space &&
+ llvm::any_of(enumInfo.getAllCases(), [&](const EnumCase &enumCase) {
+ return enumCase.getStr() == literal->getSpelling();
+ })) {
+ return emitError(loc, "space-separated bit enum cannot be followed by "
+ "its '" +
+ literal->getSpelling() + "' case literal");
+ }
}
return success();
}
More information about the Mlir-commits
mailing list