text: //===- AttrOrTypeDefGen.cpp - MLIR AttrOrType definitions generator -------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "mlir/Support/LogicalResult.h"
  #include "mlir/TableGen/AttrOrTypeDef.h"
  #include "mlir/TableGen/CodeGenHelpers.h"
  #include "mlir/TableGen/Format.h"
  #include "mlir/TableGen/GenInfo.h"
  #include "mlir/TableGen/Interfaces.h"
  #include "llvm/ADT/Sequence.h"
  #include "llvm/ADT/SmallSet.h"
  #include "llvm/ADT/StringSet.h"
  #include "llvm/Support/CommandLine.h"
  #include "llvm/TableGen/Error.h"
  #include "llvm/TableGen/TableGenBackend.h"
  
  #define DEBUG_TYPE "mlir-tblgen-attrortypedefgen"
  
  using namespace mlir;
  using namespace mlir::tblgen;
  
  /// Find all the AttrOrTypeDef for the specified dialect. If no dialect
  /// specified and can only find one dialect's defs, use that.
  static void collectAllDefs(StringRef selectedDialect,
                             std::vector<llvm::Record *> records,
                             SmallVectorImpl<AttrOrTypeDef> &resultDefs) {
    auto defs = llvm::map_range(
        records, [&](const llvm::Record *rec) { return AttrOrTypeDef(rec); });
    if (defs.empty())
      return;
  
    StringRef dialectName;
    if (selectedDialect.empty()) {
      if (defs.empty())
        return;
  
      Dialect dialect(nullptr);
      for (const AttrOrTypeDef &typeDef : defs) {
        if (!dialect) {
          dialect = typeDef.getDialect();
        } else if (dialect != typeDef.getDialect()) {
          llvm::PrintFatalError("defs belonging to more than one dialect. Must "
                                "select one via '--(attr|type)defs-dialect'");
        }
      }
  
      dialectName = dialect.getName();
    } else {
      dialectName = selectedDialect;
    }
  
    for (const AttrOrTypeDef &def : defs)
      if (def.getDialect().getName().equals(dialectName))
        resultDefs.push_back(def);
  }
  
  //===----------------------------------------------------------------------===//
  // ParamCommaFormatter
  //===----------------------------------------------------------------------===//
  
  namespace {
  
  /// Pass an instance of this class to llvm::formatv() to emit a comma separated
  /// list of parameters in the format by 'EmitFormat'.
  class ParamCommaFormatter : public llvm::detail::format_adapter {
  public:
    /// Choose the output format
    enum EmitFormat {
      /// Emit "parameter1Type parameter1Name, parameter2Type parameter2Name,
      /// [...]".
      TypeNamePairs,
  
      /// Emit "parameter1(parameter1), parameter2(parameter2), [...]".
      TypeNameInitializer,
  
      /// Emit "param1Name, param2Name, [...]".
      JustParams,
    };
  
    ParamCommaFormatter(EmitFormat emitFormat,
                        ArrayRef<AttrOrTypeParameter> params,
                        bool prependComma = true)
        : emitFormat(emitFormat), params(params), prependComma(prependComma) {}
  
    /// llvm::formatv will call this function when using an instance as a
    /// replacement value.
    void format(raw_ostream &os, StringRef options) override {
      if (!params.empty() && prependComma)
        os << ", ";
  
      switch (emitFormat) {
      case EmitFormat::TypeNamePairs:
        interleaveComma(params, os, [&](const AttrOrTypeParameter &p) {
          emitTypeNamePair(p, os);
        });
        break;
      case EmitFormat::TypeNameInitializer:
        interleaveComma(params, os, [&](const AttrOrTypeParameter &p) {
          emitTypeNameInitializer(p, os);
        });
        break;
      case EmitFormat::JustParams:
        interleaveComma(params, os,
                        [&](const AttrOrTypeParameter &p) { os << p.getName(); });
        break;
      }
    }
  
  private:
    // Emit "paramType paramName".
    static void emitTypeNamePair(const AttrOrTypeParameter &param,
                                 raw_ostream &os) {
      os << param.getCppType() << " " << param.getName();
    }
    // Emit "paramName(paramName)"
    void emitTypeNameInitializer(const AttrOrTypeParameter &param,
                                 raw_ostream &os) {
      os << param.getName() << "(" << param.getName() << ")";
    }
  
    EmitFormat emitFormat;
    ArrayRef<AttrOrTypeParameter> params;
    bool prependComma;
  };
  
  } // end anonymous namespace
  
  //===----------------------------------------------------------------------===//
  // DefGenerator
  //===----------------------------------------------------------------------===//
  
  namespace {
  /// This struct is the base generator used when processing tablegen interfaces.
  class DefGenerator {
  public:
    bool emitDecls(StringRef selectedDialect);
    bool emitDefs(StringRef selectedDialect);
  
  protected:
    DefGenerator(std::vector<llvm::Record *> &&defs, raw_ostream &os)
        : defRecords(std::move(defs)), os(os), isAttrGenerator(false) {}
  
    /// Emit the declaration of a single def.
    void emitDefDecl(const AttrOrTypeDef &def);
    /// Emit the list of def type names.
    void emitTypeDefList(ArrayRef<AttrOrTypeDef> defs);
    /// Emit the code to dispatch between different defs during parsing/printing.
    void emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs);
    /// Emit the definition of a single def.
    void emitDefDef(const AttrOrTypeDef &def);
    /// Emit the storage class for the given def.
    void emitStorageClass(const AttrOrTypeDef &def);
    /// Emit the parser/printer for the given def.
    void emitParsePrint(const AttrOrTypeDef &def);
  
    /// The set of def records to emit.
    std::vector<llvm::Record *> defRecords;
    /// The stream to emit to.
    raw_ostream &os;
    /// The prefix of the tablegen def name, e.g. Attr or Type.
    StringRef defTypePrefix;
    /// The C++ base value type of the def, e.g. Attribute or Type.
    StringRef valueType;
    /// Flag indicating if this generator is for Attributes. False if the
    /// generator is for types.
    bool isAttrGenerator;
  };
  
  /// A specialized generator for AttrDefs.
  struct AttrDefGenerator : public DefGenerator {
    AttrDefGenerator(const llvm::RecordKeeper &records, raw_ostream &os)
        : DefGenerator(records.getAllDerivedDefinitions("AttrDef"), os) {
      isAttrGenerator = true;
      defTypePrefix = "Attr";
      valueType = "Attribute";
    }
  };
  /// A specialized generator for TypeDefs.
  struct TypeDefGenerator : public DefGenerator {
    TypeDefGenerator(const llvm::RecordKeeper &records, raw_ostream &os)
        : DefGenerator(records.getAllDerivedDefinitions("TypeDef"), os) {
      defTypePrefix = "Type";
      valueType = "Type";
    }
  };
  } // end anonymous namespace
  
  //===----------------------------------------------------------------------===//
  // GEN: Declarations
  //===----------------------------------------------------------------------===//
  
  /// Print this above all the other declarations. Contains type declarations used
  /// later on.
  static const char *const typeDefDeclHeader = R"(
  namespace mlir {
  class DialectAsmParser;
  class DialectAsmPrinter;
  } // namespace mlir
  )";
  
  /// The code block for the start of a typeDef class declaration -- singleton
  /// case.
  ///
  /// {0}: The name of the def class.
  /// {1}: The name of the type base class.
  /// {2}: The name of the base value type, e.g. Attribute or Type.
  /// {3}: The tablegen record type prefix, e.g. Attr or Type.
  /// {4}: The traits of the def class.
  static const char *const defDeclSingletonBeginStr = R"(
    class {0} : public ::mlir::{2}::{3}Base<{0}, {1}, ::mlir::{2}Storage{4}> {{
    public:
      /// Inherit some necessary constructors from '{3}Base'.
      using Base::Base;
  )";
  
  /// The code block for the start of a class declaration -- parametric case.
  ///
  /// {0}: The name of the def class.
  /// {1}: The name of the base class.
  /// {2}: The def storage class namespace.
  /// {3}: The storage class name.
  /// {4}: The name of the base value type, e.g. Attribute or Type.
  /// {5}: The tablegen record type prefix, e.g. Attr or Type.
  /// {6}: The traits of the def class.
  static const char *const defDeclParametricBeginStr = R"(
    namespace {2} {
      struct {3};
    } // end namespace {2}
    class {0} : public ::mlir::{4}::{5}Base<{0}, {1},
                                           {2}::{3}{6}> {{
    public:
      /// Inherit some necessary constructors from '{5}Base'.
      using Base::Base;
  
  )";
  
  /// The code snippet for print/parse of an Attribute/Type.
  ///
  /// {0}: The name of the base value type, e.g. Attribute or Type.
  /// {1}: Extra parser parameters.
  static const char *const defDeclParsePrintStr = R"(
      static ::mlir::{0} parse(::mlir::MLIRContext *context,
                               ::mlir::DialectAsmParser &parser{1});
      void print(::mlir::DialectAsmPrinter &printer) const;
  )";
  
  /// The code block for the verify method declaration.
  ///
  /// {0}: List of parameters, parameters style.
  static const char *const defDeclVerifyStr = R"(
      using Base::getChecked;
      static ::mlir::LogicalResult verify(::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError{0});
  )";
  
  /// Emit the builders for the given def.
  static void emitBuilderDecls(const AttrOrTypeDef &def, raw_ostream &os,
                               ParamCommaFormatter &paramTypes) {
    StringRef typeClass = def.getCppClassName();
    bool genCheckedMethods = def.genVerifyDecl();
    if (!def.skipDefaultBuilders()) {
      os << llvm::formatv(
          "    static {0} get(::mlir::MLIRContext *context{1});\n", typeClass,
          paramTypes);
      if (genCheckedMethods) {
        os << llvm::formatv("    static {0} "
                            "getChecked(llvm::function_ref<::mlir::"
                            "InFlightDiagnostic()> emitError, "
                            "::mlir::MLIRContext *context{1});\n",
                            typeClass, paramTypes);
      }
    }
  
    // Generate the builders specified by the user.
    for (const AttrOrTypeBuilder &builder : def.getBuilders()) {
      std::string paramStr;
      llvm::raw_string_ostream paramOS(paramStr);
      llvm::interleaveComma(
          builder.getParameters(), paramOS,
          [&](const AttrOrTypeBuilder::Parameter &param) {
            // Note: AttrOrTypeBuilder parameters are guaranteed to have names.
            paramOS << param.getCppType() << " " << *param.getName();
            if (Optional<StringRef> defaultParamValue = param.getDefaultValue())
              paramOS << " = " << *defaultParamValue;
          });
      paramOS.flush();
  
      // Generate the `get` variant of the builder.
      os << "    static " << typeClass << " get(";
      if (!builder.hasInferredContextParameter()) {
        os << "::mlir::MLIRContext *context";
        if (!paramStr.empty())
          os << ", ";
      }
      os << paramStr << ");\n";
  
      // Generate the `getChecked` variant of the builder.
      if (genCheckedMethods) {
        os << "    static " << typeClass
           << " getChecked(llvm::function_ref<mlir::InFlightDiagnostic()> "
              "emitError";
        if (!builder.hasInferredContextParameter())
          os << ", ::mlir::MLIRContext *context";
        if (!paramStr.empty())
          os << ", ";
        os << paramStr << ");\n";
      }
    }
  }
  
  static void emitInterfaceMethodDecls(const InterfaceTrait *trait,
                                       raw_ostream &os) {
    Interface interface = trait->getInterface();
  
    // Get the set of methods that should always be declared.
    auto alwaysDeclaredMethodsVec = trait->getAlwaysDeclaredMethods();
    llvm::StringSet<> alwaysDeclaredMethods;
    alwaysDeclaredMethods.insert(alwaysDeclaredMethodsVec.begin(),
                                 alwaysDeclaredMethodsVec.end());
  
    for (const InterfaceMethod &method : interface.getMethods()) {
      // Don't declare if the method has a body.
      if (method.getBody())
        continue;
      // Don't declare if the method has a default implementation and the def
      // didn't request that it always be declared.
      if (method.getDefaultImplementation() &&
          !alwaysDeclaredMethods.count(method.getName()))
        continue;
  
      // Emit the method declaration.
      os << "    " << (method.isStatic() ? "static " : "")
         << method.getReturnType() << " " << method.getName() << "(";
      llvm::interleaveComma(method.getArguments(), os,
                            [&](const InterfaceMethod::Argument &arg) {
                              os << arg.type << " " << arg.name;
                            });
      os << ")" << (method.isStatic() ? "" : " const") << ";\n";
    }
  }
  
  void DefGenerator::emitDefDecl(const AttrOrTypeDef &def) {
    SmallVector<AttrOrTypeParameter, 4> params;
    def.getParameters(params);
  
    // Build the trait list for this def.
    std::vector<std::string> traitList;
    StringSet<> traitSet;
    for (const Trait &baseTrait : def.getTraits()) {
      std::string traitStr;
      if (const auto *trait = dyn_cast<NativeTrait>(&baseTrait))
        traitStr = trait->getFullyQualifiedTraitName();
      else if (const auto *trait = dyn_cast<InterfaceTrait>(&baseTrait))
        traitStr = trait->getFullyQualifiedTraitName();
      else
        llvm_unreachable("unexpected Attribute/Type trait type");
  
      if (traitSet.insert(traitStr).second)
        traitList.emplace_back(std::move(traitStr));
    }
    std::string traitStr;
    if (!traitList.empty())
      traitStr = ", " + llvm::join(traitList, ", ");
  
    // Emit the beginning string template: either the singleton or parametric
    // template.
    if (def.getNumParameters() == 0) {
      os << formatv(defDeclSingletonBeginStr, def.getCppClassName(),
                    def.getCppBaseClassName(), valueType, defTypePrefix,
                    traitStr);
    } else {
      os << formatv(defDeclParametricBeginStr, def.getCppClassName(),
                    def.getCppBaseClassName(), def.getStorageNamespace(),
                    def.getStorageClassName(), valueType, defTypePrefix,
                    traitStr);
    }
  
    // Emit the extra declarations first in case there's a definition in there.
    if (Optional<StringRef> extraDecl = def.getExtraDecls())
      os << *extraDecl << "\n";
  
    ParamCommaFormatter emitTypeNamePairsAfterComma(
        ParamCommaFormatter::EmitFormat::TypeNamePairs, params);
    if (!params.empty()) {
      emitBuilderDecls(def, os, emitTypeNamePairsAfterComma);
  
      // Emit the verify invariants declaration.
      if (def.genVerifyDecl())
        os << llvm::formatv(defDeclVerifyStr, emitTypeNamePairsAfterComma);
    }
  
    // Emit the mnenomic, if specified.
    if (auto mnenomic = def.getMnemonic()) {
      os << "    static constexpr ::llvm::StringLiteral getMnemonic() {\n"
         << "      return ::llvm::StringLiteral(\"" << mnenomic << "\");\n"
         << "    }\n";
  
      // If mnemonic specified, emit print/parse declarations.
      if (def.getParserCode() || def.getPrinterCode() || !params.empty()) {
        os << llvm::formatv(defDeclParsePrintStr, valueType,
                            isAttrGenerator ? ", ::mlir::Type type" : "");
      }
    }
  
    if (def.genAccessors()) {
      SmallVector<AttrOrTypeParameter, 4> parameters;
      def.getParameters(parameters);
  
      for (AttrOrTypeParameter &parameter : parameters) {
        SmallString<16> name = parameter.getName();
        name[0] = llvm::toUpper(name[0]);
        os << formatv("    {0} get{1}() const;\n", parameter.getCppAccessorType(),
                      name);
      }
    }
  
    // Emit any interface method declarations.
    for (const Trait &trait : def.getTraits()) {
      if (const auto *traitDef = dyn_cast<InterfaceTrait>(&trait)) {
        if (traitDef->shouldDeclareMethods())
          emitInterfaceMethodDecls(traitDef, os);
      }
    }
  
    // End the decl.
    os << "  };\n";
  }
  
  bool DefGenerator::emitDecls(StringRef selectedDialect) {
    emitSourceFileHeader((defTypePrefix + "Def Declarations").str(), os);
    IfDefScope scope("GET_" + defTypePrefix.upper() + "DEF_CLASSES", os);
  
    // Output the common "header".
    os << typeDefDeclHeader;
  
    SmallVector<AttrOrTypeDef, 16> defs;
    collectAllDefs(selectedDialect, defRecords, defs);
    if (defs.empty())
      return false;
    {
      NamespaceEmitter nsEmitter(os, defs.front().getDialect());
  
      // Declare all the def classes first (in case they reference each other).
      for (const AttrOrTypeDef &def : defs)
        os << "  class " << def.getCppClassName() << ";\n";
  
      // Emit the declarations.
      for (const AttrOrTypeDef &def : defs)
        emitDefDecl(def);
    }
    // Emit the TypeID explicit specializations to have a single definition for
    // each of these.
    for (const AttrOrTypeDef &def : defs)
      if (!def.getDialect().getCppNamespace().empty())
        os << "DECLARE_EXPLICIT_TYPE_ID(" << def.getDialect().getCppNamespace()
           << "::" << def.getCppClassName() << ")\n";
  
    return false;
  }
  
  //===----------------------------------------------------------------------===//
  // GEN: Def List
  //===----------------------------------------------------------------------===//
  
  void DefGenerator::emitTypeDefList(ArrayRef<AttrOrTypeDef> defs) {
    IfDefScope scope("GET_" + defTypePrefix.upper() + "DEF_LIST", os);
    auto interleaveFn = [&](const AttrOrTypeDef &def) {
      os << def.getDialect().getCppNamespace() << "::" << def.getCppClassName();
    };
    llvm::interleave(defs, os, interleaveFn, ",\n");
    os << "\n";
  }
  
  //===----------------------------------------------------------------------===//
  // GEN: Definitions
  //===----------------------------------------------------------------------===//
  
  /// The code block used to start the auto-generated parser function.
  ///
  /// {0}: The name of the base value type, e.g. Attribute or Type.
  /// {1}: Additional parser parameters.
  static const char *const defParserDispatchStartStr = R"(
  static ::mlir::OptionalParseResult generated{0}Parser(::mlir::MLIRContext *context,
                                        ::mlir::DialectAsmParser &parser,
                                        ::llvm::StringRef mnemonic{1},
                                        ::mlir::{0} &value) {{
  )";
  
  /// The code block used to start the auto-generated printer function.
  ///
  /// {0}: The name of the base value type, e.g. Attribute or Type.
  static const char *const defPrinterDispatchStartStr = R"(
  static ::mlir::LogicalResult generated{0}Printer(
                           ::mlir::{0} def, ::mlir::DialectAsmPrinter &printer) {{
    return ::llvm::TypeSwitch<::mlir::{0}, ::mlir::LogicalResult>(def)
  )";
  
  /// Beginning of storage class.
  /// {0}: Storage class namespace.
  /// {1}: Storage class c++ name.
  /// {2}: Parameters parameters.
  /// {3}: Parameter initializer string.
  /// {4}: Parameter types.
  /// {5}: The name of the base value type, e.g. Attribute or Type.
  static const char *const defStorageClassBeginStr = R"(
  namespace {0} {{
    struct {1} : public ::mlir::{5}Storage {{
      {1} ({2})
        : {3} {{ }
  
      /// The hash key is a tuple of the parameter types.
      using KeyTy = std::tuple<{4}>;
  )";
  
  /// The storage class' constructor template.
  ///
  /// {0}: storage class name.
  /// {1}: The name of the base value type, e.g. Attribute or Type.
  static const char *const defStorageClassConstructorBeginStr = R"(
      /// Define a construction method for creating a new instance of this
      /// storage.
      static {0} *construct(::mlir::{1}StorageAllocator &allocator,
                            const KeyTy &tblgenKey) {{
  )";
  
  /// The storage class' constructor return template.
  ///
  /// {0}: storage class name.
  /// {1}: list of parameters.
  static const char *const defStorageClassConstructorEndStr = R"(
        return new (allocator.allocate<{0}>())
            {0}({1});
      }
  )";
  
  /// Use tgfmt to emit custom allocation code for each parameter, if necessary.
  static void emitStorageParameterAllocation(const AttrOrTypeDef &def,
                                             raw_ostream &os) {
    SmallVector<AttrOrTypeParameter> parameters;
    def.getParameters(parameters);
    FmtContext fmtCtxt = FmtContext().addSubst("_allocator", "allocator");
    for (AttrOrTypeParameter &parameter : parameters) {
      if (Optional<StringRef> allocCode = parameter.getAllocator()) {
        fmtCtxt.withSelf(parameter.getName());
        fmtCtxt.addSubst("_dst", parameter.getName());
        os << "      " << tgfmt(*allocCode, &fmtCtxt) << "\n";
      }
    }
  }
  
  /// Builds a code block that initializes the attribute storage of 'def'.
  /// Attribute initialization is separated from Type initialization given that
  /// the Attribute also needs to initialize its self-type, which has multiple
  /// means of initialization.
  static std::string buildAttributeStorageParamInitializer(
      const AttrOrTypeDef &def, ArrayRef<AttrOrTypeParameter> parameters) {
    std::string paramInitializer;
    llvm::raw_string_ostream paramOS(paramInitializer);
    paramOS << "::mlir::AttributeStorage(";
  
    // If this is an attribute, we need to check for value type initialization.
    Optional<size_t> selfParamIndex;
    for (auto it : llvm::enumerate(parameters)) {
      const auto *selfParam = dyn_cast<AttributeSelfTypeParameter>(&it.value());
      if (!selfParam)
        continue;
      if (selfParamIndex) {
        llvm::PrintFatalError(def.getLoc(),
                              "Only one attribute parameter can be marked as "
                              "AttributeSelfTypeParameter");
      }
      paramOS << selfParam->getName();
      selfParamIndex = it.index();
    }
  
    // If we didn't find a self param, but the def has a type builder we use that
    // to construct the type.
    if (!selfParamIndex) {
      const AttrDef &attrDef = cast<AttrDef>(def);
      if (Optional<StringRef> typeBuilder = attrDef.getTypeBuilder()) {
        FmtContext fmtContext;
        for (const AttrOrTypeParameter &param : parameters)
          fmtContext.addSubst(("_" + param.getName()).str(), param.getName());
        paramOS << tgfmt(*typeBuilder, &fmtContext);
      }
    }
    paramOS << ")";
  
    // Append the parameters to the initializer.
    for (auto it : llvm::enumerate(parameters))
      if (it.index() != selfParamIndex)
        paramOS << llvm::formatv(", {0}({0})", it.value().getName());
  
    return paramOS.str();
  }
  
  void DefGenerator::emitStorageClass(const AttrOrTypeDef &def) {
    SmallVector<AttrOrTypeParameter, 4> params;
    def.getParameters(params);
  
    // Collect the parameter types.
    auto parameterTypes =
        llvm::map_range(params, [](const AttrOrTypeParameter &parameter) {
          return parameter.getCppType();
        });
    std::string parameterTypeList = llvm::join(parameterTypes, ", ");
  
    // Collect the parameter initializer.
    std::string paramInitializer;
    if (isAttrGenerator) {
      paramInitializer = buildAttributeStorageParamInitializer(def, params);
  
    } else {
      llvm::raw_string_ostream initOS(paramInitializer);
      llvm::interleaveComma(params, initOS, [&](const AttrOrTypeParameter &it) {
        initOS << llvm::formatv("{0}({0})", it.getName());
      });
    }
  
    // * Emit most of the storage class up until the hashKey body.
    os << formatv(
        defStorageClassBeginStr, def.getStorageNamespace(),
        def.getStorageClassName(),
        ParamCommaFormatter(ParamCommaFormatter::EmitFormat::TypeNamePairs,
                            params, /*prependComma=*/false),
        paramInitializer, parameterTypeList, valueType);
  
    // * Emit the comparison method.
    os << "  bool operator==(const KeyTy &tblgenKey) const {\n";
    for (auto it : llvm::enumerate(params)) {
      os << "    if (!(";
  
      // Build the comparator context.
      bool isSelfType = isa<AttributeSelfTypeParameter>(it.value());
      FmtContext context;
      context.addSubst("_lhs", isSelfType ? "getType()" : it.value().getName())
          .addSubst("_rhs", "std::get<" + Twine(it.index()) + ">(tblgenKey)");
  
      // Use the parameter specified comparator if possible, otherwise default to
      // operator==.
      Optional<StringRef> comparator = it.value().getComparator();
      os << tgfmt(comparator ? *comparator : "$_lhs == $_rhs", &context);
      os << "))\n      return false;\n";
    }
    os << "    return true;\n  }\n";
  
    // * Emit the haskKey method.
    os << "  static ::llvm::hash_code hashKey(const KeyTy &tblgenKey) {\n";
  
    // Extract each parameter from the key.
    os << "      return ::llvm::hash_combine(";
    llvm::interleaveComma(
        llvm::seq<unsigned>(0, params.size()), os,
        [&](unsigned it) { os << "std::get<" << it << ">(tblgenKey)"; });
    os << ");\n    }\n";
  
    // * Emit the construct method.
  
    // If user wants to build the storage constructor themselves, declare it
    // here and then they can write the definition elsewhere.
    if (def.hasStorageCustomConstructor()) {
      os << llvm::formatv("    static {0} *construct(::mlir::{1}StorageAllocator "
                          "&allocator, const KeyTy &tblgenKey);\n",
                          def.getStorageClassName(), valueType);
  
      // Otherwise, generate one.
    } else {
      // First, unbox the parameters.
      os << formatv(defStorageClassConstructorBeginStr, def.getStorageClassName(),
                    valueType);
      for (unsigned i = 0, e = params.size(); i < e; ++i) {
        os << formatv("      auto {0} = std::get<{1}>(tblgenKey);\n",
                      params[i].getName(), i);
      }
  
      // Second, reassign the parameter variables with allocation code, if it's
      // specified.
      emitStorageParameterAllocation(def, os);
  
      // Last, return an allocated copy.
      auto parameterNames = llvm::map_range(
          params, [](const auto &param) { return param.getName(); });
      os << formatv(defStorageClassConstructorEndStr, def.getStorageClassName(),
                    llvm::join(parameterNames, ", "));
    }
  
    // * Emit the parameters as storage class members.
    for (const AttrOrTypeParameter &parameter : params) {
      // Attribute value types are not stored as fields in the storage.
      if (!isa<AttributeSelfTypeParameter>(parameter))
        os << "      " << parameter.getCppType() << " " << parameter.getName()
           << ";\n";
    }
    os << "  };\n";
  
    os << "} // namespace " << def.getStorageNamespace() << "\n";
  }
  
  void DefGenerator::emitParsePrint(const AttrOrTypeDef &def) {
    // Emit the printer code, if specified.
    if (Optional<StringRef> printerCode = def.getPrinterCode()) {
      // Both the mnenomic and printerCode must be defined (for parity with
      // parserCode).
      os << "void " << def.getCppClassName()
         << "::print(::mlir::DialectAsmPrinter &printer) const {\n";
      if (printerCode->empty()) {
        // If no code specified, emit error.
        PrintFatalError(def.getLoc(),
                        def.getName() +
                            ": printer (if specified) must have non-empty code");
      }
      FmtContext fmtCtxt = FmtContext().addSubst("_printer", "printer");
      os << tgfmt(*printerCode, &fmtCtxt) << "\n}\n";
    }
  
    // Emit the parser code, if specified.
    if (Optional<StringRef> parserCode = def.getParserCode()) {
      FmtContext fmtCtxt;
      fmtCtxt.addSubst("_parser", "parser").addSubst("_ctxt", "context");
  
      // The mnenomic must be defined so the dispatcher knows how to dispatch.
      os << llvm::formatv("::mlir::{0} {1}::parse(::mlir::MLIRContext *context, "
                          "::mlir::DialectAsmParser &parser",
                          valueType, def.getCppClassName());
      if (isAttrGenerator) {
        // Attributes also accept a type parameter instead of a context.
        os << ", ::mlir::Type type";
        fmtCtxt.addSubst("_type", "type");
      }
      os << ") {\n";
  
      if (parserCode->empty()) {
        PrintFatalError(def.getLoc(),
                        def.getName() +
                            ": parser (if specified) must have non-empty code");
      }
      os << tgfmt(*parserCode, &fmtCtxt) << "\n}\n";
    }
  }
  
  /// Replace all instances of 'from' to 'to' in `str` and return the new string.
  static std::string replaceInStr(std::string str, StringRef from, StringRef to) {
    size_t pos = 0;
    while ((pos = str.find(from.data(), pos, from.size())) != std::string::npos)
      str.replace(pos, from.size(), to.data(), to.size());
    return str;
  }
  
  /// Emit the builders for the given def.
  static void emitBuilderDefs(const AttrOrTypeDef &def, raw_ostream &os,
                              ArrayRef<AttrOrTypeParameter> params) {
    bool genCheckedMethods = def.genVerifyDecl();
    StringRef className = def.getCppClassName();
    if (!def.skipDefaultBuilders()) {
      os << llvm::formatv(
          "{0} {0}::get(::mlir::MLIRContext *context{1}) {{\n"
          "  return Base::get(context{2});\n}\n",
          className,
          ParamCommaFormatter(ParamCommaFormatter::EmitFormat::TypeNamePairs,
                              params),
          ParamCommaFormatter(ParamCommaFormatter::EmitFormat::JustParams,
                              params));
      if (genCheckedMethods) {
        os << llvm::formatv(
            "{0} {0}::getChecked("
            "llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, "
            "::mlir::MLIRContext *context{1}) {{\n"
            "  return Base::getChecked(emitError, context{2});\n}\n",
            className,
            ParamCommaFormatter(ParamCommaFormatter::EmitFormat::TypeNamePairs,
                                params),
            ParamCommaFormatter(ParamCommaFormatter::EmitFormat::JustParams,
                                params));
      }
    }
  
    auto builderFmtCtx =
        FmtContext().addSubst("_ctxt", "context").addSubst("_get", "Base::get");
    auto inferredCtxBuilderFmtCtx = FmtContext().addSubst("_get", "Base::get");
    auto checkedBuilderFmtCtx = FmtContext().addSubst("_ctxt", "context");
  
    // Generate the builders specified by the user.
    for (const AttrOrTypeBuilder &builder : def.getBuilders()) {
      Optional<StringRef> body = builder.getBody();
      if (!body)
        continue;
      std::string paramStr;
      llvm::raw_string_ostream paramOS(paramStr);
      llvm::interleaveComma(builder.getParameters(), paramOS,
                            [&](const AttrOrTypeBuilder::Parameter &param) {
                              // Note: AttrOrTypeBuilder parameters are guaranteed
                              // to have names.
                              paramOS << param.getCppType() << " "
                                      << *param.getName();
                            });
      paramOS.flush();
  
      // Emit the `get` variant of the builder.
      os << llvm::formatv("{0} {0}::get(", className);
      if (!builder.hasInferredContextParameter()) {
        os << "::mlir::MLIRContext *context";
        if (!paramStr.empty())
          os << ", ";
        os << llvm::formatv("{0}) {{\n  {1};\n}\n", paramStr,
                            tgfmt(*body, &builderFmtCtx).str());
      } else {
        os << llvm::formatv("{0}) {{\n  {1};\n}\n", paramStr,
                            tgfmt(*body, &inferredCtxBuilderFmtCtx).str());
      }
  
      // Emit the `getChecked` variant of the builder.
      if (genCheckedMethods) {
        os << llvm::formatv("{0} "
                            "{0}::getChecked(llvm::function_ref<::mlir::"
                            "InFlightDiagnostic()> emitErrorFn",
                            className);
        std::string checkedBody =
            replaceInStr(body->str(), "$_get(", "Base::getChecked(emitErrorFn, ");
        if (!builder.hasInferredContextParameter()) {
          os << ", ::mlir::MLIRContext *context";
          checkedBody = tgfmt(checkedBody, &checkedBuilderFmtCtx).str();
        }
        if (!paramStr.empty())
          os << ", ";
        os << llvm::formatv("{0}) {{\n  {1};\n}\n", paramStr, checkedBody);
      }
    }
  }
  
  /// Print all the def-specific definition code.
  void DefGenerator::emitDefDef(const AttrOrTypeDef &def) {
    NamespaceEmitter ns(os, def.getDialect());
  
    SmallVector<AttrOrTypeParameter, 4> parameters;
    def.getParameters(parameters);
    if (!parameters.empty()) {
      // Emit the storage class, if requested and necessary.
      if (def.genStorageClass())
        emitStorageClass(def);
  
      // Emit the builders for this def.
      emitBuilderDefs(def, os, parameters);
  
      // Generate accessor definitions only if we also generate the storage class.
      // Otherwise, let the user define the exact accessor definition.
      if (def.genAccessors() && def.genStorageClass()) {
        for (const AttrOrTypeParameter &param : parameters) {
          SmallString<32> paramStorageName;
          if (isa<AttributeSelfTypeParameter>(param)) {
            Twine("getType().cast<" + param.getCppType() + ">()")
                .toVector(paramStorageName);
          } else {
            paramStorageName = param.getName();
          }
  
          SmallString<16> name = param.getName();
          name[0] = llvm::toUpper(name[0]);
          os << formatv("{0} {3}::get{1}() const {{ return getImpl()->{2}; }\n",
                        param.getCppAccessorType(), name, paramStorageName,
                        def.getCppClassName());
        }
      }
    }
  
    // If mnemonic is specified maybe print definitions for the parser and printer
    // code, if they're specified.
    if (def.getMnemonic())
      emitParsePrint(def);
  }
  
  /// Emit the dialect printer/parser dispatcher. User's code should call these
  /// functions from their dialect's print/parse methods.
  void DefGenerator::emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs) {
    if (llvm::none_of(defs, [](const AttrOrTypeDef &def) {
          return def.getMnemonic().hasValue();
        })) {
      return;
    }
  
    // The parser dispatch is just a list of if-elses, matching on the mnemonic
    // and calling the def's parse function.
    os << llvm::formatv(defParserDispatchStartStr, valueType,
                        isAttrGenerator ? ", ::mlir::Type type" : "");
    for (const AttrOrTypeDef &def : defs) {
      if (def.getMnemonic()) {
        os << formatv("  if (mnemonic == {0}::{1}::getMnemonic()) { \n"
                      "    value = {0}::{1}::",
                      def.getDialect().getCppNamespace(), def.getCppClassName());
  
        // If the def has no parameters and no parser code, just invoke a normal
        // `get`.
        if (def.getNumParameters() == 0 && !def.getParserCode()) {
          os << "get(context);\n    return ::mlir::success(!!value);\n  }\n";
          continue;
        }
  
        os << "parse(context, parser" << (isAttrGenerator ? ", type" : "")
           << ");\n    return ::mlir::success(!!value);\n  }\n";
      }
    }
    os << "  return {};\n";
    os << "}\n\n";
  
    // The printer dispatch uses llvm::TypeSwitch to find and call the correct
    // printer.
    os << llvm::formatv(defPrinterDispatchStartStr, valueType);
    for (const AttrOrTypeDef &def : defs) {
      Optional<StringRef> mnemonic = def.getMnemonic();
      if (!mnemonic)
        continue;
  
      StringRef cppNamespace = def.getDialect().getCppNamespace();
      StringRef cppClassName = def.getCppClassName();
      os << formatv("    .Case<{0}::{1}>([&]({0}::{1} t) {{\n      ",
                    cppNamespace, cppClassName);
  
      // If the def has no parameters and no printer, just print the mnemonic.
      if (def.getNumParameters() == 0 && !def.getPrinterCode()) {
        os << formatv("printer << {0}::{1}::getMnemonic();", cppNamespace,
                      cppClassName);
      } else {
        os << "t.print(printer);";
      }
      os << "\n      return ::mlir::success();\n    })\n";
    }
    os << llvm::formatv(
        "    .Default([](::mlir::{0}) {{ return ::mlir::failure(); });\n}\n\n",
        valueType);
  }
  
  bool DefGenerator::emitDefs(StringRef selectedDialect) {
    emitSourceFileHeader((defTypePrefix + "Def Definitions").str(), os);
  
    SmallVector<AttrOrTypeDef, 16> defs;
    collectAllDefs(selectedDialect, defRecords, defs);
    if (defs.empty())
      return false;
    emitTypeDefList(defs);
  
    IfDefScope scope("GET_" + defTypePrefix.upper() + "DEF_CLASSES", os);
    emitParsePrintDispatch(defs);
    for (const AttrOrTypeDef &def : defs) {
      emitDefDef(def);
      // Emit the TypeID explicit specializations to have a single symbol def.
      if (!def.getDialect().getCppNamespace().empty())
        os << "DEFINE_EXPLICIT_TYPE_ID(" << def.getDialect().getCppNamespace()
           << "::" << def.getCppClassName() << ")\n";
    }
  
    return false;
  }
  
  //===----------------------------------------------------------------------===//
  // GEN: Registration hooks
  //===----------------------------------------------------------------------===//
  
  //===----------------------------------------------------------------------===//
  // AttrDef
  
  static llvm::cl::OptionCategory attrdefGenCat("Options for -gen-attrdef-*");
  static llvm::cl::opt<std::string>
      attrDialect("attrdefs-dialect",
                  llvm::cl::desc("Generate attributes for this dialect"),
                  llvm::cl::cat(attrdefGenCat), llvm::cl::CommaSeparated);
  
  static mlir::GenRegistration
      genAttrDefs("gen-attrdef-defs", "Generate AttrDef definitions",
                  [](const llvm::RecordKeeper &records, raw_ostream &os) {
                    AttrDefGenerator generator(records, os);
                    return generator.emitDefs(attrDialect);
                  });
  static mlir::GenRegistration
      genAttrDecls("gen-attrdef-decls", "Generate AttrDef declarations",
                   [](const llvm::RecordKeeper &records, raw_ostream &os) {
                     AttrDefGenerator generator(records, os);
                     return generator.emitDecls(attrDialect);
                   });
  
  //===----------------------------------------------------------------------===//
  // TypeDef
  
  static llvm::cl::OptionCategory typedefGenCat("Options for -gen-typedef-*");
  static llvm::cl::opt<std::string>
      typeDialect("typedefs-dialect",
                  llvm::cl::desc("Generate types for this dialect"),
                  llvm::cl::cat(typedefGenCat), llvm::cl::CommaSeparated);
  
  static mlir::GenRegistration
      genTypeDefs("gen-typedef-defs", "Generate TypeDef definitions",
                  [](const llvm::RecordKeeper &records, raw_ostream &os) {
                    TypeDefGenerator generator(records, os);
                    return generator.emitDefs(typeDialect);
                  });
  static mlir::GenRegistration
      genTypeDecls("gen-typedef-decls", "Generate TypeDef declarations",
                   [](const llvm::RecordKeeper &records, raw_ostream &os) {
                     TypeDefGenerator generator(records, os);
                     return generator.emitDecls(typeDialect);
                   });
  