xref: /llvm-project/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp (revision 63aa8cf6becbeb4983e3d1a7fa3cd8a7c7147118)
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>
24 using namespace llvm;
25 using namespace clang;
26 using namespace clang::tblgen;
27 
28 static StringRef getReaderResultType(TypeNode _) { return "QualType"; }
29 
30 namespace {
31 
32 struct 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 
76 struct NodeInfo {
77   std::vector<Property> Properties;
78   CreationRule Creator = nullptr;
79   OverrideRule Override = nullptr;
80   ReadHelperRule ReadHelper = nullptr;
81 };
82 
83 struct CasedTypeInfo {
84   TypeKindRule KindRule;
85   std::vector<TypeCase> Cases;
86 };
87 
88 class 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 
95 public:
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(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(creationRule.getLoc(), "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(overrideRule.getLoc(),
127                         "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(helperRule.getLoc(),
141                         "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(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(kindRule.getLoc(), "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(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(list.begin(), 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(derived, derivedInfo,
194                           [&](HasProperties node, const NodeInfo &info) {
195                             for (Property prop : info.Properties) {
196                               if (ignoredProperties.count(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(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 
261 private:
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 
278 void ASTPropsEmitter::Validator::validate() {
279   for (auto &entry : Emitter.NodeInfos) {
280     validateNode(entry.first, entry.second);
281   }
282 
283   if (ErrorsPrinted > 0) {
284     PrintFatalError("property validation failed");
285   }
286 }
287 
288 void ASTPropsEmitter::Validator::validateNode(HasProperties derivedNode,
289                                               const NodeInfo &derivedNodeInfo) {
290   if (!ValidatedNodes.insert(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                                 [&](HasProperties node,
297                                     const NodeInfo &nodeInfo) {
298     for (Property property : nodeInfo.Properties) {
299       validateType(property.getType(), property);
300 
301       auto result = allProperties.insert(
302                       std::make_pair(property.getName(), 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(existingProperty.getLoc(),
310                    "multiple properties named \"" + property.getName()
311                       + "\" in hierarchy of " + derivedNode.getName());
312         PrintNote(property.getLoc(), "existing property");
313       }
314     }
315   });
316 }
317 
318 void ASTPropsEmitter::Validator::validateType(PropertyType type,
319                                               WrappedRecord context) {
320   if (!type.isGenericSpecialization()) {
321     if (type.getCXXTypeName() == "") {
322       PrintError(type.getLoc(),
323                  "type is not generic but has no C++ type name");
324       if (context) PrintNote(context.getLoc(), "type used here");
325     }
326   } else if (auto eltType = type.getArrayElementType()) {
327     validateType(eltType, context);
328   } else if (auto valueType = type.getOptionalElementType()) {
329     validateType(valueType, context);
330 
331     if (valueType.getPackOptionalCode().empty()) {
332       PrintError(valueType.getLoc(),
333                  "type doesn't provide optional-packing code");
334       if (context) PrintNote(context.getLoc(), "type used here");
335     } else if (valueType.getUnpackOptionalCode().empty()) {
336       PrintError(valueType.getLoc(),
337                  "type doesn't provide optional-unpacking code");
338       if (context) PrintNote(context.getLoc(), "type used here");
339     }
340   } else {
341     PrintError(type.getLoc(), "unknown generic property type");
342     if (context) PrintNote(context.getLoc(), "type used here");
343   }
344 }
345 
346 /****************************************************************************/
347 /**************************** AST READER/WRITERS ****************************/
348 /****************************************************************************/
349 
350 template <class NodeClass>
351 void 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.
405 template <class NodeClass>
406 void 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 
423 void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node,
424                                                const ReaderWriterInfo &info) {
425   // Find the information for this node.
426   auto it = NodeInfos.find(node);
427   if (it == NodeInfos.end())
428     PrintFatalError(node.getLoc(),
429                     "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(node.getLoc(),
438                       "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(node, nodeInfo, [&](Property prop) {
451     // Verify that the creation code refers to this property.
452     if (info.IsReader && !creationCode.contains(prop.getName()))
453       PrintFatalError(nodeInfo.Creator.getLoc(),
454                       "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(info.HelperVariable, prop);
461     else
462       emitWriteOfProperty(info.HelperVariable, prop);
463   });
464 
465   // Emit the final creation code.
466   if (info.IsReader)
467     Out << "    " << creationCode << "\n";
468 }
469 
470 static 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(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(isForRead, out);
491       out << ">";
492     }
493   } else {
494     PrintFatalError(type.getLoc(), "unexpected generic property type");
495   }
496 }
497 
498 /// Emit code to read the given property in a node-reader method.
499 void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
500                                          Property property) {
501   emitReadOfProperty(readerName, property.getName(), property.getType(),
502                      property.getCondition());
503 }
504 
505 void 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*/ true, 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(true, 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, type, /*for read*/ 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.
554 void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
555                                           Property property) {
556   emitWriteOfProperty(writerName, property.getName(), property.getType(),
557                       property.getReadCode(), property.getCondition());
558 }
559 
560 void 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(false, Out);
574   Out << " " << name << " = (" << readCode << ");\n"
575          "    " << writerName << ".find(\"" << name << "\").write";
576   emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ 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.
586 template <class NodeClass>
587 static void emitASTReader(const RecordKeeper &records, raw_ostream &out,
588                           StringRef description) {
589   emitSourceFileHeader(description, out, records);
590 
591   ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
592 }
593 
594 void clang::EmitClangTypeReader(const RecordKeeper &records, raw_ostream &out) {
595   emitASTReader<TypeNode>(records, out, "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.
600 template <class NodeClass>
601 static void emitASTWriter(const RecordKeeper &records, raw_ostream &out,
602                           StringRef description) {
603   emitSourceFileHeader(description, out, records);
604 
605   ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
606 }
607 
608 void clang::EmitClangTypeWriter(const RecordKeeper &records, raw_ostream &out) {
609   emitASTWriter<TypeNode>(records, out, "A CRTP writer for Clang Type nodes");
610 }
611 
612 /****************************************************************************/
613 /*************************** BASIC READER/WRITERS ***************************/
614 /****************************************************************************/
615 
616 void
617 ASTPropsEmitter::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 
664 void
665 ASTPropsEmitter::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 
704 void
705 ASTPropsEmitter::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(type);
748     if (casedIter != CasedTypeInfos.end()) {
749       enterMethod("node");
750       emitCasedReaderWriterMethodBody(type, 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 
781 void ASTPropsEmitter::emitCasedReaderWriterMethodBody(PropertyType type,
782                                              const CasedTypeInfo &typeCases,
783                                              const ReaderWriterInfo &info) {
784   if (typeCases.Cases.empty()) {
785     assert(typeCases.KindRule);
786     PrintFatalError(typeCases.KindRule.getLoc(),
787                     "no cases found for \"" + type.getCXXTypeName() + "\"");
788   }
789   if (!typeCases.KindRule) {
790     assert(!typeCases.Cases.empty());
791     PrintFatalError(typeCases.Cases.front().getLoc(),
792                     "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(subvar, kindProperty, 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(subvar, kindProperty, kindType,
816                         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(typeCase, 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 
839 void 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.
847 void clang::EmitClangBasicReader(const RecordKeeper &records,
848                                  raw_ostream &out) {
849   emitSourceFileHeader("Helper classes for BasicReaders", out, 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.
858 void clang::EmitClangBasicWriter(const RecordKeeper &records,
859                                  raw_ostream &out) {
860   emitSourceFileHeader("Helper classes for BasicWriters", out, 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