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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "mlir/TableGen/Class.h"
10 #include "mlir/TableGen/Format.h"
11 #include "llvm/ADT/Sequence.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/Support/Debug.h"
14 
15 using namespace mlir;
16 using namespace mlir::tblgen;
17 
18 /// Returns space to be emitted after the given C++ `type`. return "" if the
19 /// ends with '&' or '*', or is empty, else returns " ".
20 static StringRef getSpaceAfterType(StringRef type) {
21   return (type.empty() || type.endswith("&") || type.endswith("*")) ? "" : " ";
22 }
23 
24 //===----------------------------------------------------------------------===//
25 // MethodParameter definitions
26 //===----------------------------------------------------------------------===//
27 
28 void MethodParameter::writeDeclTo(raw_indented_ostream &os) const {
29   if (optional)
30     os << "/*optional*/";
31   os << type << getSpaceAfterType(type) << name;
32   if (hasDefaultValue())
33     os << " = " << defaultValue;
34 }
35 
36 void MethodParameter::writeDefTo(raw_indented_ostream &os) const {
37   if (optional)
38     os << "/*optional*/";
39   os << type << getSpaceAfterType(type) << name;
40 }
41 
42 //===----------------------------------------------------------------------===//
43 // MethodParameters definitions
44 //===----------------------------------------------------------------------===//
45 
46 void MethodParameters::writeDeclTo(raw_indented_ostream &os) const {
47   llvm::interleaveComma(parameters, os,
48                         [&os](auto &param) { param.writeDeclTo(os); });
49 }
50 void MethodParameters::writeDefTo(raw_indented_ostream &os) const {
51   llvm::interleaveComma(parameters, os,
52                         [&os](auto &param) { param.writeDefTo(os); });
53 }
54 
55 bool MethodParameters::subsumes(const MethodParameters &other) const {
56   // These parameters do not subsume the others if there are fewer parameters
57   // or their types do not match.
58   if (parameters.size() < other.parameters.size())
59     return false;
60   if (!std::equal(
61           other.parameters.begin(), other.parameters.end(), parameters.begin(),
62           [](auto &lhs, auto &rhs) { return lhs.getType() == rhs.getType(); }))
63     return false;
64 
65   // If all the common parameters have the same type, we can elide the other
66   // method if this method has the same number of parameters as other or if the
67   // first paramater after the common parameters has a default value (and, as
68   // required by C++, subsequent parameters will have default values too).
69   return parameters.size() == other.parameters.size() ||
70          parameters[other.parameters.size()].hasDefaultValue();
71 }
72 
73 //===----------------------------------------------------------------------===//
74 // MethodSignature definitions
75 //===----------------------------------------------------------------------===//
76 
77 bool MethodSignature::makesRedundant(const MethodSignature &other) const {
78   return methodName == other.methodName &&
79          parameters.subsumes(other.parameters);
80 }
81 
82 void MethodSignature::writeDeclTo(raw_indented_ostream &os) const {
83   os << returnType << getSpaceAfterType(returnType) << methodName << "(";
84   parameters.writeDeclTo(os);
85   os << ")";
86 }
87 
88 void MethodSignature::writeDefTo(raw_indented_ostream &os,
89                                  StringRef namePrefix) const {
90   os << returnType << getSpaceAfterType(returnType) << namePrefix
91      << (namePrefix.empty() ? "" : "::") << methodName << "(";
92   parameters.writeDefTo(os);
93   os << ")";
94 }
95 
96 //===----------------------------------------------------------------------===//
97 // MethodBody definitions
98 //===----------------------------------------------------------------------===//
99 
100 MethodBody::MethodBody(bool declOnly)
101     : declOnly(declOnly), stringOs(body), os(stringOs) {}
102 
103 void MethodBody::writeTo(raw_indented_ostream &os) const {
104   auto bodyRef = StringRef(body).drop_while([](char c) { return c == '\n'; });
105   os << bodyRef;
106   if (bodyRef.empty())
107     return;
108   if (bodyRef.back() != '\n')
109     os << "\n";
110 }
111 
112 //===----------------------------------------------------------------------===//
113 // Method definitions
114 //===----------------------------------------------------------------------===//
115 
116 void Method::writeDeclTo(raw_indented_ostream &os) const {
117   if (isStatic())
118     os << "static ";
119   if (properties & ConstexprValue)
120     os << "constexpr ";
121   methodSignature.writeDeclTo(os);
122   if (isConst())
123     os << " const";
124   if (!isInline()) {
125     os << ";\n";
126     return;
127   }
128   os << " {\n";
129   methodBody.writeTo(os);
130   os << "}\n\n";
131 }
132 
133 void Method::writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const {
134   // The method has no definition to write if it is declaration only or inline.
135   if (properties & Declaration || isInline())
136     return;
137 
138   methodSignature.writeDefTo(os, namePrefix);
139   if (isConst())
140     os << " const";
141   os << " {\n";
142   methodBody.writeTo(os);
143   os << "}\n\n";
144 }
145 
146 //===----------------------------------------------------------------------===//
147 // Constructor definitions
148 //===----------------------------------------------------------------------===//
149 
150 void Constructor::writeDeclTo(raw_indented_ostream &os) const {
151   if (properties & ConstexprValue)
152     os << "constexpr ";
153   methodSignature.writeDeclTo(os);
154   if (!isInline()) {
155     os << ";\n\n";
156     return;
157   }
158   os << ' ';
159   if (!initializers.empty())
160     os << ": ";
161   llvm::interleaveComma(initializers, os,
162                         [&](auto &initializer) { initializer.writeTo(os); });
163   if (!initializers.empty())
164     os << ' ';
165   os << "{";
166   methodBody.writeTo(os);
167   os << "}\n\n";
168 }
169 
170 void Constructor::writeDefTo(raw_indented_ostream &os,
171                              StringRef namePrefix) const {
172   // The method has no definition to write if it is declaration only or inline.
173   if (properties & Declaration || isInline())
174     return;
175 
176   methodSignature.writeDefTo(os, namePrefix);
177   os << ' ';
178   if (!initializers.empty())
179     os << ": ";
180   llvm::interleaveComma(initializers, os,
181                         [&](auto &initializer) { initializer.writeTo(os); });
182   if (!initializers.empty())
183     os << ' ';
184   os << "{";
185   methodBody.writeTo(os);
186   os << "}\n\n";
187 }
188 
189 void Constructor::MemberInitializer::writeTo(raw_indented_ostream &os) const {
190   os << name << '(' << value << ')';
191 }
192 
193 //===----------------------------------------------------------------------===//
194 // Visibility definitions
195 //===----------------------------------------------------------------------===//
196 
197 namespace mlir {
198 namespace tblgen {
199 raw_ostream &operator<<(raw_ostream &os, Visibility visibility) {
200   switch (visibility) {
201   case Visibility::Public:
202     return os << "public";
203   case Visibility::Protected:
204     return os << "protected";
205   case Visibility::Private:
206     return os << "private";
207   }
208   return os;
209 }
210 } // end namespace tblgen
211 } // end namespace mlir
212 
213 //===----------------------------------------------------------------------===//
214 // ParentClass definitions
215 //===----------------------------------------------------------------------===//
216 
217 void ParentClass::writeTo(raw_indented_ostream &os) const {
218   os << visibility << ' ' << name;
219   if (!templateParams.empty()) {
220     auto scope = os.scope("<", ">", /*indent=*/false);
221     llvm::interleaveComma(templateParams, os,
222                           [&](auto &param) { os << param; });
223   }
224 }
225 
226 //===----------------------------------------------------------------------===//
227 // UsingDeclaration definitions
228 //===----------------------------------------------------------------------===//
229 
230 void UsingDeclaration::writeDeclTo(raw_indented_ostream &os) const {
231   os << "using " << name;
232   if (!value.empty())
233     os << " = " << value;
234   os << ";\n";
235 }
236 
237 //===----------------------------------------------------------------------===//
238 // Field definitions
239 //===----------------------------------------------------------------------===//
240 
241 void Field::writeDeclTo(raw_indented_ostream &os) const {
242   os << type << ' ' << name << ";\n";
243 }
244 
245 //===----------------------------------------------------------------------===//
246 // VisibilityDeclaration definitions
247 //===----------------------------------------------------------------------===//
248 
249 void VisibilityDeclaration::writeDeclTo(raw_indented_ostream &os) const {
250   os.unindent();
251   os << visibility << ":\n";
252   os.indent();
253 }
254 
255 //===----------------------------------------------------------------------===//
256 // ExtraClassDeclaration definitions
257 //===----------------------------------------------------------------------===//
258 
259 void ExtraClassDeclaration::writeDeclTo(raw_indented_ostream &os) const {
260   os.printReindented(extraClassDeclaration);
261 }
262 
263 //===----------------------------------------------------------------------===//
264 // Class definitions
265 //===----------------------------------------------------------------------===//
266 
267 ParentClass &Class::addParent(ParentClass parent) {
268   parents.push_back(std::move(parent));
269   return parents.back();
270 }
271 
272 void Class::writeDeclTo(raw_indented_ostream &os) const {
273   // Declare the class.
274   os << (isStruct ? "struct" : "class") << ' ' << className << ' ';
275 
276   // Declare the parent classes, if any.
277   if (!parents.empty()) {
278     os << ": ";
279     llvm::interleaveComma(parents, os,
280                           [&](auto &parent) { parent.writeTo(os); });
281     os << ' ';
282   }
283   auto classScope = os.scope("{\n", "};\n", /*indent=*/true);
284 
285   // Print all the class declarations.
286   for (auto &decl : declarations)
287     decl->writeDeclTo(os);
288 }
289 
290 void Class::writeDefTo(raw_indented_ostream &os) const {
291   // Print all the definitions.
292   for (auto &decl : declarations)
293     decl->writeDefTo(os, className);
294 }
295 
296 void Class::finalize() {
297   // Sort the methods by public and private. Remove them from the pending list
298   // of methods.
299   SmallVector<std::unique_ptr<Method>> publicMethods, privateMethods;
300   for (auto &method : methods) {
301     if (method->isPrivate())
302       privateMethods.push_back(std::move(method));
303     else
304       publicMethods.push_back(std::move(method));
305   }
306   methods.clear();
307 
308   // If the last visibility declaration wasn't `public`, add one that is. Then,
309   // declare the public methods.
310   if (!publicMethods.empty() && getLastVisibilityDecl() != Visibility::Public)
311     declare<VisibilityDeclaration>(Visibility::Public);
312   for (auto &method : publicMethods)
313     declarations.push_back(std::move(method));
314 
315   // If the last visibility declaration wasn't `private`, add one that is. Then,
316   // declare the private methods.
317   if (!privateMethods.empty() && getLastVisibilityDecl() != Visibility::Private)
318     declare<VisibilityDeclaration>(Visibility::Private);
319   for (auto &method : privateMethods)
320     declarations.push_back(std::move(method));
321 
322   // All fields added to the pending list are private and declared at the bottom
323   // of the class. If the last visibility declaration wasn't `private`, add one
324   // that is, then declare the fields.
325   if (!fields.empty() && getLastVisibilityDecl() != Visibility::Private)
326     declare<VisibilityDeclaration>(Visibility::Private);
327   for (auto &field : fields)
328     declare<Field>(std::move(field));
329   fields.clear();
330 }
331 
332 Visibility Class::getLastVisibilityDecl() const {
333   auto reverseDecls = llvm::reverse(declarations);
334   auto it = llvm::find_if(reverseDecls, [](auto &decl) {
335     return isa<VisibilityDeclaration>(decl);
336   });
337   return it == reverseDecls.end()
338              ? (isStruct ? Visibility::Public : Visibility::Private)
339              : cast<VisibilityDeclaration>(*it).getVisibility();
340 }
341 
342 Method *insertAndPruneMethods(std::vector<std::unique_ptr<Method>> &methods,
343                               std::unique_ptr<Method> newMethod) {
344   if (llvm::any_of(methods, [&](auto &method) {
345         return method->makesRedundant(*newMethod);
346       }))
347     return nullptr;
348 
349   llvm::erase_if(methods, [&](auto &method) {
350     return newMethod->makesRedundant(*method);
351   });
352   methods.push_back(std::move(newMethod));
353   return methods.back().get();
354 }
355 
356 Method *Class::addMethodAndPrune(Method &&newMethod) {
357   return insertAndPruneMethods(methods,
358                                std::make_unique<Method>(std::move(newMethod)));
359 }
360 
361 Constructor *Class::addConstructorAndPrune(Constructor &&newCtor) {
362   return dyn_cast_or_null<Constructor>(insertAndPruneMethods(
363       methods, std::make_unique<Constructor>(std::move(newCtor))));
364 }
365