1//===-- ClangASTPropsEmitter.cpp - Generate Clang AST properties ----------===//
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// This tablegen backend emits code for working with Clang AST properties.
10//
11//===----------------------------------------------------------------------===//
12
13#include "ASTTableGen.h"
14#include "TableGenBackends.h"
15
16#include "llvm/ADT/Twine.h"
17#include "llvm/TableGen/Error.h"
18#include "llvm/TableGen/Record.h"
19#include "llvm/TableGen/TableGenBackend.h"
20#include <cctype>
21#include <map>
22#include <set>
23#include <string>
24using namespace llvm;
25using namespace clang;
26using namespace clang::tblgen;
27
28static StringRef getReaderResultType(TypeNode _) { return "QualType"; }
29
30namespace {
31
32struct ReaderWriterInfo {
33 bool IsReader;
34
35 /// The name of the node hierarchy. Not actually sensitive to IsReader,
36 /// but useful to cache here anyway.
37 StringRef HierarchyName;
38
39 /// The suffix on classes: Reader/Writer
40 StringRef ClassSuffix;
41
42 /// The base name of methods: read/write
43 StringRef MethodPrefix;
44
45 /// The name of the property helper member: R/W
46 StringRef HelperVariable;
47
48 /// The result type of methods on the class.
49 StringRef ResultType;
50
51 template <class NodeClass>
52 static ReaderWriterInfo forReader() {
53 return ReaderWriterInfo{
54 true,
55 NodeClass::getASTHierarchyName(),
56 "Reader",
57 "read",
58 "R",
59 getReaderResultType(NodeClass())
60 };
61 }
62
63 template <class NodeClass>
64 static ReaderWriterInfo forWriter() {
65 return ReaderWriterInfo{
66 false,
67 NodeClass::getASTHierarchyName(),
68 "Writer",
69 "write",
70 "W",
71 "void"
72 };
73 }
74};
75
76struct NodeInfo {
77 std::vector<Property> Properties;
78 CreationRule Creator = nullptr;
79 OverrideRule Override = nullptr;
80 ReadHelperRule ReadHelper = nullptr;
81};
82
83struct CasedTypeInfo {
84 TypeKindRule KindRule;
85 std::vector<TypeCase> Cases;
86};
87
88class ASTPropsEmitter {
89 raw_ostream &Out;
90 const RecordKeeper &Records;
91 std::map<HasProperties, NodeInfo> NodeInfos;
92 std::vector<PropertyType> AllPropertyTypes;
93 std::map<PropertyType, CasedTypeInfo> CasedTypeInfos;
94
95public:
96 ASTPropsEmitter(const RecordKeeper &records, raw_ostream &out)
97 : Out(out), Records(records) {
98
99 // Find all the properties.
100 for (Property property :
101 records.getAllDerivedDefinitions(PropertyClassName)) {
102 HasProperties node = property.getClass();
103 NodeInfos[node].Properties.push_back(x: property);
104 }
105
106 // Find all the creation rules.
107 for (CreationRule creationRule :
108 records.getAllDerivedDefinitions(CreationRuleClassName)) {
109 HasProperties node = creationRule.getClass();
110
111 auto &info = NodeInfos[node];
112 if (info.Creator) {
113 PrintFatalError(ErrorLoc: creationRule.getLoc(), Msg: "multiple creator rules for \"" +
114 node.getName() + "\"");
115 }
116 info.Creator = creationRule;
117 }
118
119 // Find all the override rules.
120 for (OverrideRule overrideRule :
121 records.getAllDerivedDefinitions(OverrideRuleClassName)) {
122 HasProperties node = overrideRule.getClass();
123
124 auto &info = NodeInfos[node];
125 if (info.Override) {
126 PrintFatalError(ErrorLoc: overrideRule.getLoc(),
127 Msg: "multiple override rules for \"" + node.getName() +
128 "\"");
129 }
130 info.Override = overrideRule;
131 }
132
133 // Find all the write helper rules.
134 for (ReadHelperRule helperRule :
135 records.getAllDerivedDefinitions(ReadHelperRuleClassName)) {
136 HasProperties node = helperRule.getClass();
137
138 auto &info = NodeInfos[node];
139 if (info.ReadHelper) {
140 PrintFatalError(ErrorLoc: helperRule.getLoc(),
141 Msg: "multiple write helper rules for \"" + node.getName() +
142 "\"");
143 }
144 info.ReadHelper = helperRule;
145 }
146
147 // Find all the concrete property types.
148 for (PropertyType type :
149 records.getAllDerivedDefinitions(PropertyTypeClassName)) {
150 // Ignore generic specializations; they're generally not useful when
151 // emitting basic emitters etc.
152 if (type.isGenericSpecialization())
153 continue;
154
155 AllPropertyTypes.push_back(x: type);
156 }
157
158 // Find all the type kind rules.
159 for (TypeKindRule kindRule :
160 records.getAllDerivedDefinitions(TypeKindClassName)) {
161 PropertyType type = kindRule.getParentType();
162 auto &info = CasedTypeInfos[type];
163 if (info.KindRule) {
164 PrintFatalError(ErrorLoc: kindRule.getLoc(), Msg: "multiple kind rules for \"" +
165 type.getCXXTypeName() + "\"");
166 }
167 info.KindRule = kindRule;
168 }
169
170 // Find all the type cases.
171 for (TypeCase typeCase :
172 records.getAllDerivedDefinitions(TypeCaseClassName)) {
173 CasedTypeInfos[typeCase.getParentType()].Cases.push_back(x: typeCase);
174 }
175
176 Validator(*this).validate();
177 }
178
179 void visitAllProperties(HasProperties derived, const NodeInfo &derivedInfo,
180 function_ref<void(Property)> visit) {
181 std::set<StringRef> ignoredProperties;
182
183 auto overrideRule = derivedInfo.Override;
184 if (overrideRule) {
185 auto list = overrideRule.getIgnoredProperties();
186 ignoredProperties.insert(first: list.begin(), last: list.end());
187 }
188
189 // TODO: we should sort the properties in various ways
190 // - put arrays at the end to enable abbreviations
191 // - put conditional properties after properties used in the condition
192
193 visitAllNodesWithInfo(derivedNode: derived, derivedNodeInfo: derivedInfo,
194 visit: [&](HasProperties node, const NodeInfo &info) {
195 for (Property prop : info.Properties) {
196 if (ignoredProperties.count(x: prop.getName()))
197 continue;
198
199 visit(prop);
200 }
201 });
202 }
203
204 void visitAllNodesWithInfo(
205 HasProperties derivedNode, const NodeInfo &derivedNodeInfo,
206 function_ref<void(HasProperties node, const NodeInfo &info)> visit) {
207 visit(derivedNode, derivedNodeInfo);
208
209 // Also walk the bases if appropriate.
210 if (ASTNode base = derivedNode.getAs<ASTNode>()) {
211 for (base = base.getBase(); base; base = base.getBase()) {
212 auto it = NodeInfos.find(x: base);
213
214 // Ignore intermediate nodes that don't add interesting properties.
215 if (it == NodeInfos.end())
216 continue;
217 auto &baseInfo = it->second;
218
219 visit(base, baseInfo);
220 }
221 }
222 }
223
224 template <class NodeClass> void emitNodeReaderClass() {
225 auto info = ReaderWriterInfo::forReader<NodeClass>();
226 emitNodeReaderWriterClass<NodeClass>(info);
227 }
228
229 template <class NodeClass> void emitNodeWriterClass() {
230 auto info = ReaderWriterInfo::forWriter<NodeClass>();
231 emitNodeReaderWriterClass<NodeClass>(info);
232 }
233
234 template <class NodeClass>
235 void emitNodeReaderWriterClass(const ReaderWriterInfo &info);
236
237 template <class NodeClass>
238 void emitNodeReaderWriterMethod(NodeClass node, const ReaderWriterInfo &info);
239
240 void emitPropertiedReaderWriterBody(HasProperties node,
241 const ReaderWriterInfo &info);
242
243 void emitReadOfProperty(StringRef readerName, Property property);
244 void emitReadOfProperty(StringRef readerName, StringRef name,
245 PropertyType type, StringRef condition = "");
246
247 void emitWriteOfProperty(StringRef writerName, Property property);
248 void emitWriteOfProperty(StringRef writerName, StringRef name,
249 PropertyType type, StringRef readCode,
250 StringRef condition = "");
251
252 void emitBasicReaderWriterFile(const ReaderWriterInfo &info);
253 void emitDispatcherTemplate(const ReaderWriterInfo &info);
254 void emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info);
255 void emitBasicReaderWriterTemplate(const ReaderWriterInfo &info);
256
257 void emitCasedReaderWriterMethodBody(PropertyType type,
258 const CasedTypeInfo &typeCases,
259 const ReaderWriterInfo &info);
260
261private:
262 class Validator {
263 ASTPropsEmitter &Emitter;
264 std::set<HasProperties> ValidatedNodes;
265
266 public:
267 Validator(ASTPropsEmitter &emitter) : Emitter(emitter) {}
268 void validate();
269
270 private:
271 void validateNode(HasProperties node, const NodeInfo &nodeInfo);
272 void validateType(PropertyType type, WrappedRecord context);
273 };
274};
275
276} // end anonymous namespace
277
278void ASTPropsEmitter::Validator::validate() {
279 for (auto &entry : Emitter.NodeInfos) {
280 validateNode(node: entry.first, nodeInfo: entry.second);
281 }
282
283 if (ErrorsPrinted > 0) {
284 PrintFatalError(Msg: "property validation failed");
285 }
286}
287
288void ASTPropsEmitter::Validator::validateNode(HasProperties derivedNode,
289 const NodeInfo &derivedNodeInfo) {
290 if (!ValidatedNodes.insert(x: derivedNode).second) return;
291
292 // A map from property name to property.
293 std::map<StringRef, Property> allProperties;
294
295 Emitter.visitAllNodesWithInfo(derivedNode, derivedNodeInfo,
296 visit: [&](HasProperties node,
297 const NodeInfo &nodeInfo) {
298 for (Property property : nodeInfo.Properties) {
299 validateType(type: property.getType(), context: property);
300
301 auto result = allProperties.insert(
302 x: std::make_pair(x: property.getName(), y&: property));
303
304 // Diagnose non-unique properties.
305 if (!result.second) {
306 // The existing property is more likely to be associated with a
307 // derived node, so use it as the error.
308 Property existingProperty = result.first->second;
309 PrintError(ErrorLoc: existingProperty.getLoc(),
310 Msg: "multiple properties named \"" + property.getName()
311 + "\" in hierarchy of " + derivedNode.getName());
312 PrintNote(NoteLoc: property.getLoc(), Msg: "existing property");
313 }
314 }
315 });
316}
317
318void ASTPropsEmitter::Validator::validateType(PropertyType type,
319 WrappedRecord context) {
320 if (!type.isGenericSpecialization()) {
321 if (type.getCXXTypeName() == "") {
322 PrintError(ErrorLoc: type.getLoc(),
323 Msg: "type is not generic but has no C++ type name");
324 if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here");
325 }
326 } else if (auto eltType = type.getArrayElementType()) {
327 validateType(type: eltType, context);
328 } else if (auto valueType = type.getOptionalElementType()) {
329 validateType(type: valueType, context);
330
331 if (valueType.getPackOptionalCode().empty()) {
332 PrintError(ErrorLoc: valueType.getLoc(),
333 Msg: "type doesn't provide optional-packing code");
334 if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here");
335 } else if (valueType.getUnpackOptionalCode().empty()) {
336 PrintError(ErrorLoc: valueType.getLoc(),
337 Msg: "type doesn't provide optional-unpacking code");
338 if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here");
339 }
340 } else {
341 PrintError(ErrorLoc: type.getLoc(), Msg: "unknown generic property type");
342 if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here");
343 }
344}
345
346/****************************************************************************/
347/**************************** AST READER/WRITERS ****************************/
348/****************************************************************************/
349
350template <class NodeClass>
351void ASTPropsEmitter::emitNodeReaderWriterClass(const ReaderWriterInfo &info) {
352 StringRef suffix = info.ClassSuffix;
353 StringRef var = info.HelperVariable;
354
355 // Enter the class declaration.
356 Out << "template <class Property" << suffix << ">\n"
357 "class Abstract" << info.HierarchyName << suffix << " {\n"
358 "public:\n"
359 " Property" << suffix << " &" << var << ";\n\n";
360
361 // Emit the constructor.
362 Out << " Abstract" << info.HierarchyName << suffix
363 << "(Property" << suffix << " &" << var << ") : "
364 << var << "(" << var << ") {}\n\n";
365
366 // Emit a method that dispatches on a kind to the appropriate node-specific
367 // method.
368 Out << " " << info.ResultType << " " << info.MethodPrefix << "(";
369 if (info.IsReader)
370 Out << NodeClass::getASTIdTypeName() << " kind";
371 else
372 Out << "const " << info.HierarchyName << " *node";
373 Out << ") {\n"
374 " switch (";
375 if (info.IsReader)
376 Out << "kind";
377 else
378 Out << "node->" << NodeClass::getASTIdAccessorName() << "()";
379 Out << ") {\n";
380 visitASTNodeHierarchy<NodeClass>(Records, [&](NodeClass node, NodeClass _) {
381 if (node.isAbstract()) return;
382 Out << " case " << info.HierarchyName << "::" << node.getId() << ":\n"
383 " return " << info.MethodPrefix << node.getClassName() << "(";
384 if (!info.IsReader)
385 Out << "static_cast<const " << node.getClassName()
386 << " *>(node)";
387 Out << ");\n";
388 });
389 Out << " }\n"
390 " llvm_unreachable(\"bad kind\");\n"
391 " }\n\n";
392
393 // Emit node-specific methods for all the concrete nodes.
394 visitASTNodeHierarchy<NodeClass>(Records,
395 [&](NodeClass node, NodeClass base) {
396 if (node.isAbstract()) return;
397 emitNodeReaderWriterMethod(node, info);
398 });
399
400 // Finish the class.
401 Out << "};\n\n";
402}
403
404/// Emit a reader method for the given concrete AST node class.
405template <class NodeClass>
406void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node,
407 const ReaderWriterInfo &info) {
408 // Declare and start the method.
409 Out << " " << info.ResultType << " "
410 << info.MethodPrefix << node.getClassName() << "(";
411 if (!info.IsReader)
412 Out << "const " << node.getClassName() << " *node";
413 Out << ") {\n";
414 if (info.IsReader)
415 Out << " auto &ctx = " << info.HelperVariable << ".getASTContext();\n";
416
417 emitPropertiedReaderWriterBody(node, info);
418
419 // Finish the method declaration.
420 Out << " }\n\n";
421}
422
423void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node,
424 const ReaderWriterInfo &info) {
425 // Find the information for this node.
426 auto it = NodeInfos.find(x: node);
427 if (it == NodeInfos.end())
428 PrintFatalError(ErrorLoc: node.getLoc(),
429 Msg: "no information about how to deserialize \""
430 + node.getName() + "\"");
431 auto &nodeInfo = it->second;
432
433 StringRef creationCode;
434 if (info.IsReader) {
435 // We should have a creation rule.
436 if (!nodeInfo.Creator)
437 PrintFatalError(ErrorLoc: node.getLoc(),
438 Msg: "no " CreationRuleClassName " for \""
439 + node.getName() + "\"");
440
441 creationCode = nodeInfo.Creator.getCreationCode();
442 }
443
444 // Emit the ReadHelper code, if present.
445 if (!info.IsReader && nodeInfo.ReadHelper) {
446 Out << " " << nodeInfo.ReadHelper.getHelperCode() << "\n";
447 }
448
449 // Emit code to read all the properties.
450 visitAllProperties(derived: node, derivedInfo: nodeInfo, visit: [&](Property prop) {
451 // Verify that the creation code refers to this property.
452 if (info.IsReader && !creationCode.contains(Other: prop.getName()))
453 PrintFatalError(ErrorLoc: nodeInfo.Creator.getLoc(),
454 Msg: "creation code for " + node.getName()
455 + " doesn't refer to property \""
456 + prop.getName() + "\"");
457
458 // Emit code to read or write this property.
459 if (info.IsReader)
460 emitReadOfProperty(readerName: info.HelperVariable, property: prop);
461 else
462 emitWriteOfProperty(writerName: info.HelperVariable, property: prop);
463 });
464
465 // Emit the final creation code.
466 if (info.IsReader)
467 Out << " " << creationCode << "\n";
468}
469
470static void emitBasicReaderWriterMethodSuffix(raw_ostream &out,
471 PropertyType type,
472 bool isForRead) {
473 if (!type.isGenericSpecialization()) {
474 out << type.getAbstractTypeName();
475 } else if (auto eltType = type.getArrayElementType()) {
476 out << "Array";
477 // We only include an explicit template argument for reads so that
478 // we don't cause spurious const mismatches.
479 if (isForRead) {
480 out << "<";
481 eltType.emitCXXValueTypeName(forRead: isForRead, out);
482 out << ">";
483 }
484 } else if (auto valueType = type.getOptionalElementType()) {
485 out << "Optional";
486 // We only include an explicit template argument for reads so that
487 // we don't cause spurious const mismatches.
488 if (isForRead) {
489 out << "<";
490 valueType.emitCXXValueTypeName(forRead: isForRead, out);
491 out << ">";
492 }
493 } else {
494 PrintFatalError(ErrorLoc: type.getLoc(), Msg: "unexpected generic property type");
495 }
496}
497
498/// Emit code to read the given property in a node-reader method.
499void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
500 Property property) {
501 emitReadOfProperty(readerName, name: property.getName(), type: property.getType(),
502 condition: property.getCondition());
503}
504
505void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
506 StringRef name,
507 PropertyType type,
508 StringRef condition) {
509 // Declare all the necessary buffers.
510 auto bufferTypes = type.getBufferElementTypes();
511 for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
512 Out << " llvm::SmallVector<";
513 PropertyType(bufferTypes[i]).emitCXXValueTypeName(/*for read*/ forRead: true, out&: Out);
514 Out << ", 8> " << name << "_buffer_" << i << ";\n";
515 }
516
517 // T prop = R.find("prop").read##ValueType(buffers...);
518 // We intentionally ignore shouldPassByReference here: we're going to
519 // get a pr-value back from read(), and we should be able to forward
520 // that in the creation rule.
521 Out << " ";
522 if (!condition.empty())
523 Out << "std::optional<";
524 type.emitCXXValueTypeName(forRead: true, out&: Out);
525 if (!condition.empty()) Out << ">";
526 Out << " " << name;
527
528 if (condition.empty()) {
529 Out << " = ";
530 } else {
531 Out << ";\n"
532 " if (" << condition << ") {\n"
533 " " << name << ".emplace(";
534 }
535
536 Out << readerName << ".find(\"" << name << "\")."
537 << (type.isGenericSpecialization() ? "template " : "") << "read";
538 emitBasicReaderWriterMethodSuffix(out&: Out, type, /*for read*/ isForRead: true);
539 Out << "(";
540 for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) {
541 Out << (i > 0 ? ", " : "") << name << "_buffer_" << i;
542 }
543 Out << ")";
544
545 if (condition.empty()) {
546 Out << ";\n";
547 } else {
548 Out << ");\n"
549 " }\n";
550 }
551}
552
553/// Emit code to write the given property in a node-writer method.
554void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
555 Property property) {
556 emitWriteOfProperty(writerName, name: property.getName(), type: property.getType(),
557 readCode: property.getReadCode(), condition: property.getCondition());
558}
559
560void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
561 StringRef name,
562 PropertyType type,
563 StringRef readCode,
564 StringRef condition) {
565 if (!condition.empty()) {
566 Out << " if (" << condition << ") {\n";
567 }
568
569 // Focus down to the property:
570 // T prop = <READ>;
571 // W.find("prop").write##ValueType(prop);
572 Out << " ";
573 type.emitCXXValueTypeName(forRead: false, out&: Out);
574 Out << " " << name << " = (" << readCode << ");\n"
575 " " << writerName << ".find(\"" << name << "\").write";
576 emitBasicReaderWriterMethodSuffix(out&: Out, type, /*for read*/ isForRead: false);
577 Out << "(" << name << ");\n";
578
579 if (!condition.empty()) {
580 Out << " }\n";
581 }
582}
583
584/// Emit an .inc file that defines the AbstractFooReader class
585/// for the given AST class hierarchy.
586template <class NodeClass>
587static void emitASTReader(const RecordKeeper &records, raw_ostream &out,
588 StringRef description) {
589 emitSourceFileHeader(Desc: description, OS&: out, Record: records);
590
591 ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
592}
593
594void clang::EmitClangTypeReader(const RecordKeeper &records, raw_ostream &out) {
595 emitASTReader<TypeNode>(records, out, description: "A CRTP reader for Clang Type nodes");
596}
597
598/// Emit an .inc file that defines the AbstractFooWriter class
599/// for the given AST class hierarchy.
600template <class NodeClass>
601static void emitASTWriter(const RecordKeeper &records, raw_ostream &out,
602 StringRef description) {
603 emitSourceFileHeader(Desc: description, OS&: out, Record: records);
604
605 ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
606}
607
608void clang::EmitClangTypeWriter(const RecordKeeper &records, raw_ostream &out) {
609 emitASTWriter<TypeNode>(records, out, description: "A CRTP writer for Clang Type nodes");
610}
611
612/****************************************************************************/
613/*************************** BASIC READER/WRITERS ***************************/
614/****************************************************************************/
615
616void
617ASTPropsEmitter::emitDispatcherTemplate(const ReaderWriterInfo &info) {
618 // Declare the {Read,Write}Dispatcher template.
619 StringRef dispatcherPrefix = (info.IsReader ? "Read" : "Write");
620 Out << "template <class ValueType>\n"
621 "struct " << dispatcherPrefix << "Dispatcher;\n";
622
623 // Declare a specific specialization of the dispatcher template.
624 auto declareSpecialization =
625 [&](StringRef specializationParameters,
626 const Twine &cxxTypeName,
627 StringRef methodSuffix) {
628 StringRef var = info.HelperVariable;
629 Out << "template " << specializationParameters << "\n"
630 "struct " << dispatcherPrefix << "Dispatcher<"
631 << cxxTypeName << "> {\n";
632 Out << " template <class Basic" << info.ClassSuffix << ", class... Args>\n"
633 " static " << (info.IsReader ? cxxTypeName : "void") << " "
634 << info.MethodPrefix
635 << "(Basic" << info.ClassSuffix << " &" << var
636 << ", Args &&... args) {\n"
637 " return " << var << "."
638 << info.MethodPrefix << methodSuffix
639 << "(std::forward<Args>(args)...);\n"
640 " }\n"
641 "};\n";
642 };
643
644 // Declare explicit specializations for each of the concrete types.
645 for (PropertyType type : AllPropertyTypes) {
646 declareSpecialization("<>",
647 type.getCXXTypeName(),
648 type.getAbstractTypeName());
649 // Also declare a specialization for the const type when appropriate.
650 if (!info.IsReader && type.isConstWhenWriting()) {
651 declareSpecialization("<>",
652 "const " + type.getCXXTypeName(),
653 type.getAbstractTypeName());
654 }
655 }
656 // Declare partial specializations for ArrayRef and Optional.
657 declareSpecialization("<class T>",
658 "llvm::ArrayRef<T>",
659 "Array");
660 declareSpecialization("<class T>", "std::optional<T>", "Optional");
661 Out << "\n";
662}
663
664void
665ASTPropsEmitter::emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info) {
666 StringRef classPrefix = (info.IsReader ? "Unpack" : "Pack");
667 StringRef methodName = (info.IsReader ? "unpack" : "pack");
668
669 // Declare the {Pack,Unpack}OptionalValue template.
670 Out << "template <class ValueType>\n"
671 "struct " << classPrefix << "OptionalValue;\n";
672
673 auto declareSpecialization = [&](const Twine &typeName, StringRef code) {
674 Out << "template <>\n"
675 "struct "
676 << classPrefix << "OptionalValue<" << typeName
677 << "> {\n"
678 " static "
679 << (info.IsReader ? "std::optional<" : "") << typeName
680 << (info.IsReader ? "> " : " ") << methodName << "("
681 << (info.IsReader ? "" : "std::optional<") << typeName
682 << (info.IsReader ? "" : ">")
683 << " value) {\n"
684 " return "
685 << code
686 << ";\n"
687 " }\n"
688 "};\n";
689 };
690
691 for (PropertyType type : AllPropertyTypes) {
692 StringRef code = (info.IsReader ? type.getUnpackOptionalCode()
693 : type.getPackOptionalCode());
694 if (code.empty()) continue;
695
696 StringRef typeName = type.getCXXTypeName();
697 declareSpecialization(typeName, code);
698 if (type.isConstWhenWriting() && !info.IsReader)
699 declareSpecialization("const " + typeName, code);
700 }
701 Out << "\n";
702}
703
704void
705ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
706 // Emit the Basic{Reader,Writer}Base template.
707 Out << "template <class Impl>\n"
708 "class Basic" << info.ClassSuffix << "Base {\n";
709 Out << " ASTContext &C;\n";
710 Out << "protected:\n"
711 " Basic"
712 << info.ClassSuffix << "Base" << ("(ASTContext &ctx) : C(ctx)")
713 << " {}\n"
714 "public:\n";
715 Out << " ASTContext &getASTContext() { return C; }\n";
716 Out << " Impl &asImpl() { return static_cast<Impl&>(*this); }\n";
717
718 auto enterReaderWriterMethod = [&](StringRef cxxTypeName,
719 StringRef abstractTypeName,
720 bool shouldPassByReference,
721 bool constWhenWriting,
722 StringRef paramName) {
723 Out << " " << (info.IsReader ? cxxTypeName : "void")
724 << " " << info.MethodPrefix << abstractTypeName << "(";
725 if (!info.IsReader)
726 Out << (shouldPassByReference || constWhenWriting ? "const " : "")
727 << cxxTypeName
728 << (shouldPassByReference ? " &" : "") << " " << paramName;
729 Out << ") {\n";
730 };
731
732 // Emit {read,write}ValueType methods for all the enum and subclass types
733 // that default to using the integer/base-class implementations.
734 for (PropertyType type : AllPropertyTypes) {
735 auto enterMethod = [&](StringRef paramName) {
736 enterReaderWriterMethod(type.getCXXTypeName(),
737 type.getAbstractTypeName(),
738 type.shouldPassByReference(),
739 type.isConstWhenWriting(),
740 paramName);
741 };
742 auto exitMethod = [&] {
743 Out << " }\n";
744 };
745
746 // Handled cased types.
747 auto casedIter = CasedTypeInfos.find(x: type);
748 if (casedIter != CasedTypeInfos.end()) {
749 enterMethod("node");
750 emitCasedReaderWriterMethodBody(type, typeCases: casedIter->second, info);
751 exitMethod();
752
753 } else if (type.isEnum()) {
754 enterMethod("value");
755 if (info.IsReader)
756 Out << " return asImpl().template readEnum<"
757 << type.getCXXTypeName() << ">();\n";
758 else
759 Out << " asImpl().writeEnum(value);\n";
760 exitMethod();
761
762 } else if (PropertyType superclass = type.getSuperclassType()) {
763 enterMethod("value");
764 if (info.IsReader)
765 Out << " return cast_or_null<" << type.getSubclassClassName()
766 << ">(asImpl().read"
767 << superclass.getAbstractTypeName()
768 << "());\n";
769 else
770 Out << " asImpl().write" << superclass.getAbstractTypeName()
771 << "(value);\n";
772 exitMethod();
773
774 } else {
775 // The other types can't be handled as trivially.
776 }
777 }
778 Out << "};\n\n";
779}
780
781void ASTPropsEmitter::emitCasedReaderWriterMethodBody(PropertyType type,
782 const CasedTypeInfo &typeCases,
783 const ReaderWriterInfo &info) {
784 if (typeCases.Cases.empty()) {
785 assert(typeCases.KindRule);
786 PrintFatalError(ErrorLoc: typeCases.KindRule.getLoc(),
787 Msg: "no cases found for \"" + type.getCXXTypeName() + "\"");
788 }
789 if (!typeCases.KindRule) {
790 assert(!typeCases.Cases.empty());
791 PrintFatalError(ErrorLoc: typeCases.Cases.front().getLoc(),
792 Msg: "no kind rule for \"" + type.getCXXTypeName() + "\"");
793 }
794
795 auto var = info.HelperVariable;
796 std::string subvar = ("sub" + var).str();
797
798 // Bind `ctx` for readers.
799 if (info.IsReader)
800 Out << " auto &ctx = asImpl().getASTContext();\n";
801
802 // Start an object.
803 Out << " auto &&" << subvar << " = asImpl()."
804 << info.MethodPrefix << "Object();\n";
805
806 // Read/write the kind property;
807 TypeKindRule kindRule = typeCases.KindRule;
808 StringRef kindProperty = kindRule.getKindPropertyName();
809 PropertyType kindType = kindRule.getKindType();
810 if (info.IsReader) {
811 emitReadOfProperty(readerName: subvar, name: kindProperty, type: kindType);
812 } else {
813 // Write the property. Note that this will implicitly read the
814 // kind into a local variable with the right name.
815 emitWriteOfProperty(writerName: subvar, name: kindProperty, type: kindType,
816 readCode: kindRule.getReadCode());
817 }
818
819 // Prepare a ReaderWriterInfo with a helper variable that will use
820 // the sub-reader/writer.
821 ReaderWriterInfo subInfo = info;
822 subInfo.HelperVariable = subvar;
823
824 // Switch on the kind.
825 Out << " switch (" << kindProperty << ") {\n";
826 for (TypeCase typeCase : typeCases.Cases) {
827 Out << " case " << type.getCXXTypeName() << "::"
828 << typeCase.getCaseName() << ": {\n";
829 emitPropertiedReaderWriterBody(node: typeCase, info: subInfo);
830 if (!info.IsReader)
831 Out << " return;\n";
832 Out << " }\n\n";
833 }
834 Out << " }\n"
835 " llvm_unreachable(\"bad " << kindType.getCXXTypeName()
836 << "\");\n";
837}
838
839void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) {
840 emitDispatcherTemplate(info);
841 emitPackUnpackOptionalTemplate(info);
842 emitBasicReaderWriterTemplate(info);
843}
844
845/// Emit an .inc file that defines some helper classes for reading
846/// basic values.
847void clang::EmitClangBasicReader(const RecordKeeper &records,
848 raw_ostream &out) {
849 emitSourceFileHeader(Desc: "Helper classes for BasicReaders", OS&: out, Record: records);
850
851 // Use any property, we won't be using those properties.
852 auto info = ReaderWriterInfo::forReader<TypeNode>();
853 ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info);
854}
855
856/// Emit an .inc file that defines some helper classes for writing
857/// basic values.
858void clang::EmitClangBasicWriter(const RecordKeeper &records,
859 raw_ostream &out) {
860 emitSourceFileHeader(Desc: "Helper classes for BasicWriters", OS&: out, Record: records);
861
862 // Use any property, we won't be using those properties.
863 auto info = ReaderWriterInfo::forWriter<TypeNode>();
864 ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info);
865}
866

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/utils/TableGen/ClangASTPropertiesEmitter.cpp