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(x: property); |
106 | } |
107 | |
108 | // Find all the creation rules. |
109 | for (CreationRule creationRule : |
110 | records.getAllDerivedDefinitions(CreationRuleClassName)) { |
111 | HasProperties node = creationRule.getClass(); |
112 | |
113 | auto &info = NodeInfos[node]; |
114 | if (info.Creator) { |
115 | PrintFatalError(ErrorLoc: creationRule.getLoc(), |
116 | Msg: "multiple creator rules for \"" + node.getName() |
117 | + "\"" ); |
118 | } |
119 | info.Creator = creationRule; |
120 | } |
121 | |
122 | // Find all the override rules. |
123 | for (OverrideRule overrideRule : |
124 | records.getAllDerivedDefinitions(OverrideRuleClassName)) { |
125 | HasProperties node = overrideRule.getClass(); |
126 | |
127 | auto &info = NodeInfos[node]; |
128 | if (info.Override) { |
129 | PrintFatalError(ErrorLoc: overrideRule.getLoc(), |
130 | Msg: "multiple override rules for \"" + node.getName() |
131 | + "\"" ); |
132 | } |
133 | info.Override = overrideRule; |
134 | } |
135 | |
136 | // Find all the write helper rules. |
137 | for (ReadHelperRule helperRule : |
138 | records.getAllDerivedDefinitions(ReadHelperRuleClassName)) { |
139 | HasProperties node = helperRule.getClass(); |
140 | |
141 | auto &info = NodeInfos[node]; |
142 | if (info.ReadHelper) { |
143 | PrintFatalError(ErrorLoc: helperRule.getLoc(), |
144 | Msg: "multiple write helper rules for \"" + node.getName() |
145 | + "\"" ); |
146 | } |
147 | info.ReadHelper = helperRule; |
148 | } |
149 | |
150 | // Find all the concrete property types. |
151 | for (PropertyType type : |
152 | records.getAllDerivedDefinitions(PropertyTypeClassName)) { |
153 | // Ignore generic specializations; they're generally not useful when |
154 | // emitting basic emitters etc. |
155 | if (type.isGenericSpecialization()) continue; |
156 | |
157 | AllPropertyTypes.push_back(x: type); |
158 | } |
159 | |
160 | // Find all the type kind rules. |
161 | for (TypeKindRule kindRule : |
162 | records.getAllDerivedDefinitions(TypeKindClassName)) { |
163 | PropertyType type = kindRule.getParentType(); |
164 | auto &info = CasedTypeInfos[type]; |
165 | if (info.KindRule) { |
166 | PrintFatalError(ErrorLoc: kindRule.getLoc(), |
167 | Msg: "multiple kind rules for \"" |
168 | + type.getCXXTypeName() + "\"" ); |
169 | } |
170 | info.KindRule = kindRule; |
171 | } |
172 | |
173 | // Find all the type cases. |
174 | for (TypeCase typeCase : |
175 | records.getAllDerivedDefinitions(TypeCaseClassName)) { |
176 | CasedTypeInfos[typeCase.getParentType()].Cases.push_back(x: typeCase); |
177 | } |
178 | |
179 | Validator(*this).validate(); |
180 | } |
181 | |
182 | void visitAllProperties(HasProperties derived, const NodeInfo &derivedInfo, |
183 | function_ref<void (Property)> visit) { |
184 | std::set<StringRef> ignoredProperties; |
185 | |
186 | auto overrideRule = derivedInfo.Override; |
187 | if (overrideRule) { |
188 | auto list = overrideRule.getIgnoredProperties(); |
189 | ignoredProperties.insert(first: list.begin(), last: list.end()); |
190 | } |
191 | |
192 | // TODO: we should sort the properties in various ways |
193 | // - put arrays at the end to enable abbreviations |
194 | // - put conditional properties after properties used in the condition |
195 | |
196 | visitAllNodesWithInfo(derivedNode: derived, derivedNodeInfo: derivedInfo, |
197 | visit: [&](HasProperties node, const NodeInfo &info) { |
198 | for (Property prop : info.Properties) { |
199 | if (ignoredProperties.count(x: prop.getName())) |
200 | continue; |
201 | |
202 | visit(prop); |
203 | } |
204 | }); |
205 | } |
206 | |
207 | void visitAllNodesWithInfo(HasProperties derivedNode, |
208 | const NodeInfo &derivedNodeInfo, |
209 | llvm::function_ref<void (HasProperties node, |
210 | const NodeInfo &info)> |
211 | visit) { |
212 | visit(derivedNode, derivedNodeInfo); |
213 | |
214 | // Also walk the bases if appropriate. |
215 | if (ASTNode base = derivedNode.getAs<ASTNode>()) { |
216 | for (base = base.getBase(); base; base = base.getBase()) { |
217 | auto it = NodeInfos.find(x: base); |
218 | |
219 | // Ignore intermediate nodes that don't add interesting properties. |
220 | if (it == NodeInfos.end()) continue; |
221 | auto &baseInfo = it->second; |
222 | |
223 | visit(base, baseInfo); |
224 | } |
225 | } |
226 | } |
227 | |
228 | template <class NodeClass> |
229 | void emitNodeReaderClass() { |
230 | auto info = ReaderWriterInfo::forReader<NodeClass>(); |
231 | emitNodeReaderWriterClass<NodeClass>(info); |
232 | } |
233 | |
234 | template <class NodeClass> |
235 | void emitNodeWriterClass() { |
236 | auto info = ReaderWriterInfo::forWriter<NodeClass>(); |
237 | emitNodeReaderWriterClass<NodeClass>(info); |
238 | } |
239 | |
240 | template <class NodeClass> |
241 | void emitNodeReaderWriterClass(const ReaderWriterInfo &info); |
242 | |
243 | template <class NodeClass> |
244 | void emitNodeReaderWriterMethod(NodeClass node, |
245 | const ReaderWriterInfo &info); |
246 | |
247 | void emitPropertiedReaderWriterBody(HasProperties node, |
248 | const ReaderWriterInfo &info); |
249 | |
250 | void emitReadOfProperty(StringRef readerName, Property property); |
251 | void emitReadOfProperty(StringRef readerName, StringRef name, |
252 | PropertyType type, StringRef condition = "" ); |
253 | |
254 | void emitWriteOfProperty(StringRef writerName, Property property); |
255 | void emitWriteOfProperty(StringRef writerName, StringRef name, |
256 | PropertyType type, StringRef readCode, |
257 | StringRef condition = "" ); |
258 | |
259 | void emitBasicReaderWriterFile(const ReaderWriterInfo &info); |
260 | void emitDispatcherTemplate(const ReaderWriterInfo &info); |
261 | void emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info); |
262 | void emitBasicReaderWriterTemplate(const ReaderWriterInfo &info); |
263 | |
264 | void emitCasedReaderWriterMethodBody(PropertyType type, |
265 | const CasedTypeInfo &typeCases, |
266 | const ReaderWriterInfo &info); |
267 | |
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(node: entry.first, nodeInfo: entry.second); |
288 | } |
289 | |
290 | if (ErrorsPrinted > 0) { |
291 | PrintFatalError(Msg: "property validation failed" ); |
292 | } |
293 | } |
294 | |
295 | void ASTPropsEmitter::Validator::validateNode(HasProperties derivedNode, |
296 | const NodeInfo &derivedNodeInfo) { |
297 | if (!ValidatedNodes.insert(x: derivedNode).second) return; |
298 | |
299 | // A map from property name to property. |
300 | std::map<StringRef, Property> allProperties; |
301 | |
302 | Emitter.visitAllNodesWithInfo(derivedNode, derivedNodeInfo, |
303 | visit: [&](HasProperties node, |
304 | const NodeInfo &nodeInfo) { |
305 | for (Property property : nodeInfo.Properties) { |
306 | validateType(type: property.getType(), context: property); |
307 | |
308 | auto result = allProperties.insert( |
309 | x: std::make_pair(x: property.getName(), y&: property)); |
310 | |
311 | // Diagnose non-unique properties. |
312 | if (!result.second) { |
313 | // The existing property is more likely to be associated with a |
314 | // derived node, so use it as the error. |
315 | Property existingProperty = result.first->second; |
316 | PrintError(ErrorLoc: existingProperty.getLoc(), |
317 | Msg: "multiple properties named \"" + property.getName() |
318 | + "\" in hierarchy of " + derivedNode.getName()); |
319 | PrintNote(NoteLoc: property.getLoc(), Msg: "existing property" ); |
320 | } |
321 | } |
322 | }); |
323 | } |
324 | |
325 | void ASTPropsEmitter::Validator::validateType(PropertyType type, |
326 | WrappedRecord context) { |
327 | if (!type.isGenericSpecialization()) { |
328 | if (type.getCXXTypeName() == "" ) { |
329 | PrintError(ErrorLoc: type.getLoc(), |
330 | Msg: "type is not generic but has no C++ type name" ); |
331 | if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here" ); |
332 | } |
333 | } else if (auto eltType = type.getArrayElementType()) { |
334 | validateType(type: eltType, context); |
335 | } else if (auto valueType = type.getOptionalElementType()) { |
336 | validateType(type: valueType, context); |
337 | |
338 | if (valueType.getPackOptionalCode().empty()) { |
339 | PrintError(ErrorLoc: valueType.getLoc(), |
340 | Msg: "type doesn't provide optional-packing code" ); |
341 | if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here" ); |
342 | } else if (valueType.getUnpackOptionalCode().empty()) { |
343 | PrintError(ErrorLoc: valueType.getLoc(), |
344 | Msg: "type doesn't provide optional-unpacking code" ); |
345 | if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here" ); |
346 | } |
347 | } else { |
348 | PrintError(ErrorLoc: type.getLoc(), Msg: "unknown generic property type" ); |
349 | if (context) PrintNote(NoteLoc: context.getLoc(), Msg: "type used here" ); |
350 | } |
351 | } |
352 | |
353 | /****************************************************************************/ |
354 | /**************************** AST READER/WRITERS ****************************/ |
355 | /****************************************************************************/ |
356 | |
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(x: node); |
434 | if (it == NodeInfos.end()) |
435 | PrintFatalError(ErrorLoc: node.getLoc(), |
436 | Msg: "no information about how to deserialize \"" |
437 | + node.getName() + "\"" ); |
438 | auto &nodeInfo = it->second; |
439 | |
440 | StringRef creationCode; |
441 | if (info.IsReader) { |
442 | // We should have a creation rule. |
443 | if (!nodeInfo.Creator) |
444 | PrintFatalError(ErrorLoc: node.getLoc(), |
445 | Msg: "no " CreationRuleClassName " for \"" |
446 | + node.getName() + "\"" ); |
447 | |
448 | creationCode = nodeInfo.Creator.getCreationCode(); |
449 | } |
450 | |
451 | // Emit the ReadHelper code, if present. |
452 | if (!info.IsReader && nodeInfo.ReadHelper) { |
453 | Out << " " << nodeInfo.ReadHelper.getHelperCode() << "\n" ; |
454 | } |
455 | |
456 | // Emit code to read all the properties. |
457 | visitAllProperties(derived: node, derivedInfo: nodeInfo, visit: [&](Property prop) { |
458 | // Verify that the creation code refers to this property. |
459 | if (info.IsReader && !creationCode.contains(Other: prop.getName())) |
460 | PrintFatalError(ErrorLoc: nodeInfo.Creator.getLoc(), |
461 | Msg: "creation code for " + node.getName() |
462 | + " doesn't refer to property \"" |
463 | + prop.getName() + "\"" ); |
464 | |
465 | // Emit code to read or write this property. |
466 | if (info.IsReader) |
467 | emitReadOfProperty(readerName: info.HelperVariable, property: prop); |
468 | else |
469 | emitWriteOfProperty(writerName: info.HelperVariable, property: prop); |
470 | }); |
471 | |
472 | // Emit the final creation code. |
473 | if (info.IsReader) |
474 | Out << " " << creationCode << "\n" ; |
475 | } |
476 | |
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(forRead: isForRead, out); |
489 | out << ">" ; |
490 | } |
491 | } else if (auto valueType = type.getOptionalElementType()) { |
492 | out << "Optional" ; |
493 | // We only include an explicit template argument for reads so that |
494 | // we don't cause spurious const mismatches. |
495 | if (isForRead) { |
496 | out << "<" ; |
497 | valueType.emitCXXValueTypeName(forRead: isForRead, out); |
498 | out << ">" ; |
499 | } |
500 | } else { |
501 | PrintFatalError(ErrorLoc: type.getLoc(), Msg: "unexpected generic property type" ); |
502 | } |
503 | } |
504 | |
505 | /// Emit code to read the given property in a node-reader method. |
506 | void ASTPropsEmitter::emitReadOfProperty(StringRef readerName, |
507 | Property property) { |
508 | emitReadOfProperty(readerName, name: property.getName(), type: property.getType(), |
509 | condition: 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*/ forRead: true, out&: Out); |
521 | Out << ", 8> " << name << "_buffer_" << i << ";\n" ; |
522 | } |
523 | |
524 | // T prop = R.find("prop").read##ValueType(buffers...); |
525 | // We intentionally ignore shouldPassByReference here: we're going to |
526 | // get a pr-value back from read(), and we should be able to forward |
527 | // that in the creation rule. |
528 | Out << " " ; |
529 | if (!condition.empty()) |
530 | Out << "std::optional<" ; |
531 | type.emitCXXValueTypeName(forRead: true, out&: Out); |
532 | if (!condition.empty()) Out << ">" ; |
533 | Out << " " << name; |
534 | |
535 | if (condition.empty()) { |
536 | Out << " = " ; |
537 | } else { |
538 | Out << ";\n" |
539 | " if (" << condition << ") {\n" |
540 | " " << name << ".emplace(" ; |
541 | } |
542 | |
543 | Out << readerName << ".find(\"" << name << "\")." |
544 | << (type.isGenericSpecialization() ? "template " : "" ) << "read" ; |
545 | emitBasicReaderWriterMethodSuffix(out&: Out, type, /*for read*/ isForRead: true); |
546 | Out << "(" ; |
547 | for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) { |
548 | Out << (i > 0 ? ", " : "" ) << name << "_buffer_" << i; |
549 | } |
550 | Out << ")" ; |
551 | |
552 | if (condition.empty()) { |
553 | Out << ";\n" ; |
554 | } else { |
555 | Out << ");\n" |
556 | " }\n" ; |
557 | } |
558 | } |
559 | |
560 | /// Emit code to write the given property in a node-writer method. |
561 | void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName, |
562 | Property property) { |
563 | emitWriteOfProperty(writerName, name: property.getName(), type: property.getType(), |
564 | readCode: property.getReadCode(), condition: property.getCondition()); |
565 | } |
566 | |
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(forRead: false, out&: Out); |
581 | Out << " " << name << " = (" << readCode << ");\n" |
582 | " " << writerName << ".find(\"" << name << "\").write" ; |
583 | emitBasicReaderWriterMethodSuffix(out&: Out, type, /*for read*/ isForRead: false); |
584 | Out << "(" << name << ");\n" ; |
585 | |
586 | if (!condition.empty()) { |
587 | Out << " }\n" ; |
588 | } |
589 | } |
590 | |
591 | /// Emit an .inc file that defines the AbstractFooReader class |
592 | /// for the given AST class hierarchy. |
593 | template <class NodeClass> |
594 | static void emitASTReader(RecordKeeper &records, raw_ostream &out, |
595 | StringRef description) { |
596 | emitSourceFileHeader(Desc: description, OS&: out, Record: records); |
597 | |
598 | ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>(); |
599 | } |
600 | |
601 | void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) { |
602 | emitASTReader<TypeNode>(records, out, description: "A CRTP reader for Clang Type nodes" ); |
603 | } |
604 | |
605 | /// Emit an .inc file that defines the AbstractFooWriter class |
606 | /// for the given AST class hierarchy. |
607 | template <class NodeClass> |
608 | static void emitASTWriter(RecordKeeper &records, raw_ostream &out, |
609 | StringRef description) { |
610 | emitSourceFileHeader(Desc: description, OS&: out, Record: records); |
611 | |
612 | ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>(); |
613 | } |
614 | |
615 | void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) { |
616 | emitASTWriter<TypeNode>(records, out, description: "A CRTP writer for Clang Type nodes" ); |
617 | } |
618 | |
619 | /****************************************************************************/ |
620 | /*************************** BASIC READER/WRITERS ***************************/ |
621 | /****************************************************************************/ |
622 | |
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(x: type); |
755 | if (casedIter != CasedTypeInfos.end()) { |
756 | enterMethod("node" ); |
757 | emitCasedReaderWriterMethodBody(type, typeCases: casedIter->second, info); |
758 | exitMethod(); |
759 | |
760 | } else if (type.isEnum()) { |
761 | enterMethod("value" ); |
762 | if (info.IsReader) |
763 | Out << " return asImpl().template readEnum<" |
764 | << type.getCXXTypeName() << ">();\n" ; |
765 | else |
766 | Out << " asImpl().writeEnum(value);\n" ; |
767 | exitMethod(); |
768 | |
769 | } else if (PropertyType superclass = type.getSuperclassType()) { |
770 | enterMethod("value" ); |
771 | if (info.IsReader) |
772 | Out << " return cast_or_null<" << type.getSubclassClassName() |
773 | << ">(asImpl().read" |
774 | << superclass.getAbstractTypeName() |
775 | << "());\n" ; |
776 | else |
777 | Out << " asImpl().write" << superclass.getAbstractTypeName() |
778 | << "(value);\n" ; |
779 | exitMethod(); |
780 | |
781 | } else { |
782 | // The other types can't be handled as trivially. |
783 | } |
784 | } |
785 | Out << "};\n\n" ; |
786 | } |
787 | |
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(ErrorLoc: typeCases.KindRule.getLoc(), |
794 | Msg: "no cases found for \"" + type.getCXXTypeName() + "\"" ); |
795 | } |
796 | if (!typeCases.KindRule) { |
797 | assert(!typeCases.Cases.empty()); |
798 | PrintFatalError(ErrorLoc: typeCases.Cases.front().getLoc(), |
799 | Msg: "no kind rule for \"" + type.getCXXTypeName() + "\"" ); |
800 | } |
801 | |
802 | auto var = info.HelperVariable; |
803 | std::string subvar = ("sub" + var).str(); |
804 | |
805 | // Bind `ctx` for readers. |
806 | if (info.IsReader) |
807 | Out << " auto &ctx = asImpl().getASTContext();\n" ; |
808 | |
809 | // Start an object. |
810 | Out << " auto &&" << subvar << " = asImpl()." |
811 | << info.MethodPrefix << "Object();\n" ; |
812 | |
813 | // Read/write the kind property; |
814 | TypeKindRule kindRule = typeCases.KindRule; |
815 | StringRef kindProperty = kindRule.getKindPropertyName(); |
816 | PropertyType kindType = kindRule.getKindType(); |
817 | if (info.IsReader) { |
818 | emitReadOfProperty(readerName: subvar, name: kindProperty, type: kindType); |
819 | } else { |
820 | // Write the property. Note that this will implicitly read the |
821 | // kind into a local variable with the right name. |
822 | emitWriteOfProperty(writerName: subvar, name: kindProperty, type: kindType, |
823 | readCode: kindRule.getReadCode()); |
824 | } |
825 | |
826 | // Prepare a ReaderWriterInfo with a helper variable that will use |
827 | // the sub-reader/writer. |
828 | ReaderWriterInfo subInfo = info; |
829 | subInfo.HelperVariable = subvar; |
830 | |
831 | // Switch on the kind. |
832 | Out << " switch (" << kindProperty << ") {\n" ; |
833 | for (TypeCase typeCase : typeCases.Cases) { |
834 | Out << " case " << type.getCXXTypeName() << "::" |
835 | << typeCase.getCaseName() << ": {\n" ; |
836 | emitPropertiedReaderWriterBody(node: typeCase, info: subInfo); |
837 | if (!info.IsReader) |
838 | Out << " return;\n" ; |
839 | Out << " }\n\n" ; |
840 | } |
841 | Out << " }\n" |
842 | " llvm_unreachable(\"bad " << kindType.getCXXTypeName() |
843 | << "\");\n" ; |
844 | } |
845 | |
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(Desc: "Helper classes for BasicReaders" , OS&: out, Record: records); |
856 | |
857 | // Use any property, we won't be using those properties. |
858 | auto info = ReaderWriterInfo::forReader<TypeNode>(); |
859 | ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info); |
860 | } |
861 | |
862 | /// Emit an .inc file that defines some helper classes for writing |
863 | /// basic values. |
864 | void clang::EmitClangBasicWriter(RecordKeeper &records, raw_ostream &out) { |
865 | emitSourceFileHeader(Desc: "Helper classes for BasicWriters" , OS&: out, Record: records); |
866 | |
867 | // Use any property, we won't be using those properties. |
868 | auto info = ReaderWriterInfo::forWriter<TypeNode>(); |
869 | ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info); |
870 | } |
871 | |