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

source code of mlir/lib/TableGen/Class.cpp