xref: /freebsd/contrib/llvm-project/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp (revision fe75646a0234a261c0013bf1840fdac4acaf0cec)
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>
26 using namespace llvm;
27 using namespace clang;
28 using namespace clang::tblgen;
29 
30 static StringRef getReaderResultType(TypeNode _) { return "QualType"; }
31 
32 namespace {
33 
34 struct 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 
78 struct NodeInfo {
79   std::vector<Property> Properties;
80   CreationRule Creator = nullptr;
81   OverrideRule Override = nullptr;
82   ReadHelperRule ReadHelper = nullptr;
83 };
84 
85 struct CasedTypeInfo {
86   TypeKindRule KindRule;
87   std::vector<TypeCase> Cases;
88 };
89 
90 class 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 
97 public:
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(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(creationRule.getLoc(),
116                         "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(overrideRule.getLoc(),
130                         "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(helperRule.getLoc(),
144                         "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(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(kindRule.getLoc(),
167                         "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(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(list.begin(), 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(derived, derivedInfo,
197                           [&](HasProperties node, const NodeInfo &info) {
198       for (Property prop : info.Properties) {
199         if (ignoredProperties.count(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(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 
268 private:
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 
285 void ASTPropsEmitter::Validator::validate() {
286   for (auto &entry : Emitter.NodeInfos) {
287     validateNode(entry.first, entry.second);
288   }
289 
290   if (ErrorsPrinted > 0) {
291     PrintFatalError("property validation failed");
292   }
293 }
294 
295 void ASTPropsEmitter::Validator::validateNode(HasProperties derivedNode,
296                                               const NodeInfo &derivedNodeInfo) {
297   if (!ValidatedNodes.insert(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                                 [&](HasProperties node,
304                                     const NodeInfo &nodeInfo) {
305     for (Property property : nodeInfo.Properties) {
306       validateType(property.getType(), property);
307 
308       auto result = allProperties.insert(
309                       std::make_pair(property.getName(), 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(existingProperty.getLoc(),
317                    "multiple properties named \"" + property.getName()
318                       + "\" in hierarchy of " + derivedNode.getName());
319         PrintNote(property.getLoc(), "existing property");
320       }
321     }
322   });
323 }
324 
325 void ASTPropsEmitter::Validator::validateType(PropertyType type,
326                                               WrappedRecord context) {
327   if (!type.isGenericSpecialization()) {
328     if (type.getCXXTypeName() == "") {
329       PrintError(type.getLoc(),
330                  "type is not generic but has no C++ type name");
331       if (context) PrintNote(context.getLoc(), "type used here");
332     }
333   } else if (auto eltType = type.getArrayElementType()) {
334     validateType(eltType, context);
335   } else if (auto valueType = type.getOptionalElementType()) {
336     validateType(valueType, context);
337 
338     if (valueType.getPackOptionalCode().empty()) {
339       PrintError(valueType.getLoc(),
340                  "type doesn't provide optional-packing code");
341       if (context) PrintNote(context.getLoc(), "type used here");
342     } else if (valueType.getUnpackOptionalCode().empty()) {
343       PrintError(valueType.getLoc(),
344                  "type doesn't provide optional-unpacking code");
345       if (context) PrintNote(context.getLoc(), "type used here");
346     }
347   } else {
348     PrintError(type.getLoc(), "unknown generic property type");
349     if (context) PrintNote(context.getLoc(), "type used here");
350   }
351 }
352 
353 /****************************************************************************/
354 /**************************** AST READER/WRITERS ****************************/
355 /****************************************************************************/
356 
357 template <class NodeClass>
358 void 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.
412 template <class NodeClass>
413 void 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 
430 void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node,
431                                                const ReaderWriterInfo &info) {
432   // Find the information for this node.
433   auto it = NodeInfos.find(node);
434   if (it == NodeInfos.end())
435     PrintFatalError(node.getLoc(),
436                     "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(node.getLoc(),
445                       "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(node, nodeInfo, [&](Property prop) {
458     // Verify that the creation code refers to this property.
459     if (info.IsReader && !creationCode.contains(prop.getName()))
460       PrintFatalError(nodeInfo.Creator.getLoc(),
461                       "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(info.HelperVariable, prop);
468     else
469       emitWriteOfProperty(info.HelperVariable, prop);
470   });
471 
472   // Emit the final creation code.
473   if (info.IsReader)
474     Out << "    " << creationCode << "\n";
475 }
476 
477 static 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(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(isForRead, out);
498       out << ">";
499     }
500   } else {
501     PrintFatalError(type.getLoc(), "unexpected generic property type");
502   }
503 }
504 
505 /// Emit code to read the given property in a node-reader method.
506 void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
507                                          Property property) {
508   emitReadOfProperty(readerName, property.getName(), property.getType(),
509                      property.getCondition());
510 }
511 
512 void 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*/ true, 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(true, 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, type, /*for read*/ 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.
561 void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
562                                           Property property) {
563   emitWriteOfProperty(writerName, property.getName(), property.getType(),
564                       property.getReadCode(), property.getCondition());
565 }
566 
567 void 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(false, Out);
581   Out << " " << name << " = (" << readCode << ");\n"
582          "    " << writerName << ".find(\"" << name << "\").write";
583   emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ 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.
593 template <class NodeClass>
594 static void emitASTReader(RecordKeeper &records, raw_ostream &out,
595                           StringRef description) {
596   emitSourceFileHeader(description, out);
597 
598   ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
599 }
600 
601 void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) {
602   emitASTReader<TypeNode>(records, out, "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.
607 template <class NodeClass>
608 static void emitASTWriter(RecordKeeper &records, raw_ostream &out,
609                           StringRef description) {
610   emitSourceFileHeader(description, out);
611 
612   ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
613 }
614 
615 void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) {
616   emitASTWriter<TypeNode>(records, out, "A CRTP writer for Clang Type nodes");
617 }
618 
619 /****************************************************************************/
620 /*************************** BASIC READER/WRITERS ***************************/
621 /****************************************************************************/
622 
623 void
624 ASTPropsEmitter::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 
671 void
672 ASTPropsEmitter::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 
711 void
712 ASTPropsEmitter::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(type);
755     if (casedIter != CasedTypeInfos.end()) {
756       enterMethod("node");
757       emitCasedReaderWriterMethodBody(type, 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 
788 void ASTPropsEmitter::emitCasedReaderWriterMethodBody(PropertyType type,
789                                              const CasedTypeInfo &typeCases,
790                                              const ReaderWriterInfo &info) {
791   if (typeCases.Cases.empty()) {
792     assert(typeCases.KindRule);
793     PrintFatalError(typeCases.KindRule.getLoc(),
794                     "no cases found for \"" + type.getCXXTypeName() + "\"");
795   }
796   if (!typeCases.KindRule) {
797     assert(!typeCases.Cases.empty());
798     PrintFatalError(typeCases.Cases.front().getLoc(),
799                     "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(subvar, kindProperty, 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(subvar, kindProperty, kindType,
823                         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(typeCase, 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 
846 void 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.
854 void clang::EmitClangBasicReader(RecordKeeper &records, raw_ostream &out) {
855   emitSourceFileHeader("Helper classes for BasicReaders", out);
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.
864 void clang::EmitClangBasicWriter(RecordKeeper &records, raw_ostream &out) {
865   emitSourceFileHeader("Helper classes for BasicWriters", out);
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