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