T::getParseAssemblyFn(), T::getPrintAssemblyFn(),
T::getVerifyInvariantsFn(), T::getFoldHookFn(),
T::getGetCanonicalizationPatternsFn(), T::getInterfaceMap(),
- T::getHasTraitFn());
+ T::getHasTraitFn(), T::getAttributeNames());
}
/// Register a new operation in a Dialect object.
ParseAssemblyFn &&parseAssembly, PrintAssemblyFn &&printAssembly,
VerifyInvariantsFn &&verifyInvariants, FoldHookFn &&foldHook,
GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
- detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait);
+ detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
+ ArrayRef<StringRef> attrNames);
+
+ /// Return the list of cached attribute names registered to this operation.
+ /// The order of attributes cached here is unique to each type of operation,
+ /// and the interpretation of this attribute list should generally be driven
+ /// by the respective operation. In many cases, this caching removes the need
+ /// to use the raw string name of a known attribute.
+ ///
+ /// For example the ODS generator, with an op defining the following
+ /// attributes:
+ ///
+ /// let arguments = (ins I32Attr:$attr1, I32Attr:$attr2);
+ ///
+ /// ... may produce an order here of ["attr1", "attr2"]. This allows for the
+ /// ODS generator to directly access the cached name for a known attribute,
+ /// greatly simplifying the cost and complexity of attribute usage produced by
+ /// the generator.
+ ///
+ ArrayRef<Identifier> getAttributeNames() const { return attributeNames; }
private:
AbstractOperation(StringRef name, Dialect &dialect, TypeID typeID,
VerifyInvariantsFn &&verifyInvariants,
FoldHookFn &&foldHook,
GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
- detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait);
+ detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
+ ArrayRef<Identifier> attrNames);
/// Give Op access to lookupMutable.
template <typename ConcreteType, template <typename T> class... Traits>
ParseAssemblyFn parseAssemblyFn;
PrintAssemblyFn printAssemblyFn;
VerifyInvariantsFn verifyInvariantsFn;
+
+ /// A list of attribute names registered to this operation in identifier form.
+ /// This allows for operation classes to use identifiers for attribute
+ /// lookup/creation/etc., as opposed to strings.
+ ArrayRef<Identifier> attributeNames;
};
//===----------------------------------------------------------------------===//
ParseAssemblyFn &&parseAssembly, PrintAssemblyFn &&printAssembly,
VerifyInvariantsFn &&verifyInvariants, FoldHookFn &&foldHook,
GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
- detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait) {
- AbstractOperation opInfo(name, dialect, typeID, std::move(parseAssembly),
- std::move(printAssembly),
- std::move(verifyInvariants), std::move(foldHook),
- std::move(getCanonicalizationPatterns),
- std::move(interfaceMap), std::move(hasTrait));
-
- auto &impl = dialect.getContext()->getImpl();
+ detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
+ ArrayRef<StringRef> attrNames) {
+ MLIRContext *ctx = dialect.getContext();
+ auto &impl = ctx->getImpl();
assert(impl.multiThreadedExecutionContext == 0 &&
"Registering a new operation kind while in a multi-threaded execution "
"context");
+
+ // Register the attribute names of this operation.
+ MutableArrayRef<Identifier> cachedAttrNames;
+ if (!attrNames.empty()) {
+ cachedAttrNames = MutableArrayRef<Identifier>(
+ impl.identifierAllocator.Allocate<Identifier>(attrNames.size()),
+ attrNames.size());
+ for (unsigned i : llvm::seq<unsigned>(0, attrNames.size()))
+ new (&cachedAttrNames[i]) Identifier(Identifier::get(attrNames[i], ctx));
+ }
+
+ // Register the information for this operation.
+ AbstractOperation opInfo(
+ name, dialect, typeID, std::move(parseAssembly), std::move(printAssembly),
+ std::move(verifyInvariants), std::move(foldHook),
+ std::move(getCanonicalizationPatterns), std::move(interfaceMap),
+ std::move(hasTrait), cachedAttrNames);
if (!impl.registeredOperations.insert({name, std::move(opInfo)}).second) {
llvm::errs() << "error: operation named '" << name
<< "' is already registered.\n";
ParseAssemblyFn &&parseAssembly, PrintAssemblyFn &&printAssembly,
VerifyInvariantsFn &&verifyInvariants, FoldHookFn &&foldHook,
GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
- detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait)
+ detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
+ ArrayRef<Identifier> attrNames)
: name(Identifier::get(name, dialect.getContext())), dialect(dialect),
typeID(typeID), interfaceMap(std::move(interfaceMap)),
foldHookFn(std::move(foldHook)),
hasTraitFn(std::move(hasTrait)),
parseAssemblyFn(std::move(parseAssembly)),
printAssemblyFn(std::move(printAssembly)),
- verifyInvariantsFn(std::move(verifyInvariants)) {}
+ verifyInvariantsFn(std::move(verifyInvariants)),
+ attributeNames(attrNames) {}
//===----------------------------------------------------------------------===//
// AbstractType
);
}
+// DECL-LABEL: AOp declarations
+
+// Test attribute name methods
+// ---
+
+// DECL: static ::llvm::ArrayRef<::llvm::StringRef> getAttributeNames()
+// DECL-NEXT: static ::llvm::StringRef attrNames[] =
+// DECL-SAME: {::llvm::StringRef("aAttr"), ::llvm::StringRef("bAttr"), ::llvm::StringRef("cAttr")};
+// DECL-NEXT: return attrNames;
+
+// DECL: ::mlir::Identifier aAttrAttrName()
+// DECL-NEXT: return getAttributeNameForIndex(0);
+// DECL: ::mlir::Identifier aAttrAttrName(::mlir::OperationName name)
+// DECL-NEXT: return getAttributeNameForIndex(name, 0);
+
+// DECL: ::mlir::Identifier bAttrAttrName()
+// DECL-NEXT: return getAttributeNameForIndex(1);
+// DECL: ::mlir::Identifier bAttrAttrName(::mlir::OperationName name)
+// DECL-NEXT: return getAttributeNameForIndex(name, 1);
+
+// DECL: ::mlir::Identifier cAttrAttrName()
+// DECL-NEXT: return getAttributeNameForIndex(2);
+// DECL: ::mlir::Identifier cAttrAttrName(::mlir::OperationName name)
+// DECL-NEXT: return getAttributeNameForIndex(name, 2);
+
// DEF-LABEL: AOp definitions
// Test verify method
// ---
// DEF: some-attr-kind AOp::aAttrAttr()
-// DEF-NEXT: (*this)->getAttr("aAttr").template cast<some-attr-kind>()
+// DEF-NEXT: (*this)->getAttr(aAttrAttrName()).template cast<some-attr-kind>()
// DEF: some-return-type AOp::aAttr() {
// DEF-NEXT: auto attr = aAttrAttr()
// DEF-NEXT: return attr.some-convert-from-storage();
// DEF: some-attr-kind AOp::bAttrAttr()
-// DEF-NEXT: return (*this)->getAttr("bAttr").template dyn_cast_or_null<some-attr-kind>()
+// DEF-NEXT: return (*this)->getAttr(bAttrAttrName()).template dyn_cast_or_null<some-attr-kind>()
// DEF: some-return-type AOp::bAttr() {
// DEF-NEXT: auto attr = bAttrAttr();
// DEF-NEXT: if (!attr)
// DEF-NEXT: return attr.some-convert-from-storage();
// DEF: some-attr-kind AOp::cAttrAttr()
-// DEF-NEXT: return (*this)->getAttr("cAttr").template dyn_cast_or_null<some-attr-kind>()
+// DEF-NEXT: return (*this)->getAttr(cAttrAttrName()).template dyn_cast_or_null<some-attr-kind>()
// DEF: ::llvm::Optional<some-return-type> AOp::cAttr() {
// DEF-NEXT: auto attr = cAttrAttr()
// DEF-NEXT: return attr ? ::llvm::Optional<some-return-type>(attr.some-convert-from-storage()) : (::llvm::None);
// ---
// DEF: void AOp::aAttrAttr(some-attr-kind attr) {
-// DEF-NEXT: (*this)->setAttr("aAttr", attr);
+// DEF-NEXT: (*this)->setAttr(aAttrAttrName(), attr);
// DEF: void AOp::bAttrAttr(some-attr-kind attr) {
-// DEF-NEXT: (*this)->setAttr("bAttr", attr);
+// DEF-NEXT: (*this)->setAttr(bAttrAttrName(), attr);
// DEF: void AOp::cAttrAttr(some-attr-kind attr) {
-// DEF-NEXT: (*this)->setAttr("cAttr", attr);
+// DEF-NEXT: (*this)->setAttr(cAttrAttrName(), attr);
// Test remove methods
// ---
// DEF: ::mlir::Attribute AOp::removeCAttrAttr() {
-// DEF-NEXT: return (*this)->removeAttr("cAttr");
+// DEF-NEXT: return (*this)->removeAttr(cAttrAttrName());
// Test build methods
// ---
// DEF: void AOp::build(
-// DEF: odsState.addAttribute("aAttr", aAttr);
-// DEF: odsState.addAttribute("bAttr", bAttr);
+// DEF: odsState.addAttribute(aAttrAttrName(odsState.name), aAttr);
+// DEF: odsState.addAttribute(bAttrAttrName(odsState.name), bAttr);
// DEF: if (cAttr) {
-// DEF-NEXT: odsState.addAttribute("cAttr", cAttr);
+// DEF-NEXT: odsState.addAttribute(cAttrAttrName(odsState.name), cAttr);
// DEF: void AOp::build(
// DEF: some-return-type aAttr, some-return-type bAttr, /*optional*/some-attr-kind cAttr
-// DEF: odsState.addAttribute("aAttr", some-const-builder-call(odsBuilder, aAttr));
+// DEF: odsState.addAttribute(aAttrAttrName(odsState.name), some-const-builder-call(odsBuilder, aAttr));
// DEF: void AOp::build(
// DEF: ::llvm::ArrayRef<::mlir::NamedAttribute> attributes
// DECL: static void build({{.*}}, uint32_t i32_attr, ::llvm::APFloat f64_attr, ::llvm::StringRef str_attr, bool bool_attr, ::SomeI32Enum enum_attr, uint32_t dv_i32_attr, ::llvm::APFloat dv_f64_attr, ::llvm::StringRef dv_str_attr = "abc", bool dv_bool_attr = true, ::SomeI32Enum dv_enum_attr = ::SomeI32Enum::case5)
// DEF-LABEL: DOp definitions
-// DEF: odsState.addAttribute("str_attr", odsBuilder.getStringAttr(str_attr));
-// DEF: odsState.addAttribute("dv_str_attr", odsBuilder.getStringAttr(dv_str_attr));
+// DEF: odsState.addAttribute(str_attrAttrName(odsState.name), odsBuilder.getStringAttr(str_attr));
+// DEF: odsState.addAttribute(dv_str_attrAttrName(odsState.name), odsBuilder.getStringAttr(dv_str_attr));
// Test derived type attr.
// ---
// DEF: return {{.*}} != nullptr
// DEF: ::mlir::Attribute UnitAttrOp::removeAttrAttr() {
-// DEF-NEXT: (*this)->removeAttr("attr");
+// DEF-NEXT: (*this)->removeAttr(attrAttrName());
// DEF: build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, /*optional*/::mlir::UnitAttr attr)
#include "mlir/TableGen/Operator.h"
#include "mlir/TableGen/SideEffects.h"
#include "mlir/TableGen/Trait.h"
+#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Path.h"
auto sizeAttr = odsAttrs.get("{0}").cast<::mlir::DenseIntElementsAttr>();
)";
const char *opSegmentSizeAttrInitCode = R"(
- auto sizeAttr = (*this)->getAttr("{0}").cast<::mlir::DenseIntElementsAttr>();
+ auto sizeAttr = (*this)->getAttr({0}).cast<::mlir::DenseIntElementsAttr>();
)";
const char *attrSizedSegmentValueRangeCalcCode = R"(
auto sizeAttrValues = sizeAttr.getValues<uint32_t>();
void emitDecl(raw_ostream &os);
void emitDef(raw_ostream &os);
+ // Generate methods for accessing the attribute names of this operation.
+ void genAttrNameGetters();
+
+ // Return the index of the given attribute name. This is a relative ordering
+ // for this name, used in attribute getters.
+ unsigned getAttrNameIndex(StringRef attrName) const;
+
// Generates the OpAsmOpInterface for this operation if possible.
void genOpAsmInterface();
// The emitter containing all of the locally emitted verification functions.
const StaticVerifierFunctionEmitter &staticVerifierEmitter;
+
+ // A map of attribute names (including implicit attributes) registered to the
+ // current operation, to the relative order in which they were registered.
+ llvm::MapVector<StringRef, unsigned> attributeNames;
};
} // end anonymous namespace
// Generate C++ code for various op methods. The order here determines the
// methods in the generated file.
+ genAttrNameGetters();
genOpAsmInterface();
genOpNameGetter();
genNamedOperandGetters();
void OpEmitter::emitDef(raw_ostream &os) { opClass.writeDefTo(os); }
+void OpEmitter::genAttrNameGetters() {
+ // Enumerate the attribute names of this op, assigning each a relative
+ // ordering.
+ auto addAttrName = [&](StringRef name) {
+ unsigned index = attributeNames.size();
+ attributeNames.insert({name, index});
+ };
+ for (const NamedAttribute &namedAttr : op.getAttributes())
+ addAttrName(namedAttr.name);
+ // Include key attributes from several traits as implicitly registered.
+ if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"))
+ addAttrName("operand_segment_sizes");
+ if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))
+ addAttrName("result_segment_sizes");
+
+ // Emit the getAttributeNames method.
+ {
+ auto *method = opClass.addMethodAndPrune(
+ "::llvm::ArrayRef<::llvm::StringRef>", "getAttributeNames",
+ OpMethod::Property(OpMethod::MP_Static | OpMethod::MP_Inline));
+ auto &body = method->body();
+ if (attributeNames.empty()) {
+ body << " return {};";
+ } else {
+ body << " static ::llvm::StringRef attrNames[] = {";
+ llvm::interleaveComma(llvm::make_first_range(attributeNames), body,
+ [&](StringRef attrName) {
+ body << "::llvm::StringRef(\"" << attrName
+ << "\")";
+ });
+ body << "};\n return attrNames;";
+ }
+ }
+
+ // Emit the getAttributeNameForIndex methods.
+ {
+ auto *method = opClass.addMethodAndPrune(
+ "::mlir::Identifier", "getAttributeNameForIndex",
+ OpMethod::Property(OpMethod::MP_Private | OpMethod::MP_Inline),
+ "unsigned", "index");
+ method->body()
+ << " return getAttributeNameForIndex((*this)->getName(), index);";
+ }
+ {
+ auto *method = opClass.addMethodAndPrune(
+ "::mlir::Identifier", "getAttributeNameForIndex",
+ OpMethod::Property(OpMethod::MP_Private | OpMethod::MP_Inline |
+ OpMethod::MP_Static),
+ "::mlir::OperationName name, unsigned index");
+ method->body() << "assert(index < " << attributeNames.size()
+ << " && \"invalid attribute index\");\n"
+ " return name.getAbstractOperation()"
+ "->getAttributeNames()[index];";
+ }
+
+ // Generate the <attr>AttrName methods, that expose the attribute names to
+ // users.
+ const char *attrNameMethodBody = " return getAttributeNameForIndex({0});";
+ for (const std::pair<StringRef, unsigned> &attrIt : attributeNames) {
+ std::string methodName = (attrIt.first + "AttrName").str();
+
+ // Generate the non-static variant.
+ {
+ auto *method =
+ opClass.addMethodAndPrune("::mlir::Identifier", methodName,
+ OpMethod::Property(OpMethod::MP_Inline));
+ method->body() << llvm::formatv(attrNameMethodBody, attrIt.second).str();
+ }
+
+ // Generate the static variant.
+ {
+ auto *method = opClass.addMethodAndPrune(
+ "::mlir::Identifier", methodName,
+ OpMethod::Property(OpMethod::MP_Inline | OpMethod::MP_Static),
+ "::mlir::OperationName", "name");
+ method->body() << llvm::formatv(attrNameMethodBody,
+ "name, " + Twine(attrIt.second))
+ .str();
+ }
+ }
+}
+
+unsigned OpEmitter::getAttrNameIndex(StringRef attrName) const {
+ auto it = attributeNames.find(attrName);
+ assert(it != attributeNames.end() && "expected attribute name to have been "
+ "registered in genAttrNameGetters");
+ return it->second;
+}
+
void OpEmitter::genAttrGetters() {
FmtContext fctx;
fctx.withBuilder("::mlir::Builder((*this)->getContext())");
- Dialect opDialect = op.getDialect();
// Emit the derived attribute body.
auto emitDerivedAttr = [&](StringRef name, Attribute attr) {
- auto *method = opClass.addMethodAndPrune(attr.getReturnType(), name);
- if (!method)
- return;
- auto &body = method->body();
- body << " " << attr.getDerivedCodeBody() << "\n";
+ if (auto *method = opClass.addMethodAndPrune(attr.getReturnType(), name))
+ method->body() << " " << attr.getDerivedCodeBody() << "\n";
};
// Emit with return type specified.
if (!method)
return;
auto &body = method->body();
- body << " return (*this)->getAttr(\"" << name << "\").template ";
+ body << " return (*this)->getAttr(" << name << "AttrName()).template ";
if (attr.isOptional() || attr.hasDefaultValue())
body << "dyn_cast_or_null<";
else
body << attr.getStorageType() << ">();";
};
- for (auto &namedAttr : op.getAttributes()) {
- const auto &name = namedAttr.name;
- const auto &attr = namedAttr.attr;
- if (attr.isDerivedAttr()) {
- emitDerivedAttr(name, attr);
+ for (const NamedAttribute &namedAttr : op.getAttributes()) {
+ if (namedAttr.attr.isDerivedAttr()) {
+ emitDerivedAttr(namedAttr.name, namedAttr.attr);
} else {
- emitAttrWithStorageType(name, attr);
- emitAttrWithReturnType(name, attr);
+ emitAttrWithStorageType(namedAttr.name, namedAttr.attr);
+ emitAttrWithReturnType(namedAttr.name, namedAttr.attr);
}
}
derivedAttrs, body,
[&](const NamedAttribute &namedAttr) {
auto tmpl = namedAttr.attr.getConvertFromStorageCall();
- body << " {::mlir::Identifier::get(\"" << namedAttr.name
- << "\", ctx),\n"
+ body << " {" << namedAttr.name << "AttrName(),\n"
<< tgfmt(tmpl, &fctx.withSelf(namedAttr.name + "()")
.withBuilder("odsBuilder")
.addSubst("_ctx", "ctx"))
auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) {
auto *method = opClass.addMethodAndPrune("void", (name + "Attr").str(),
attr.getStorageType(), "attr");
- if (!method)
- return;
- auto &body = method->body();
- body << " (*this)->setAttr(\"" << name << "\", attr);";
+ if (method)
+ method->body() << " (*this)->setAttr(" << name << "AttrName(), attr);";
};
- for (auto &namedAttr : op.getAttributes()) {
- const auto &name = namedAttr.name;
- const auto &attr = namedAttr.attr;
- if (!attr.isDerivedAttr())
- emitAttrWithStorageType(name, attr);
- }
+ for (const NamedAttribute &namedAttr : op.getAttributes())
+ if (!namedAttr.attr.isDerivedAttr())
+ emitAttrWithStorageType(namedAttr.name, namedAttr.attr);
}
void OpEmitter::genOptionalAttrRemovers() {
"::mlir::Attribute", ("remove" + upperInitial + suffix + "Attr").str());
if (!method)
return;
- auto &body = method->body();
- body << " return (*this)->removeAttr(\"" << name << "\");";
+ method->body() << " return (*this)->removeAttr(" << name << "AttrName());";
};
- for (const auto &namedAttr : op.getAttributes()) {
- const auto &name = namedAttr.name;
- const auto &attr = namedAttr.attr;
- if (attr.isOptional())
- emitRemoveAttr(name);
- }
+ for (const NamedAttribute &namedAttr : op.getAttributes())
+ if (namedAttr.attr.isOptional())
+ emitRemoveAttr(namedAttr.name);
}
// Generates the code to compute the start and end index of an operand or result
}
void OpEmitter::genNamedOperandGetters() {
+ // Build the code snippet used for initializing the operand_segment_sizes
+ // array.
+ std::string attrSizeInitCode;
+ if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
+ attrSizeInitCode =
+ formatv(opSegmentSizeAttrInitCode, "operand_segment_sizesAttrName()")
+ .str();
+ }
+
generateNamedOperandGetters(
op, opClass,
- /*sizeAttrInit=*/
- formatv(opSegmentSizeAttrInitCode, "operand_segment_sizes").str(),
+ /*sizeAttrInit=*/attrSizeInitCode,
/*rangeType=*/"::mlir::Operation::operand_range",
/*rangeBeginCall=*/"getOperation()->operand_begin()",
/*rangeSizeCall=*/"getOperation()->getNumOperands()",
if (attrSizedOperands)
body << ", ::mlir::MutableOperandRange::OperandSegment(" << i
<< "u, *getOperation()->getAttrDictionary().getNamed("
- "\"operand_segment_sizes\"))";
+ "operand_segment_sizesAttrName()))";
body << ");\n";
}
}
"'SameVariadicResultSize' traits");
}
+ // Build the initializer string for the result segment size attribute.
+ std::string attrSizeInitCode;
+ if (attrSizedResults) {
+ attrSizeInitCode =
+ formatv(opSegmentSizeAttrInitCode, "result_segment_sizesAttrName()")
+ .str();
+ }
+
generateValueRangeStartAndEnd(
opClass, "getODSResultIndexAndLength", numVariadicResults,
numNormalResults, "getOperation()->getNumResults()", attrSizedResults,
- formatv(opSegmentSizeAttrInitCode, "result_segment_sizes").str(),
- op.getResults());
+ attrSizeInitCode, op.getResults());
auto *m = opClass.addMethodAndPrune("::mlir::Operation::result_range",
"getODSResults", "unsigned", "index");
std::string resultType;
const auto &namedAttr = op.getAttribute(0);
- body << " for (auto attr : attributes) {\n";
- body << " if (attr.first != \"" << namedAttr.name << "\") continue;\n";
+ body << " auto attrName = " << namedAttr.name << "AttrName("
+ << builderOpState
+ << ".name);\n"
+ " for (auto attr : attributes) {\n"
+ " if (attr.first != attrName) continue;\n";
if (namedAttr.attr.isTypeAttr()) {
resultType = "attr.second.cast<::mlir::TypeAttr>().getValue()";
} else {
// If the operation has the operand segment size attribute, add it here.
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
body << " " << builderOpState
- << ".addAttribute(\"operand_segment_sizes\", "
- "odsBuilder.getI32VectorAttr({";
+ << ".addAttribute(operand_segment_sizesAttrName(" << builderOpState
+ << ".name), "
+ << "odsBuilder.getI32VectorAttr({";
interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {
if (op.getOperand(i).isOptional())
body << "(" << getArgumentName(op, i) << " ? 1 : 0)";
auto &attr = namedAttr.attr;
if (!attr.isDerivedAttr()) {
bool emitNotNullCheck = attr.isOptional();
- if (emitNotNullCheck) {
+ if (emitNotNullCheck)
body << formatv(" if ({0}) ", namedAttr.name) << "{\n";
- }
+
if (isRawValueAttr && canUseUnwrappedRawValue(attr)) {
// If this is a raw value, then we need to wrap it in an Attribute
// instance.
std::string value =
std::string(tgfmt(builderTemplate, &fctx, namedAttr.name));
- body << formatv(" {0}.addAttribute(\"{1}\", {2});\n", builderOpState,
- namedAttr.name, value);
+ body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {2});\n",
+ builderOpState, namedAttr.name, value);
} else {
- body << formatv(" {0}.addAttribute(\"{1}\", {1});\n", builderOpState,
- namedAttr.name);
+ body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {1});\n",
+ builderOpState, namedAttr.name);
}
- if (emitNotNullCheck) {
+ if (emitNotNullCheck)
body << " }\n";
- }
}
}