431 lines · cpp
1//===- Class.cpp - Helper classes for Op C++ code emission --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/TableGen/Class.h"10#include "llvm/ADT/Twine.h"11#include "llvm/Support/Debug.h"12 13using namespace mlir;14using namespace mlir::tblgen;15 16/// Returns space to be emitted after the given C++ `type`. return "" if the17/// ends with '&' or '*', or is empty, else returns " ".18static StringRef getSpaceAfterType(StringRef type) {19 return (type.empty() || type.ends_with("&") || type.ends_with("*")) ? ""20 : " ";21}22 23//===----------------------------------------------------------------------===//24// MethodParameter definitions25//===----------------------------------------------------------------------===//26 27void MethodParameter::writeDeclTo(raw_indented_ostream &os) const {28 if (optional)29 os << "/*optional*/";30 os << type << getSpaceAfterType(type) << name;31 if (hasDefaultValue())32 os << " = " << defaultValue;33}34 35void MethodParameter::writeDefTo(raw_indented_ostream &os) const {36 if (optional)37 os << "/*optional*/";38 os << type << getSpaceAfterType(type) << name;39}40 41//===----------------------------------------------------------------------===//42// MethodParameters definitions43//===----------------------------------------------------------------------===//44 45void MethodParameters::writeDeclTo(raw_indented_ostream &os) const {46 llvm::interleaveComma(parameters, os,47 [&os](auto ¶m) { param.writeDeclTo(os); });48}49void MethodParameters::writeDefTo(raw_indented_ostream &os) const {50 llvm::interleaveComma(parameters, os,51 [&os](auto ¶m) { param.writeDefTo(os); });52}53 54bool MethodParameters::subsumes(const MethodParameters &other) const {55 // These parameters do not subsume the others if there are fewer parameters56 // or their types do not match.57 if (parameters.size() < other.parameters.size())58 return false;59 if (!std::equal(60 other.parameters.begin(), other.parameters.end(), parameters.begin(),61 [](auto &lhs, auto &rhs) { return lhs.getType() == rhs.getType(); }))62 return false;63 64 // If all the common parameters have the same type, we can elide the other65 // method if this method has the same number of parameters as other or if the66 // first paramater after the common parameters has a default value (and, as67 // required by C++, subsequent parameters will have default values too).68 return parameters.size() == other.parameters.size() ||69 parameters[other.parameters.size()].hasDefaultValue();70}71 72//===----------------------------------------------------------------------===//73// MethodSignature definitions74//===----------------------------------------------------------------------===//75 76bool MethodSignature::makesRedundant(const MethodSignature &other) const {77 return methodName == other.methodName &&78 parameters.subsumes(other.parameters);79}80 81void MethodSignature::writeDeclTo(raw_indented_ostream &os) const {82 os << returnType << getSpaceAfterType(returnType) << methodName << "(";83 parameters.writeDeclTo(os);84 os << ")";85}86 87void MethodSignature::writeDefTo(raw_indented_ostream &os,88 StringRef namePrefix) const {89 os << returnType << getSpaceAfterType(returnType) << namePrefix90 << (namePrefix.empty() ? "" : "::") << methodName << "(";91 parameters.writeDefTo(os);92 os << ")";93}94 95void MethodSignature::writeTemplateParamsTo(96 mlir::raw_indented_ostream &os) const {97 if (templateParams.empty())98 return;99 100 os << "template <";101 llvm::interleaveComma(templateParams, os,102 [&](StringRef param) { os << "typename " << param; });103 os << ">\n";104}105 106//===----------------------------------------------------------------------===//107// MethodBody definitions108//===----------------------------------------------------------------------===//109 110MethodBody::MethodBody(bool declOnly)111 : declOnly(declOnly), stringOs(body), os(stringOs) {}112 113void MethodBody::writeTo(raw_indented_ostream &os) const {114 auto bodyRef = StringRef(body).ltrim('\n');115 os << bodyRef;116 if (bodyRef.empty())117 return;118 if (bodyRef.back() != '\n')119 os << "\n";120}121 122//===----------------------------------------------------------------------===//123// Method definitions124//===----------------------------------------------------------------------===//125 126void Method::writeDeclTo(raw_indented_ostream &os) const {127 methodSignature.writeTemplateParamsTo(os);128 if (deprecationMessage) {129 os << "[[deprecated(\"";130 os.write_escaped(*deprecationMessage);131 os << "\")]]\n";132 }133 if (isStatic())134 os << "static ";135 if (properties & ConstexprValue)136 os << "constexpr ";137 methodSignature.writeDeclTo(os);138 if (isConst())139 os << " const";140 if (!isInline()) {141 os << ";\n";142 return;143 }144 os << " {\n";145 methodBody.writeTo(os);146 os << "}\n\n";147}148 149void Method::writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const {150 // The method has no definition to write if it is declaration only or inline.151 if (properties & Declaration || isInline())152 return;153 154 methodSignature.writeDefTo(os, namePrefix);155 if (isConst())156 os << " const";157 os << " {\n";158 methodBody.writeTo(os);159 os << "}\n\n";160}161 162bool Method::methodPropertiesAreCompatible(Properties properties) {163 const bool isStatic = (properties & Method::Static);164 const bool isConstructor = (properties & Method::Constructor);165 // const bool isPrivate = (properties & Method::Private);166 const bool isDeclaration = (properties & Method::Declaration);167 const bool isInline = (properties & Method::Inline);168 const bool isConstexprValue = (properties & Method::ConstexprValue);169 const bool isConst = (properties & Method::Const);170 171 // Note: assert to immediately fail and thus simplify debugging.172 if (isStatic && isConstructor) {173 assert(false && "constructor cannot be static");174 return false;175 }176 if (isConstructor && isConst) { // albeit constexpr is fine177 assert(false && "constructor cannot be const");178 return false;179 }180 if (isDeclaration && isInline) {181 assert(false &&182 "declaration implies no definition and thus cannot be inline");183 return false;184 }185 if (isDeclaration && isConstexprValue) {186 assert(false &&187 "declaration implies no definition and thus cannot be constexpr");188 return false;189 }190 191 return true;192}193 194//===----------------------------------------------------------------------===//195// Constructor definitions196//===----------------------------------------------------------------------===//197 198void Constructor::writeDeclTo(raw_indented_ostream &os) const {199 methodSignature.writeTemplateParamsTo(os);200 if (properties & ConstexprValue)201 os << "constexpr ";202 methodSignature.writeDeclTo(os);203 if (!isInline()) {204 os << ";\n\n";205 return;206 }207 os << ' ';208 if (!initializers.empty())209 os << ": ";210 llvm::interleaveComma(initializers, os,211 [&](auto &initializer) { initializer.writeTo(os); });212 if (!initializers.empty())213 os << ' ';214 os << "{";215 methodBody.writeTo(os);216 os << "}\n\n";217}218 219void Constructor::writeDefTo(raw_indented_ostream &os,220 StringRef namePrefix) const {221 // The method has no definition to write if it is declaration only or inline.222 if (properties & Declaration || isInline())223 return;224 225 methodSignature.writeDefTo(os, namePrefix);226 os << ' ';227 if (!initializers.empty())228 os << ": ";229 llvm::interleaveComma(initializers, os,230 [&](auto &initializer) { initializer.writeTo(os); });231 if (!initializers.empty())232 os << ' ';233 os << "{";234 methodBody.writeTo(os);235 os << "}\n\n";236}237 238void Constructor::MemberInitializer::writeTo(raw_indented_ostream &os) const {239 os << name << '(' << value << ')';240}241 242//===----------------------------------------------------------------------===//243// Visibility definitions244//===----------------------------------------------------------------------===//245 246namespace mlir {247namespace tblgen {248raw_ostream &operator<<(raw_ostream &os, Visibility visibility) {249 switch (visibility) {250 case Visibility::Public:251 return os << "public";252 case Visibility::Protected:253 return os << "protected";254 case Visibility::Private:255 return os << "private";256 }257 return os;258}259} // namespace tblgen260} // namespace mlir261 262//===----------------------------------------------------------------------===//263// ParentClass definitions264//===----------------------------------------------------------------------===//265 266void ParentClass::writeTo(raw_indented_ostream &os) const {267 os << visibility << ' ' << name;268 if (!templateParams.empty()) {269 auto scope = os.scope("<", ">", /*indent=*/false);270 llvm::interleaveComma(templateParams, os,271 [&](auto ¶m) { os << param; });272 }273}274 275//===----------------------------------------------------------------------===//276// UsingDeclaration definitions277//===----------------------------------------------------------------------===//278 279void UsingDeclaration::writeDeclTo(raw_indented_ostream &os) const {280 if (!templateParams.empty()) {281 os << "template <";282 llvm::interleaveComma(templateParams, os, [&](StringRef paramName) {283 os << "typename " << paramName;284 });285 os << ">\n";286 }287 os << "using " << name;288 if (!value.empty())289 os << " = " << value;290 os << ";\n";291}292 293//===----------------------------------------------------------------------===//294// Field definitions295//===----------------------------------------------------------------------===//296 297void Field::writeDeclTo(raw_indented_ostream &os) const {298 os << type << ' ' << name << ";\n";299}300 301//===----------------------------------------------------------------------===//302// VisibilityDeclaration definitions303//===----------------------------------------------------------------------===//304 305void VisibilityDeclaration::writeDeclTo(raw_indented_ostream &os) const {306 os.unindent();307 os << visibility << ":\n";308 os.indent();309}310 311//===----------------------------------------------------------------------===//312// ExtraClassDeclaration definitions313//===----------------------------------------------------------------------===//314 315void ExtraClassDeclaration::writeDeclTo(raw_indented_ostream &os) const {316 os.printReindented(extraClassDeclaration);317}318 319void ExtraClassDeclaration::writeDefTo(raw_indented_ostream &os,320 StringRef namePrefix) const {321 os.printReindented(extraClassDefinition);322}323 324//===----------------------------------------------------------------------===//325// Class definitions326//===----------------------------------------------------------------------===//327 328ParentClass &Class::addParent(ParentClass parent) {329 parents.push_back(std::move(parent));330 return parents.back();331}332 333void Class::writeDeclTo(raw_indented_ostream &os) const {334 if (!templateParams.empty()) {335 os << "template <";336 llvm::interleaveComma(templateParams, os,337 [&](StringRef param) { os << "typename " << param; });338 os << ">\n";339 }340 341 // Declare the class.342 os << (isStruct ? "struct" : "class") << ' ' << className << ' ';343 344 // Declare the parent classes, if any.345 if (!parents.empty()) {346 os << ": ";347 llvm::interleaveComma(parents, os,348 [&](auto &parent) { parent.writeTo(os); });349 os << ' ';350 }351 auto classScope = os.scope("{\n", "};\n", /*indent=*/true);352 353 // Print all the class declarations.354 for (auto &decl : declarations)355 decl->writeDeclTo(os);356}357 358void Class::writeDefTo(raw_indented_ostream &os) const {359 // Print all the definitions.360 for (auto &decl : declarations)361 decl->writeDefTo(os, className);362}363 364void Class::finalize() {365 // Sort the methods by public and private. Remove them from the pending list366 // of methods.367 SmallVector<std::unique_ptr<Method>> publicMethods, privateMethods;368 for (auto &method : methods) {369 if (method->isPrivate())370 privateMethods.push_back(std::move(method));371 else372 publicMethods.push_back(std::move(method));373 }374 methods.clear();375 376 // If the last visibility declaration wasn't `public`, add one that is. Then,377 // declare the public methods.378 if (!publicMethods.empty() && getLastVisibilityDecl() != Visibility::Public)379 declare<VisibilityDeclaration>(Visibility::Public);380 for (auto &method : publicMethods)381 declarations.push_back(std::move(method));382 383 // If the last visibility declaration wasn't `private`, add one that is. Then,384 // declare the private methods.385 if (!privateMethods.empty() && getLastVisibilityDecl() != Visibility::Private)386 declare<VisibilityDeclaration>(Visibility::Private);387 for (auto &method : privateMethods)388 declarations.push_back(std::move(method));389 390 // All fields added to the pending list are private and declared at the bottom391 // of the class. If the last visibility declaration wasn't `private`, add one392 // that is, then declare the fields.393 if (!fields.empty() && getLastVisibilityDecl() != Visibility::Private)394 declare<VisibilityDeclaration>(Visibility::Private);395 for (auto &field : fields)396 declare<Field>(std::move(field));397 fields.clear();398}399 400Visibility Class::getLastVisibilityDecl() const {401 auto reverseDecls = llvm::reverse(declarations);402 auto it = llvm::find_if(reverseDecls, llvm::IsaPred<VisibilityDeclaration>);403 return it == reverseDecls.end()404 ? (isStruct ? Visibility::Public : Visibility::Private)405 : cast<VisibilityDeclaration>(**it).getVisibility();406}407 408Method *insertAndPruneMethods(std::vector<std::unique_ptr<Method>> &methods,409 std::unique_ptr<Method> newMethod) {410 if (llvm::any_of(methods, [&](auto &method) {411 return method->makesRedundant(*newMethod);412 }))413 return nullptr;414 415 llvm::erase_if(methods, [&](auto &method) {416 return newMethod->makesRedundant(*method);417 });418 methods.push_back(std::move(newMethod));419 return methods.back().get();420}421 422Method *Class::addMethodAndPrune(Method &&newMethod) {423 return insertAndPruneMethods(methods,424 std::make_unique<Method>(std::move(newMethod)));425}426 427Constructor *Class::addConstructorAndPrune(Constructor &&newCtor) {428 return dyn_cast_or_null<Constructor>(insertAndPruneMethods(429 methods, std::make_unique<Constructor>(std::move(newCtor))));430}431