diff --git a/mlir/lib/TableGen/AttrOrTypeDef.cpp b/mlir/lib/TableGen/AttrOrTypeDef.cpp --- a/mlir/lib/TableGen/AttrOrTypeDef.cpp +++ b/mlir/lib/TableGen/AttrOrTypeDef.cpp @@ -25,10 +25,66 @@ return def->getValueAsBit("hasInferredContextParam"); } +enum class K3 : int { False = -1, Unsure = 0, True = 1 }; + +// TODO(wrengr): make this a method instead? +/// Compares the builder's parameters against a list of expected +/// parameters. Returns `True` if the builder's parameters definitely +/// match; returns `False` if the parameters definitely do not match; +/// and returns `Unsure` if we cannot be certain either way. This third +/// case arises from the problem of using string-equality to compare C++ +/// types, since multiple distinct strings could resolve to the same type. +static K3 builderHasParameters(AttrOrTypeBuilder builder, + ArrayRef params) { + unsigned numParams = params.size(); + auto builderParams = builder.getParameters(); + if (builderParams.size() != numParams) + return K3::False; + for (unsigned i = 0; i < numParams; ++i) + if (builderParams[i].getCppType() != params[i]) + return K3::Unsure; + return K3::True; +} + //===----------------------------------------------------------------------===// // AttrOrTypeDef //===----------------------------------------------------------------------===// +// TODO(wrengr): make this a method instead? +/// Returns true if this `AttrOrTypeDef` has a builder which matches +/// the default-builder's prototype, regardless of how that builder +/// is implemented. +static void checkForBuilderWithDefaultPrototype(const AttrOrTypeDef &def) { + // Default builders always have default parameters. + if (!def.skipDefaultBuilders()) + return; + // Construct the default parameters. + SmallVector defParams; + defParams.emplace_back("::mlir::MLIRContext *"); + for (const AttrOrTypeParameter ¶m : def.getParameters()) + defParams.emplace_back(param.getCppType()); + // Compare each custom builder to the default parameters. + bool isUnsure = false; + for (auto &builder : def.getBuilders()) { + switch (builderHasParameters(builder, defParams)) { + case K3::True: + // This one definitely matches, so we're done. + return; + case K3::Unsure: + // This one might match, so make a note. + isUnsure = true; + continue; + case K3::False: + // This one definitely doesn't match, so keep going. + continue; + } + } + if (isUnsure) + PrintWarning(def.getLoc(), + "using 'assemblyFormat' with 'skipDefaultBuilders=1' may " + "result in C++ compilation errors"); +} + AttrOrTypeDef::AttrOrTypeDef(const llvm::Record *def) : def(def) { // Populate the builders. auto *builderList = @@ -83,12 +139,8 @@ } // Assembly format parser requires builders with the same prototype // as the default-builders. - // TODO: attempt to detect when a custom builder matches the prototype. - if (hasDeclarativeFormat && skipDefaultBuilders()) { - PrintWarning(getLoc(), - "using 'assemblyFormat' with 'skipDefaultBuilders=1' may " - "result in C++ compilation errors"); - } + if (hasDeclarativeFormat) + checkForBuilderWithDefaultPrototype(*this); // Assembly format printer requires accessors to be generated. if (hasDeclarativeFormat && !genAccessors()) { PrintFatalError(getLoc(),