12696a952SMogball //===- Class.cpp - Helper classes for Op C++ code emission --------------===//
22696a952SMogball //
32696a952SMogball // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42696a952SMogball // See https://llvm.org/LICENSE.txt for license information.
52696a952SMogball // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62696a952SMogball //
72696a952SMogball //===----------------------------------------------------------------------===//
82696a952SMogball 
92696a952SMogball #include "mlir/TableGen/Class.h"
102696a952SMogball #include "mlir/TableGen/Format.h"
112696a952SMogball #include "llvm/ADT/Sequence.h"
122696a952SMogball #include "llvm/ADT/Twine.h"
132696a952SMogball #include "llvm/Support/Debug.h"
142696a952SMogball 
152696a952SMogball using namespace mlir;
162696a952SMogball using namespace mlir::tblgen;
172696a952SMogball 
18ca6bd9cdSMogball /// Returns space to be emitted after the given C++ `type`. return "" if the
19ca6bd9cdSMogball /// ends with '&' or '*', or is empty, else returns " ".
getSpaceAfterType(StringRef type)202696a952SMogball static StringRef getSpaceAfterType(StringRef type) {
212696a952SMogball   return (type.empty() || type.endswith("&") || type.endswith("*")) ? "" : " ";
222696a952SMogball }
232696a952SMogball 
242696a952SMogball //===----------------------------------------------------------------------===//
252696a952SMogball // MethodParameter definitions
262696a952SMogball //===----------------------------------------------------------------------===//
272696a952SMogball 
writeDeclTo(raw_indented_ostream & os) const28ca6bd9cdSMogball void MethodParameter::writeDeclTo(raw_indented_ostream &os) const {
292696a952SMogball   if (optional)
302696a952SMogball     os << "/*optional*/";
312696a952SMogball   os << type << getSpaceAfterType(type) << name;
32ca6bd9cdSMogball   if (hasDefaultValue())
332696a952SMogball     os << " = " << defaultValue;
342696a952SMogball }
352696a952SMogball 
writeDefTo(raw_indented_ostream & os) const36ca6bd9cdSMogball void MethodParameter::writeDefTo(raw_indented_ostream &os) const {
37ca6bd9cdSMogball   if (optional)
38ca6bd9cdSMogball     os << "/*optional*/";
39ca6bd9cdSMogball   os << type << getSpaceAfterType(type) << name;
40ca6bd9cdSMogball }
41ca6bd9cdSMogball 
422696a952SMogball //===----------------------------------------------------------------------===//
432696a952SMogball // MethodParameters definitions
442696a952SMogball //===----------------------------------------------------------------------===//
452696a952SMogball 
writeDeclTo(raw_indented_ostream & os) const46ca6bd9cdSMogball void MethodParameters::writeDeclTo(raw_indented_ostream &os) const {
472696a952SMogball   llvm::interleaveComma(parameters, os,
482696a952SMogball                         [&os](auto &param) { param.writeDeclTo(os); });
492696a952SMogball }
writeDefTo(raw_indented_ostream & os) const50ca6bd9cdSMogball void MethodParameters::writeDefTo(raw_indented_ostream &os) const {
512696a952SMogball   llvm::interleaveComma(parameters, os,
522696a952SMogball                         [&os](auto &param) { param.writeDefTo(os); });
532696a952SMogball }
542696a952SMogball 
subsumes(const MethodParameters & other) const552696a952SMogball bool MethodParameters::subsumes(const MethodParameters &other) const {
562696a952SMogball   // These parameters do not subsume the others if there are fewer parameters
572696a952SMogball   // or their types do not match.
582696a952SMogball   if (parameters.size() < other.parameters.size())
592696a952SMogball     return false;
602696a952SMogball   if (!std::equal(
612696a952SMogball           other.parameters.begin(), other.parameters.end(), parameters.begin(),
622696a952SMogball           [](auto &lhs, auto &rhs) { return lhs.getType() == rhs.getType(); }))
632696a952SMogball     return false;
642696a952SMogball 
652696a952SMogball   // If all the common parameters have the same type, we can elide the other
662696a952SMogball   // method if this method has the same number of parameters as other or if the
672696a952SMogball   // first paramater after the common parameters has a default value (and, as
682696a952SMogball   // required by C++, subsequent parameters will have default values too).
692696a952SMogball   return parameters.size() == other.parameters.size() ||
702696a952SMogball          parameters[other.parameters.size()].hasDefaultValue();
712696a952SMogball }
722696a952SMogball 
732696a952SMogball //===----------------------------------------------------------------------===//
742696a952SMogball // MethodSignature definitions
752696a952SMogball //===----------------------------------------------------------------------===//
762696a952SMogball 
makesRedundant(const MethodSignature & other) const772696a952SMogball bool MethodSignature::makesRedundant(const MethodSignature &other) const {
782696a952SMogball   return methodName == other.methodName &&
792696a952SMogball          parameters.subsumes(other.parameters);
802696a952SMogball }
812696a952SMogball 
writeDeclTo(raw_indented_ostream & os) const82ca6bd9cdSMogball void MethodSignature::writeDeclTo(raw_indented_ostream &os) const {
832696a952SMogball   os << returnType << getSpaceAfterType(returnType) << methodName << "(";
842696a952SMogball   parameters.writeDeclTo(os);
852696a952SMogball   os << ")";
862696a952SMogball }
872696a952SMogball 
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const88ca6bd9cdSMogball void MethodSignature::writeDefTo(raw_indented_ostream &os,
89ca6bd9cdSMogball                                  StringRef namePrefix) const {
902696a952SMogball   os << returnType << getSpaceAfterType(returnType) << namePrefix
912696a952SMogball      << (namePrefix.empty() ? "" : "::") << methodName << "(";
922696a952SMogball   parameters.writeDefTo(os);
932696a952SMogball   os << ")";
942696a952SMogball }
952696a952SMogball 
962696a952SMogball //===----------------------------------------------------------------------===//
972696a952SMogball // MethodBody definitions
982696a952SMogball //===----------------------------------------------------------------------===//
992696a952SMogball 
MethodBody(bool declOnly)100ca6bd9cdSMogball MethodBody::MethodBody(bool declOnly)
101ca6bd9cdSMogball     : declOnly(declOnly), stringOs(body), os(stringOs) {}
1022696a952SMogball 
writeTo(raw_indented_ostream & os) const103ca6bd9cdSMogball void MethodBody::writeTo(raw_indented_ostream &os) const {
1042696a952SMogball   auto bodyRef = StringRef(body).drop_while([](char c) { return c == '\n'; });
1052696a952SMogball   os << bodyRef;
106ca6bd9cdSMogball   if (bodyRef.empty())
107ca6bd9cdSMogball     return;
108ca6bd9cdSMogball   if (bodyRef.back() != '\n')
1092696a952SMogball     os << "\n";
1102696a952SMogball }
1112696a952SMogball 
1122696a952SMogball //===----------------------------------------------------------------------===//
1132696a952SMogball // Method definitions
1142696a952SMogball //===----------------------------------------------------------------------===//
1152696a952SMogball 
writeDeclTo(raw_indented_ostream & os) const116ca6bd9cdSMogball void Method::writeDeclTo(raw_indented_ostream &os) const {
1172696a952SMogball   if (isStatic())
1182696a952SMogball     os << "static ";
119ca6bd9cdSMogball   if (properties & ConstexprValue)
1202696a952SMogball     os << "constexpr ";
1212696a952SMogball   methodSignature.writeDeclTo(os);
122ca6bd9cdSMogball   if (isConst())
123ca6bd9cdSMogball     os << " const";
1242696a952SMogball   if (!isInline()) {
125ca6bd9cdSMogball     os << ";\n";
1262696a952SMogball     return;
127ca6bd9cdSMogball   }
1282696a952SMogball   os << " {\n";
1292696a952SMogball   methodBody.writeTo(os);
130ca6bd9cdSMogball   os << "}\n\n";
131ca6bd9cdSMogball }
132ca6bd9cdSMogball 
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const133ca6bd9cdSMogball void Method::writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const {
134ca6bd9cdSMogball   // The method has no definition to write if it is declaration only or inline.
135ca6bd9cdSMogball   if (properties & Declaration || isInline())
136ca6bd9cdSMogball     return;
137ca6bd9cdSMogball 
138ca6bd9cdSMogball   methodSignature.writeDefTo(os, namePrefix);
139ca6bd9cdSMogball   if (isConst())
140ca6bd9cdSMogball     os << " const";
141ca6bd9cdSMogball   os << " {\n";
142ca6bd9cdSMogball   methodBody.writeTo(os);
143ca6bd9cdSMogball   os << "}\n\n";
1442696a952SMogball }
1452696a952SMogball 
1462696a952SMogball //===----------------------------------------------------------------------===//
1472696a952SMogball // Constructor definitions
1482696a952SMogball //===----------------------------------------------------------------------===//
1492696a952SMogball 
writeDeclTo(raw_indented_ostream & os) const150ca6bd9cdSMogball void Constructor::writeDeclTo(raw_indented_ostream &os) const {
151ca6bd9cdSMogball   if (properties & ConstexprValue)
152ca6bd9cdSMogball     os << "constexpr ";
153ca6bd9cdSMogball   methodSignature.writeDeclTo(os);
154ca6bd9cdSMogball   if (!isInline()) {
155ca6bd9cdSMogball     os << ";\n\n";
156ca6bd9cdSMogball     return;
157ca6bd9cdSMogball   }
158ca6bd9cdSMogball   os << ' ';
159ca6bd9cdSMogball   if (!initializers.empty())
160ca6bd9cdSMogball     os << ": ";
161ca6bd9cdSMogball   llvm::interleaveComma(initializers, os,
162ca6bd9cdSMogball                         [&](auto &initializer) { initializer.writeTo(os); });
163ca6bd9cdSMogball   if (!initializers.empty())
164ca6bd9cdSMogball     os << ' ';
165ca6bd9cdSMogball   os << "{";
166ca6bd9cdSMogball   methodBody.writeTo(os);
167ca6bd9cdSMogball   os << "}\n\n";
1682696a952SMogball }
1692696a952SMogball 
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const170ca6bd9cdSMogball void Constructor::writeDefTo(raw_indented_ostream &os,
171ca6bd9cdSMogball                              StringRef namePrefix) const {
172ca6bd9cdSMogball   // The method has no definition to write if it is declaration only or inline.
173ca6bd9cdSMogball   if (properties & Declaration || isInline())
1742696a952SMogball     return;
1752696a952SMogball 
1762696a952SMogball   methodSignature.writeDefTo(os, namePrefix);
177ca6bd9cdSMogball   os << ' ';
178ca6bd9cdSMogball   if (!initializers.empty())
179ca6bd9cdSMogball     os << ": ";
180ca6bd9cdSMogball   llvm::interleaveComma(initializers, os,
181ca6bd9cdSMogball                         [&](auto &initializer) { initializer.writeTo(os); });
182ca6bd9cdSMogball   if (!initializers.empty())
183ca6bd9cdSMogball     os << ' ';
184ca6bd9cdSMogball   os << "{";
1852696a952SMogball   methodBody.writeTo(os);
186ca6bd9cdSMogball   os << "}\n\n";
187ca6bd9cdSMogball }
188ca6bd9cdSMogball 
writeTo(raw_indented_ostream & os) const189ca6bd9cdSMogball void Constructor::MemberInitializer::writeTo(raw_indented_ostream &os) const {
190ca6bd9cdSMogball   os << name << '(' << value << ')';
191ca6bd9cdSMogball }
192ca6bd9cdSMogball 
193ca6bd9cdSMogball //===----------------------------------------------------------------------===//
194ca6bd9cdSMogball // Visibility definitions
195ca6bd9cdSMogball //===----------------------------------------------------------------------===//
196ca6bd9cdSMogball 
197ca6bd9cdSMogball namespace mlir {
198ca6bd9cdSMogball namespace tblgen {
operator <<(raw_ostream & os,Visibility visibility)199ca6bd9cdSMogball raw_ostream &operator<<(raw_ostream &os, Visibility visibility) {
200ca6bd9cdSMogball   switch (visibility) {
201ca6bd9cdSMogball   case Visibility::Public:
202ca6bd9cdSMogball     return os << "public";
203ca6bd9cdSMogball   case Visibility::Protected:
204ca6bd9cdSMogball     return os << "protected";
205ca6bd9cdSMogball   case Visibility::Private:
206ca6bd9cdSMogball     return os << "private";
207ca6bd9cdSMogball   }
208ca6bd9cdSMogball   return os;
209ca6bd9cdSMogball }
210be0a7e9fSMehdi Amini } // namespace tblgen
211be0a7e9fSMehdi Amini } // namespace mlir
212ca6bd9cdSMogball 
213ca6bd9cdSMogball //===----------------------------------------------------------------------===//
214ca6bd9cdSMogball // ParentClass definitions
215ca6bd9cdSMogball //===----------------------------------------------------------------------===//
216ca6bd9cdSMogball 
writeTo(raw_indented_ostream & os) const217ca6bd9cdSMogball void ParentClass::writeTo(raw_indented_ostream &os) const {
218ca6bd9cdSMogball   os << visibility << ' ' << name;
219ca6bd9cdSMogball   if (!templateParams.empty()) {
220ca6bd9cdSMogball     auto scope = os.scope("<", ">", /*indent=*/false);
221ca6bd9cdSMogball     llvm::interleaveComma(templateParams, os,
222ca6bd9cdSMogball                           [&](auto &param) { os << param; });
223ca6bd9cdSMogball   }
224ca6bd9cdSMogball }
225ca6bd9cdSMogball 
226ca6bd9cdSMogball //===----------------------------------------------------------------------===//
227ca6bd9cdSMogball // UsingDeclaration definitions
228ca6bd9cdSMogball //===----------------------------------------------------------------------===//
229ca6bd9cdSMogball 
writeDeclTo(raw_indented_ostream & os) const230ca6bd9cdSMogball void UsingDeclaration::writeDeclTo(raw_indented_ostream &os) const {
231ca6bd9cdSMogball   os << "using " << name;
232ca6bd9cdSMogball   if (!value.empty())
233ca6bd9cdSMogball     os << " = " << value;
234ca6bd9cdSMogball   os << ";\n";
235ca6bd9cdSMogball }
236ca6bd9cdSMogball 
237ca6bd9cdSMogball //===----------------------------------------------------------------------===//
238ca6bd9cdSMogball // Field definitions
239ca6bd9cdSMogball //===----------------------------------------------------------------------===//
240ca6bd9cdSMogball 
writeDeclTo(raw_indented_ostream & os) const241ca6bd9cdSMogball void Field::writeDeclTo(raw_indented_ostream &os) const {
242ca6bd9cdSMogball   os << type << ' ' << name << ";\n";
243ca6bd9cdSMogball }
244ca6bd9cdSMogball 
245ca6bd9cdSMogball //===----------------------------------------------------------------------===//
246ca6bd9cdSMogball // VisibilityDeclaration definitions
247ca6bd9cdSMogball //===----------------------------------------------------------------------===//
248ca6bd9cdSMogball 
writeDeclTo(raw_indented_ostream & os) const249ca6bd9cdSMogball void VisibilityDeclaration::writeDeclTo(raw_indented_ostream &os) const {
250ca6bd9cdSMogball   os.unindent();
251ca6bd9cdSMogball   os << visibility << ":\n";
252ca6bd9cdSMogball   os.indent();
253ca6bd9cdSMogball }
254ca6bd9cdSMogball 
255ca6bd9cdSMogball //===----------------------------------------------------------------------===//
256ca6bd9cdSMogball // ExtraClassDeclaration definitions
257ca6bd9cdSMogball //===----------------------------------------------------------------------===//
258ca6bd9cdSMogball 
writeDeclTo(raw_indented_ostream & os) const259ca6bd9cdSMogball void ExtraClassDeclaration::writeDeclTo(raw_indented_ostream &os) const {
260ca6bd9cdSMogball   os.printReindented(extraClassDeclaration);
2612696a952SMogball }
2622696a952SMogball 
writeDefTo(raw_indented_ostream & os,StringRef namePrefix) const263*b0774e5fSMogball void ExtraClassDeclaration::writeDefTo(raw_indented_ostream &os,
264*b0774e5fSMogball                                        StringRef namePrefix) const {
265*b0774e5fSMogball   os.printReindented(extraClassDefinition);
266*b0774e5fSMogball }
267*b0774e5fSMogball 
2682696a952SMogball //===----------------------------------------------------------------------===//
2692696a952SMogball // Class definitions
2702696a952SMogball //===----------------------------------------------------------------------===//
2712696a952SMogball 
addParent(ParentClass parent)272ca6bd9cdSMogball ParentClass &Class::addParent(ParentClass parent) {
273ca6bd9cdSMogball   parents.push_back(std::move(parent));
274ca6bd9cdSMogball   return parents.back();
2752696a952SMogball }
2762696a952SMogball 
writeDeclTo(raw_indented_ostream & os) const277ca6bd9cdSMogball void Class::writeDeclTo(raw_indented_ostream &os) const {
278ca6bd9cdSMogball   // Declare the class.
279ca6bd9cdSMogball   os << (isStruct ? "struct" : "class") << ' ' << className << ' ';
2802696a952SMogball 
281ca6bd9cdSMogball   // Declare the parent classes, if any.
282ca6bd9cdSMogball   if (!parents.empty()) {
283ca6bd9cdSMogball     os << ": ";
284ca6bd9cdSMogball     llvm::interleaveComma(parents, os,
285ca6bd9cdSMogball                           [&](auto &parent) { parent.writeTo(os); });
286ca6bd9cdSMogball     os << ' ';
2872696a952SMogball   }
288ca6bd9cdSMogball   auto classScope = os.scope("{\n", "};\n", /*indent=*/true);
289ca6bd9cdSMogball 
290ca6bd9cdSMogball   // Print all the class declarations.
291ca6bd9cdSMogball   for (auto &decl : declarations)
292ca6bd9cdSMogball     decl->writeDeclTo(os);
293ca6bd9cdSMogball }
294ca6bd9cdSMogball 
writeDefTo(raw_indented_ostream & os) const295ca6bd9cdSMogball void Class::writeDefTo(raw_indented_ostream &os) const {
296ca6bd9cdSMogball   // Print all the definitions.
297ca6bd9cdSMogball   for (auto &decl : declarations)
298ca6bd9cdSMogball     decl->writeDefTo(os, className);
299ca6bd9cdSMogball }
300ca6bd9cdSMogball 
finalize()301ca6bd9cdSMogball void Class::finalize() {
302ca6bd9cdSMogball   // Sort the methods by public and private. Remove them from the pending list
303ca6bd9cdSMogball   // of methods.
304ca6bd9cdSMogball   SmallVector<std::unique_ptr<Method>> publicMethods, privateMethods;
305ca6bd9cdSMogball   for (auto &method : methods) {
306ca6bd9cdSMogball     if (method->isPrivate())
307ca6bd9cdSMogball       privateMethods.push_back(std::move(method));
308ca6bd9cdSMogball     else
309ca6bd9cdSMogball       publicMethods.push_back(std::move(method));
310ca6bd9cdSMogball   }
311ca6bd9cdSMogball   methods.clear();
312ca6bd9cdSMogball 
313ca6bd9cdSMogball   // If the last visibility declaration wasn't `public`, add one that is. Then,
314ca6bd9cdSMogball   // declare the public methods.
315ca6bd9cdSMogball   if (!publicMethods.empty() && getLastVisibilityDecl() != Visibility::Public)
316ca6bd9cdSMogball     declare<VisibilityDeclaration>(Visibility::Public);
317ca6bd9cdSMogball   for (auto &method : publicMethods)
318ca6bd9cdSMogball     declarations.push_back(std::move(method));
319ca6bd9cdSMogball 
320ca6bd9cdSMogball   // If the last visibility declaration wasn't `private`, add one that is. Then,
321ca6bd9cdSMogball   // declare the private methods.
322ca6bd9cdSMogball   if (!privateMethods.empty() && getLastVisibilityDecl() != Visibility::Private)
323ca6bd9cdSMogball     declare<VisibilityDeclaration>(Visibility::Private);
324ca6bd9cdSMogball   for (auto &method : privateMethods)
325ca6bd9cdSMogball     declarations.push_back(std::move(method));
326ca6bd9cdSMogball 
327ca6bd9cdSMogball   // All fields added to the pending list are private and declared at the bottom
328ca6bd9cdSMogball   // of the class. If the last visibility declaration wasn't `private`, add one
329ca6bd9cdSMogball   // that is, then declare the fields.
330ca6bd9cdSMogball   if (!fields.empty() && getLastVisibilityDecl() != Visibility::Private)
331ca6bd9cdSMogball     declare<VisibilityDeclaration>(Visibility::Private);
332ca6bd9cdSMogball   for (auto &field : fields)
333ca6bd9cdSMogball     declare<Field>(std::move(field));
334ca6bd9cdSMogball   fields.clear();
335ca6bd9cdSMogball }
336ca6bd9cdSMogball 
getLastVisibilityDecl() const337ca6bd9cdSMogball Visibility Class::getLastVisibilityDecl() const {
338ca6bd9cdSMogball   auto reverseDecls = llvm::reverse(declarations);
339ca6bd9cdSMogball   auto it = llvm::find_if(reverseDecls, [](auto &decl) {
340ca6bd9cdSMogball     return isa<VisibilityDeclaration>(decl);
3412696a952SMogball   });
342ca6bd9cdSMogball   return it == reverseDecls.end()
343ca6bd9cdSMogball              ? (isStruct ? Visibility::Public : Visibility::Private)
344ca6bd9cdSMogball              : cast<VisibilityDeclaration>(*it).getVisibility();
3452696a952SMogball }
3462696a952SMogball 
insertAndPruneMethods(std::vector<std::unique_ptr<Method>> & methods,std::unique_ptr<Method> newMethod)347ca6bd9cdSMogball Method *insertAndPruneMethods(std::vector<std::unique_ptr<Method>> &methods,
348ca6bd9cdSMogball                               std::unique_ptr<Method> newMethod) {
3492696a952SMogball   if (llvm::any_of(methods, [&](auto &method) {
350ca6bd9cdSMogball         return method->makesRedundant(*newMethod);
3512696a952SMogball       }))
3522696a952SMogball     return nullptr;
3532696a952SMogball 
354ca6bd9cdSMogball   llvm::erase_if(methods, [&](auto &method) {
355ca6bd9cdSMogball     return newMethod->makesRedundant(*method);
356ca6bd9cdSMogball   });
3572696a952SMogball   methods.push_back(std::move(newMethod));
358ca6bd9cdSMogball   return methods.back().get();
3592696a952SMogball }
3602696a952SMogball 
addMethodAndPrune(Method && newMethod)3612696a952SMogball Method *Class::addMethodAndPrune(Method &&newMethod) {
362ca6bd9cdSMogball   return insertAndPruneMethods(methods,
363ca6bd9cdSMogball                                std::make_unique<Method>(std::move(newMethod)));
3642696a952SMogball }
3652696a952SMogball 
addConstructorAndPrune(Constructor && newCtor)3662696a952SMogball Constructor *Class::addConstructorAndPrune(Constructor &&newCtor) {
367ca6bd9cdSMogball   return dyn_cast_or_null<Constructor>(insertAndPruneMethods(
368ca6bd9cdSMogball       methods, std::make_unique<Constructor>(std::move(newCtor))));
3692696a952SMogball }
370