diff --git a/mlir/test/mlir-tblgen/invalid.mlir b/mlir/test/mlir-tblgen/invalid.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-tblgen/invalid.mlir @@ -0,0 +1,4 @@ +// RUN: mlir-opt -split-input-file -verify-diagnostics %s + +// expected-error @+1 {{attribute attr not allowed in attributes}} +test.format_symbol_name_attr_op @name { attr = "xx" } diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp --- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp +++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp @@ -403,7 +403,7 @@ /// Generate the operation parser from this format. void genParser(Operator &op, OpClass &opClass); /// Generate the parser code for a specific format element. - void genElementParser(Element *element, OpMethodBody &body, + void genElementParser(Element *element, Operator &op, OpMethodBody &body, FmtContext &attrTypeCtx); /// Generate the c++ to resolve the types of operands and results during /// parsing. @@ -1010,7 +1010,7 @@ // Generate parsers for each of the elements. for (auto &element : elements) - genElementParser(element.get(), body, attrTypeCtx); + genElementParser(element.get(), op, body, attrTypeCtx); // Generate the code to resolve the operand/result types and successors now // that they have been parsed. @@ -1022,7 +1022,28 @@ body << " return ::mlir::success();\n"; } -void OperationFormat::genElementParser(Element *element, OpMethodBody &body, +/// Returns all the elided attributes in a comma separated strings. These +/// attributes are handled by the printer/parser as a part of printing/parsing +/// and should not appear in the printed/parsed attribute dictionary. +static std::string getElidedAttributes(OperationFormat &fmt, Operator &op) { + std::string result; + llvm::raw_string_ostream os(result); + + // Elide the variadic segment size attributes if necessary. + if (!fmt.allOperands && + op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) + os << "\"operand_segment_sizes\", "; + if (!fmt.allResultTypes && + op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) + os << "\"result_segment_sizes\", "; + llvm::interleaveComma( + fmt.usedAttributes, os, + [&](const NamedAttribute *attr) { os << "\"" << attr->name << "\""; }); + return os.str(); +} + +void OperationFormat::genElementParser(Element *element, Operator &op, + OpMethodBody &body, FmtContext &attrTypeCtx) { /// Optional Group. if (auto *optional = dyn_cast(element)) { @@ -1032,19 +1053,19 @@ // parsing of the rest of the elements. Element *firstElement = &*elements.begin(); if (auto *attrVar = dyn_cast(firstElement)) { - genElementParser(attrVar, body, attrTypeCtx); + genElementParser(attrVar, op, body, attrTypeCtx); body << " if (" << attrVar->getVar()->name << "Attr) {\n"; } else if (auto *literal = dyn_cast(firstElement)) { body << " if (succeeded(parser.parseOptional"; genLiteralParser(literal->getLiteral(), body); body << ")) {\n"; } else if (auto *opVar = dyn_cast(firstElement)) { - genElementParser(opVar, body, attrTypeCtx); + genElementParser(opVar, op, body, attrTypeCtx); body << " if (!" << opVar->getVar()->name << "Operands.empty()) {\n"; } else if (auto *regionVar = dyn_cast(firstElement)) { const NamedRegion *region = regionVar->getVar(); if (region->isVariadic()) { - genElementParser(regionVar, body, attrTypeCtx); + genElementParser(regionVar, op, body, attrTypeCtx); body << " if (!" << region->name << "Regions.empty()) {\n"; } else { body << llvm::formatv(optionalRegionParserCode, region->name); @@ -1069,7 +1090,7 @@ // Generate the rest of the elements normally. for (Element &childElement : llvm::drop_begin(elements, 1)) { if (&childElement != elidedAnchorElement) - genElementParser(&childElement, body, attrTypeCtx); + genElementParser(&childElement, op, body, attrTypeCtx); } body << " }\n"; @@ -1152,10 +1173,27 @@ /// Directives. } else if (auto *attrDict = dyn_cast(element)) { + std::string elidedAttributes = getElidedAttributes(*this, op); + StringRef attribDictName = "result.attributes"; + if (!elidedAttributes.empty()) { + body << " ::mlir::NamedAttrList parsedAttributes;\n"; + body << " auto attribDictLoc = parser.getNameLoc();\n"; + attribDictName = "parsedAttributes"; + } body << " if (parser.parseOptionalAttrDict" - << (attrDict->isWithKeyword() ? "WithKeyword" : "") - << "(result.attributes))\n" + << (attrDict->isWithKeyword() ? "WithKeyword" : "") << "(" + << attribDictName << "))\n" << " return ::mlir::failure();\n"; + // Check that the parsed atttributes are not one of the attributes the + // printer would elide. + if (!elidedAttributes.empty()) { + body << " for (StringRef elidedAttr : {" << elidedAttributes << "})\n"; + body << " if (parsedAttributes.get(elidedAttr))\n"; + body + << " return parser.emitError(attribDictLoc) << \"attribute \" " + "<< elidedAttr << \" not allowed in attributes\";\n"; + body << "result.attributes.append(parsedAttributes.getAttrs());\n"; + } } else if (auto *customDir = dyn_cast(element)) { genCustomDirectiveParser(customDir, body); @@ -1452,18 +1490,8 @@ static void genAttrDictPrinter(OperationFormat &fmt, Operator &op, OpMethodBody &body, bool withKeyword) { body << " p.printOptionalAttrDict" << (withKeyword ? "WithKeyword" : "") - << "(getAttrs(), /*elidedAttrs=*/{"; - // Elide the variadic segment size attributes if necessary. - if (!fmt.allOperands && - op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) - body << "\"operand_segment_sizes\", "; - if (!fmt.allResultTypes && - op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) - body << "\"result_segment_sizes\", "; - llvm::interleaveComma( - fmt.usedAttributes, body, - [&](const NamedAttribute *attr) { body << "\"" << attr->name << "\""; }); - body << "});\n"; + << "(getAttrs(), /*elidedAttrs=*/{" << getElidedAttributes(fmt, op) + << "});\n"; } /// Generate the printer for a literal value. `shouldEmitSpace` is true if a