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(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
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(entry.first, entry.second);
288  }
289
290  if (ErrorsPrinted > 0) {
291    PrintFatalError("property validation failed");
292  }
293}
294
295void 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
325void 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
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(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
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(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.
506void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
507                                         Property property) {
508  emitReadOfProperty(readerName, property.getName(), property.getType(),
509                     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*/ 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.
561void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
562                                          Property property) {
563  emitWriteOfProperty(writerName, property.getName(), property.getType(),
564                      property.getReadCode(), 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(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.
593template <class NodeClass>
594static void emitASTReader(RecordKeeper &records, raw_ostream &out,
595                          StringRef description) {
596  emitSourceFileHeader(description, out, records);
597
598  ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
599}
600
601void 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.
607template <class NodeClass>
608static void emitASTWriter(RecordKeeper &records, raw_ostream &out,
609                          StringRef description) {
610  emitSourceFileHeader(description, out, records);
611
612  ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
613}
614
615void 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
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(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
788void 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
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("Helper classes for BasicReaders", out, 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("Helper classes for BasicWriters", out, 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