1 /**
2 * This module contains the implementation of the C++ header generation available through
3 * the command line switch -Hc.
4 *
5 * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
6 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
7 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtohd, _dtoh.d)
9 * Documentation: https://dlang.org/phobos/dmd_dtoh.html
10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtoh.d
11 */
12 module dmd.dtoh;
13
14 import core.stdc.stdio;
15 import core.stdc.string;
16 import core.stdc.ctype;
17
18 import dmd.astcodegen;
19 import dmd.astenums;
20 import dmd.arraytypes;
21 import dmd.attrib;
22 import dmd.dsymbol;
23 import dmd.errors;
24 import dmd.globals;
25 import dmd.hdrgen;
26 import dmd.identifier;
27 import dmd.root.filename;
28 import dmd.visitor;
29 import dmd.tokens;
30
31 import dmd.common.outbuffer;
32 import dmd.utils;
33
34 //debug = Debug_DtoH;
35
36 // Generate asserts to validate the header
37 //debug = Debug_DtoH_Checks;
38
39 /**
40 * Generates a C++ header containing bindings for all `extern(C[++])` declarations
41 * found in the supplied modules.
42 *
43 * Params:
44 * ms = the modules
45 *
46 * Notes:
47 * - the header is written to `<global.params.cxxhdrdir>/<global.params.cxxhdrfile>`
48 * or `stdout` if no explicit file was specified
49 * - bindings conform to the C++ standard defined in `global.params.cplusplus`
50 * - ignored declarations are mentioned in a comment if `global.params.doCxxHdrGeneration`
51 * is set to `CxxHeaderMode.verbose`
52 */
genCppHdrFiles(ref Modules ms)53 extern(C++) void genCppHdrFiles(ref Modules ms)
54 {
55 initialize();
56
57 OutBuffer fwd;
58 OutBuffer done;
59 OutBuffer decl;
60
61 // enable indent by spaces on buffers
62 fwd.doindent = true;
63 fwd.spaces = true;
64 decl.doindent = true;
65 decl.spaces = true;
66
67 scope v = new ToCppBuffer(&fwd, &done, &decl);
68
69 // Conditionally include another buffer for sanity checks
70 debug (Debug_DtoH_Checks)
71 {
72 OutBuffer check;
73 check.doindent = true;
74 check.spaces = true;
75 v.checkbuf = ✓
76 }
77
78 OutBuffer buf;
79 buf.doindent = true;
80 buf.spaces = true;
81
82 foreach (m; ms)
83 m.accept(v);
84
85 if (global.params.doCxxHdrGeneration == CxxHeaderMode.verbose)
86 buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber());
87 else
88 buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr);
89
90 buf.writenl();
91 buf.writenl();
92 buf.writestringln("#pragma once");
93 buf.writenl();
94 hashInclude(buf, "<assert.h>");
95 hashInclude(buf, "<stddef.h>");
96 hashInclude(buf, "<stdint.h>");
97 hashInclude(buf, "<math.h>");
98 // buf.writestring(buf, "#include <stdio.h>\n");
99 // buf.writestring("#include <string.h>\n");
100
101 // Emit array compatibility because extern(C++) types may have slices
102 // as members (as opposed to function parameters)
103 buf.writestring(`
104 #ifdef CUSTOM_D_ARRAY_TYPE
105 #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
106 #else
107 /// Represents a D [] array
108 template<typename T>
109 struct _d_dynamicArray final
110 {
111 size_t length;
112 T *ptr;
113
114 _d_dynamicArray() : length(0), ptr(NULL) { }
115
116 _d_dynamicArray(size_t length_in, T *ptr_in)
117 : length(length_in), ptr(ptr_in) { }
118
119 T& operator[](const size_t idx) {
120 assert(idx < length);
121 return ptr[idx];
122 }
123
124 const T& operator[](const size_t idx) const {
125 assert(idx < length);
126 return ptr[idx];
127 }
128 };
129 #endif
130 `);
131
132 if (v.hasReal)
133 {
134 hashIf(buf, "!defined(_d_real)");
135 {
136 hashDefine(buf, "_d_real long double");
137 }
138 hashEndIf(buf);
139 }
140 buf.writenl();
141 // buf.writestringln("// fwd:");
142 buf.write(&fwd);
143 if (fwd.length > 0)
144 buf.writenl();
145
146 // buf.writestringln("// done:");
147 buf.write(&done);
148
149 // buf.writestringln("// decl:");
150 buf.write(&decl);
151
152 debug (Debug_DtoH_Checks)
153 {
154 // buf.writestringln("// check:");
155 buf.writestring(`
156 #if OFFSETS
157 template <class T>
158 size_t getSlotNumber(int dummy, ...)
159 {
160 T c;
161 va_list ap;
162 va_start(ap, dummy);
163
164 void *f = va_arg(ap, void*);
165 for (size_t i = 0; ; i++)
166 {
167 if ( (*(void***)&c)[i] == f)
168 return i;
169 }
170 va_end(ap);
171 }
172
173 void testOffsets()
174 {
175 `);
176 buf.write(&check);
177 buf.writestring(`
178 }
179 #endif
180 `);
181 }
182
183 // prevent trailing newlines
184 version (Windows)
185 while (buf.length >= 4 && buf[$-4..$] == "\r\n\r\n")
186 buf.remove(buf.length - 2, 2);
187 else
188 while (buf.length >= 2 && buf[$-2..$] == "\n\n")
189 buf.remove(buf.length - 1, 1);
190
191
192 if (global.params.cxxhdrname is null)
193 {
194 // Write to stdout; assume it succeeds
195 size_t n = fwrite(buf[].ptr, 1, buf.length, stdout);
196 assert(n == buf.length); // keep gcc happy about return values
197 }
198 else
199 {
200 const(char)[] name = FileName.combine(global.params.cxxhdrdir, global.params.cxxhdrname);
201 writeFile(Loc.initial, name, buf[]);
202 }
203 }
204
205 private:
206
207 /****************************************************
208 * Visitor that writes bindings for `extern(C[++]` declarations.
209 */
210 extern(C++) final class ToCppBuffer : Visitor
211 {
212 alias visit = Visitor.visit;
213 public:
214 enum EnumKind
215 {
216 Int,
217 Numeric,
218 String,
219 Enum,
220 Other
221 }
222
223 /// Namespace providing the actual AST nodes
224 alias AST = ASTCodegen;
225
226 /// Visited nodes
227 bool[void*] visited;
228
229 /// Forward declared nodes (which might not be emitted yet)
230 bool[void*] forwarded;
231
232 /// Buffer for forward declarations
233 OutBuffer* fwdbuf;
234
235 /// Buffer for integrity checks
236 debug (Debug_DtoH_Checks) OutBuffer* checkbuf;
237
238 /// Buffer for declarations that must emitted before the currently
239 /// visited node but can't be forward declared (see `includeSymbol`)
240 OutBuffer* donebuf;
241
242 /// Default buffer for the currently visited declaration
243 OutBuffer* buf;
244
245 /// The generated header uses `real` emitted as `_d_real`?
246 bool hasReal;
247
248 /// The generated header should contain comments for skipped declarations?
249 const bool printIgnored;
250
251 /// State specific to the current context which depends
252 /// on the currently visited node and it's parents
253 static struct Context
254 {
255 /// Default linkage in the current scope (e.g. LINK.c inside `extern(C) { ... }`)
256 LINK linkage = LINK.d;
257
258 /// Enclosing class / struct / union
259 AST.AggregateDeclaration adparent;
260
261 /// Enclosing template declaration
262 AST.TemplateDeclaration tdparent;
263
264 /// Identifier of the currently visited `VarDeclaration`
265 /// (required to write variables of funtion pointers)
266 Identifier ident;
267
268 /// Original type of the currently visited declaration
269 AST.Type origType;
270
271 /// Last written visibility level applying to the current scope
272 AST.Visibility.Kind currentVisibility;
273
274 /// Currently applicable storage classes
275 AST.STC storageClass;
276
277 /// How many symbols were ignored
278 int ignoredCounter;
279
280 /// Currently visited types are required by another declaration
281 /// and hence must be emitted
282 bool mustEmit;
283
284 /// Processing a type that can be forward referenced
285 bool forwarding;
286
287 /// Inside of an anonymous struct/union (AnonDeclaration)
288 bool inAnonymousDecl;
289 }
290
291 /// Informations about the current context in the AST
292 Context context;
293 alias context this;
294
295 this(OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf)
296 {
297 this.fwdbuf = fwdbuf;
298 this.donebuf = donebuf;
299 this.buf = buf;
300 this.printIgnored = global.params.doCxxHdrGeneration == CxxHeaderMode.verbose;
301 }
302
303 /**
304 * Emits `dsym` into `donebuf` s.t. it is declared before the currently
305 * visited symbol that written to `buf`.
306 *
307 * Temporarily clears `context` to behave as if it was visited normally.
308 */
309 private void includeSymbol(AST.Dsymbol dsym)
310 {
311 debug (Debug_DtoH)
312 {
313 printf("[includeSymbol(AST.Dsymbol) enter] %s\n", dsym.toChars());
314 scope(exit) printf("[includeSymbol(AST.Dsymbol) exit] %s\n", dsym.toChars());
315 }
316
317 auto ptr = cast(void*) dsym in visited;
318 if (ptr && *ptr)
319 return;
320
321 // Temporary replacement for `buf` which is appended to `donebuf`
322 OutBuffer decl;
323 decl.doindent = true;
324 decl.spaces = true;
325 scope (exit) donebuf.write(&decl);
326
327 auto ctxStash = this.context;
328 auto bufStash = this.buf;
329
330 this.context = Context.init;
331 this.buf = &decl;
332 this.mustEmit = true;
333
334 dsym.accept(this);
335
336 this.context = ctxStash;
337 this.buf = bufStash;
338 }
339
340 /// Determines what kind of enum `type` is (see `EnumKind`)
341 private EnumKind getEnumKind(AST.Type type)
342 {
343 if (type) switch (type.ty)
344 {
345 case AST.Tint32:
346 return EnumKind.Int;
347 case AST.Tbool,
348 AST.Tchar, AST.Twchar, AST.Tdchar,
349 AST.Tint8, AST.Tuns8,
350 AST.Tint16, AST.Tuns16,
351 AST.Tuns32,
352 AST.Tint64, AST.Tuns64:
353 return EnumKind.Numeric;
354 case AST.Tarray:
355 if (type.isString())
356 return EnumKind.String;
357 break;
358 case AST.Tenum:
359 return EnumKind.Enum;
360 default:
361 break;
362 }
363 return EnumKind.Other;
364 }
365
366 /// Determines the type used to represent `type` in C++.
367 /// Returns: `const [w,d]char*` for `[w,d]string` or `type`
368 private AST.Type determineEnumType(AST.Type type)
369 {
370 if (auto arr = type.isTypeDArray())
371 {
372 switch (arr.next.ty)
373 {
374 case AST.Tchar: return AST.Type.tchar.constOf.pointerTo;
375 case AST.Twchar: return AST.Type.twchar.constOf.pointerTo;
376 case AST.Tdchar: return AST.Type.tdchar.constOf.pointerTo;
377 default: break;
378 }
379 }
380 return type;
381 }
382
383 /// Writes a final `;` and insert an empty line outside of aggregates
384 private void writeDeclEnd()
385 {
386 buf.writestringln(";");
387
388 if (!adparent)
389 buf.writenl();
390 }
391
392 /// Writes the corresponding access specifier if necessary
393 private void writeProtection(const AST.Visibility.Kind kind)
394 {
395 // Don't write visibility for global declarations
396 if (!adparent || inAnonymousDecl)
397 return;
398
399 string token;
400
401 switch(kind) with(AST.Visibility.Kind)
402 {
403 case none, private_:
404 if (this.currentVisibility == AST.Visibility.Kind.private_)
405 return;
406 this.currentVisibility = AST.Visibility.Kind.private_;
407 token = "private:";
408 break;
409
410 case package_, protected_:
411 if (this.currentVisibility == AST.Visibility.Kind.protected_)
412 return;
413 this.currentVisibility = AST.Visibility.Kind.protected_;
414 token = "protected:";
415 break;
416
417 case undefined, public_, export_:
418 if (this.currentVisibility == AST.Visibility.Kind.public_)
419 return;
420 this.currentVisibility = AST.Visibility.Kind.public_;
421 token = "public:";
422 break;
423
424 default:
425 printf("Unexpected visibility: %d!\n", kind);
426 assert(0);
427 }
428
429 buf.level--;
430 buf.writestringln(token);
431 buf.level++;
432 }
433
434 /**
435 * Writes an identifier into `buf` and checks for reserved identifiers. The
436 * parameter `canFix` determines how this function handles C++ keywords:
437 *
438 * `false` => Raise a warning and print the identifier as-is
439 * `true` => Append an underscore to the identifier
440 *
441 * Params:
442 * s = the symbol denoting the identifier
443 * canFixup = whether the identifier may be changed without affecting
444 * binary compatibility
445 */
446 private void writeIdentifier(const AST.Dsymbol s, const bool canFix = false)
447 {
448 if (const mn = getMangleOverride(s))
449 return buf.writestring(mn);
450
451 writeIdentifier(s.ident, s.loc, s.kind(), canFix);
452 }
453
454 /** Overload of `writeIdentifier` used for all AST nodes not descending from Dsymbol **/
455 private void writeIdentifier(const Identifier ident, const Loc loc, const char* kind, const bool canFix = false)
456 {
457 bool needsFix;
458
459 void warnCxxCompat(const(char)* reason)
460 {
461 if (canFix)
462 {
463 needsFix = true;
464 return;
465 }
466
467 __gshared bool warned = false;
468 warning(loc, "%s `%s` is a %s", kind, ident.toChars(), reason);
469
470 if (!warned)
471 {
472 warningSupplemental(loc, "The generated C++ header will contain " ~
473 "identifiers that are keywords in C++");
474 warned = true;
475 }
476 }
477
478 if (global.params.warnings != DiagnosticReporting.off || canFix)
479 {
480 // Warn about identifiers that are keywords in C++.
481 if (auto kc = keywordClass(ident))
482 warnCxxCompat(kc);
483 }
484 buf.writestring(ident.toString());
485 if (needsFix)
486 buf.writeByte('_');
487 }
488
489 /// Checks whether `t` is a type that can be exported to C++
490 private bool isSupportedType(AST.Type t)
491 {
492 if (!t)
493 {
494 assert(tdparent);
495 return true;
496 }
497
498 switch (t.ty)
499 {
500 // Nested types
501 case AST.Tarray:
502 case AST.Tsarray:
503 case AST.Tpointer:
504 case AST.Treference:
505 case AST.Tdelegate:
506 return isSupportedType((cast(AST.TypeNext) t).next);
507
508 // Function pointers
509 case AST.Tfunction:
510 {
511 auto tf = cast(AST.TypeFunction) t;
512 if (!isSupportedType(tf.next))
513 return false;
514 foreach (_, param; tf.parameterList)
515 {
516 if (!isSupportedType(param.type))
517 return false;
518 }
519 return true;
520 }
521
522 // Noreturn has a different mangling
523 case AST.Tnoreturn:
524
525 // _Imaginary is C only.
526 case AST.Timaginary32:
527 case AST.Timaginary64:
528 case AST.Timaginary80:
529 return false;
530 default:
531 return true;
532 }
533 }
534
535 override void visit(AST.Dsymbol s)
536 {
537 debug (Debug_DtoH)
538 {
539 mixin(traceVisit!s);
540 import dmd.asttypename;
541 printf("[AST.Dsymbol enter] %s\n", s.astTypeName().ptr);
542 }
543 }
544
545 override void visit(AST.Import i)
546 {
547 debug (Debug_DtoH) mixin(traceVisit!i);
548
549 /// Writes `using <alias_> = <sym.ident>` into `buf`
550 const(char*) writeImport(AST.Dsymbol sym, const Identifier alias_)
551 {
552 /// `using` was introduced in C++ 11 and only works for types...
553 if (global.params.cplusplus < CppStdRevision.cpp11)
554 return "requires C++11";
555
556 if (auto ad = sym.isAliasDeclaration())
557 {
558 sym = ad.toAlias();
559 ad = sym.isAliasDeclaration();
560
561 // Might be an alias to a basic type
562 if (ad && !ad.aliassym && ad.type)
563 goto Emit;
564 }
565
566 // Restricted to types and other aliases
567 if (!sym.isScopeDsymbol() && !sym.isAggregateDeclaration())
568 return "only supports types";
569
570 // Write `using <alias_> = `<sym>`
571 Emit:
572 buf.writestring("using ");
573 writeIdentifier(alias_, i.loc, "renamed import");
574 buf.writestring(" = ");
575 // Start at module scope to avoid collisions with local symbols
576 if (this.context.adparent)
577 buf.writestring("::");
578 buf.writestring(sym.ident.toString());
579 writeDeclEnd();
580 return null;
581 }
582
583 // Only missing without semantic analysis
584 // FIXME: Templates need work due to missing parent & imported module
585 if (!i.parent)
586 {
587 assert(tdparent);
588 ignored("`%s` because it's inside of a template declaration", i.toChars());
589 return;
590 }
591
592 // Non-public imports don't create new symbols, include as needed
593 if (i.visibility.kind < AST.Visibility.Kind.public_)
594 return;
595
596 // Symbols from static imports should be emitted inline
597 if (i.isstatic)
598 return;
599
600 const isLocal = !i.parent.isModule();
601
602 // Need module for symbol lookup
603 assert(i.mod);
604
605 // Emit an alias for each public module member
606 if (isLocal && i.names.length == 0)
607 {
608 assert(i.mod.symtab);
609
610 // Sort alphabetically s.t. slight changes in semantic don't cause
611 // massive changes in the order of declarations
612 AST.Dsymbols entries;
613 entries.reserve(i.mod.symtab.length);
614
615 foreach (entry; i.mod.symtab.tab.asRange)
616 {
617 // Skip anonymous / invisible members
618 import dmd.access : symbolIsVisible;
619 if (!entry.key.isAnonymous() && symbolIsVisible(i, entry.value))
620 entries.push(entry.value);
621 }
622
623 // Seperate function because of a spurious dual-context deprecation
624 static int compare(const AST.Dsymbol* a, const AST.Dsymbol* b)
625 {
626 return strcmp(a.ident.toChars(), b.ident.toChars());
627 }
628 entries.sort!compare();
629
630 foreach (sym; entries)
631 {
632 includeSymbol(sym);
633 if (auto err = writeImport(sym, sym.ident))
634 ignored("public import for `%s` because `using` %s", sym.ident.toChars(), err);
635 }
636 return;
637 }
638
639 // Include all public imports and emit using declarations for each alias
640 foreach (const idx, name; i.names)
641 {
642 // Search the imported symbol
643 auto sym = i.mod.search(Loc.initial, name);
644 assert(sym); // Missing imports should error during semantic
645
646 includeSymbol(sym);
647
648 // Detect the assigned name for renamed import
649 auto alias_ = i.aliases[idx];
650 if (!alias_)
651 continue;
652
653 if (auto err = writeImport(sym, alias_))
654 ignored("renamed import `%s = %s` because `using` %s", alias_.toChars(), name.toChars(), err);
655 }
656 }
657
658 override void visit(AST.AttribDeclaration pd)
659 {
660 debug (Debug_DtoH) mixin(traceVisit!pd);
661
662 Dsymbols* decl = pd.include(null);
663 if (!decl)
664 return;
665
666 foreach (s; *decl)
667 {
668 if (adparent || s.visible().kind >= AST.Visibility.Kind.public_)
669 s.accept(this);
670 }
671 }
672
673 override void visit(AST.StorageClassDeclaration scd)
674 {
675 debug (Debug_DtoH) mixin(traceVisit!scd);
676
677 const stcStash = this.storageClass;
678 this.storageClass |= scd.stc;
679 visit(cast(AST.AttribDeclaration) scd);
680 this.storageClass = stcStash;
681 }
682
683 override void visit(AST.LinkDeclaration ld)
684 {
685 debug (Debug_DtoH) mixin(traceVisit!ld);
686
687 auto save = linkage;
688 linkage = ld.linkage;
689 visit(cast(AST.AttribDeclaration)ld);
690 linkage = save;
691 }
692
693 override void visit(AST.CPPMangleDeclaration md)
694 {
695 debug (Debug_DtoH) mixin(traceVisit!md);
696
697 const oldLinkage = this.linkage;
698 this.linkage = LINK.cpp;
699 visit(cast(AST.AttribDeclaration) md);
700 this.linkage = oldLinkage;
701 }
702
703 override void visit(AST.Module m)
704 {
705 debug (Debug_DtoH) mixin(traceVisit!m);
706
707 foreach (s; *m.members)
708 {
709 if (s.visible().kind < AST.Visibility.Kind.public_)
710 continue;
711 s.accept(this);
712 }
713 }
714
715 override void visit(AST.FuncDeclaration fd)
716 {
717 debug (Debug_DtoH) mixin(traceVisit!fd);
718
719 if (cast(void*)fd in visited)
720 return;
721 // printf("FuncDeclaration %s %s\n", fd.toPrettyChars(), fd.type.toChars());
722 visited[cast(void*)fd] = true;
723
724 // silently ignore non-user-defined destructors
725 if (fd.isGenerated && fd.isDtorDeclaration())
726 return;
727
728 // Note that tf might be null for templated (member) functions
729 auto tf = cast(AST.TypeFunction)fd.type;
730 if ((tf && (tf.linkage != LINK.c || adparent) && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration()))
731 {
732 ignored("function %s because of linkage", fd.toPrettyChars());
733 return checkFunctionNeedsPlaceholder(fd);
734 }
735 if (fd.mangleOverride && tf && tf.linkage != LINK.c)
736 {
737 ignored("function %s because C++ doesn't support explicit mangling", fd.toPrettyChars());
738 return checkFunctionNeedsPlaceholder(fd);
739 }
740 if (!adparent && !fd.fbody)
741 {
742 ignored("function %s because it is extern", fd.toPrettyChars());
743 return;
744 }
745 if (fd.visibility.kind == AST.Visibility.Kind.none || fd.visibility.kind == AST.Visibility.Kind.private_)
746 {
747 ignored("function %s because it is private", fd.toPrettyChars());
748 return;
749 }
750 if (tf && !isSupportedType(tf.next))
751 {
752 ignored("function %s because its return type cannot be mapped to C++", fd.toPrettyChars());
753 return checkFunctionNeedsPlaceholder(fd);
754 }
755 if (tf) foreach (i, fparam; tf.parameterList)
756 {
757 if (!isSupportedType(fparam.type))
758 {
759 ignored("function %s because one of its parameters has type `%s` which cannot be mapped to C++",
760 fd.toPrettyChars(), fparam.type.toChars());
761 return checkFunctionNeedsPlaceholder(fd);
762 }
763 }
764
765 writeProtection(fd.visibility.kind);
766
767 if (tf && tf.linkage == LINK.c)
768 buf.writestring("extern \"C\" ");
769 else if (!adparent)
770 buf.writestring("extern ");
771 if (adparent && fd.isStatic())
772 buf.writestring("static ");
773 else if (adparent && (
774 // Virtual functions in non-templated classes
775 (fd.vtblIndex != -1 && !fd.isOverride()) ||
776
777 // Virtual functions in templated classes (fd.vtblIndex still -1)
778 (tdparent && adparent.isClassDeclaration() && !(this.storageClass & AST.STC.final_ || fd.isFinal))))
779 buf.writestring("virtual ");
780
781 debug (Debug_DtoH_Checks)
782 if (adparent && !tdparent)
783 {
784 auto s = adparent.search(Loc.initial, fd.ident);
785 auto cd = adparent.isClassDeclaration();
786
787 if (!(adparent.storage_class & AST.STC.abstract_) &&
788 !(cd && cd.isAbstract()) &&
789 s is fd && !fd.overnext)
790 {
791 const cn = adparent.ident.toChars();
792 const fn = fd.ident.toChars();
793 const vi = fd.vtblIndex;
794
795 checkbuf.printf("assert(getSlotNumber <%s>(0, &%s::%s) == %d);",
796 cn, cn, fn, vi);
797 checkbuf.writenl();
798 }
799 }
800
801 if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11)
802 writeProtection(AST.Visibility.Kind.private_);
803 funcToBuffer(tf, fd);
804 // FIXME: How to determine if fd is const without tf?
805 if (adparent && tf && (tf.isConst() || tf.isImmutable()))
806 {
807 bool fdOverridesAreConst = true;
808 foreach (fdv; fd.foverrides)
809 {
810 auto tfv = cast(AST.TypeFunction)fdv.type;
811 if (!tfv.isConst() && !tfv.isImmutable())
812 {
813 fdOverridesAreConst = false;
814 break;
815 }
816 }
817
818 buf.writestring(fdOverridesAreConst ? " const" : " /* const */");
819 }
820 if (adparent && fd.isAbstract())
821 buf.writestring(" = 0");
822 if (adparent && fd.isDisabled && global.params.cplusplus >= CppStdRevision.cpp11)
823 buf.writestring(" = delete");
824 buf.writestringln(";");
825 if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11)
826 writeProtection(AST.Visibility.Kind.public_);
827
828 if (!adparent)
829 buf.writenl();
830
831 }
832
833 /++
834 + Checks whether `fd` is a function that requires a dummy declaration
835 + instead of simply emitting the declaration (because it would cause
836 + ABI / behaviour issues). This includes:
837 +
838 + - virtual functions to ensure proper vtable layout
839 + - destructors that would break RAII
840 +/
841 private void checkFunctionNeedsPlaceholder(AST.FuncDeclaration fd)
842 {
843 // Omit redundant declarations - the slot was already
844 // reserved in the base class
845 if (fd.isVirtual() && fd.isIntroducing())
846 {
847 // Hide placeholders because they are not ABI compatible
848 writeProtection(AST.Visibility.Kind.private_);
849
850 __gshared int counter; // Ensure unique names in all cases
851 buf.printf("virtual void __vtable_slot_%u();", counter++);
852 buf.writenl();
853 }
854 else if (fd.isDtorDeclaration())
855 {
856 // Create inaccessible dtor to prevent code from keeping instances that
857 // need to be destroyed on the C++ side (but cannot call the dtor)
858 writeProtection(AST.Visibility.Kind.private_);
859 buf.writeByte('~');
860 buf.writestring(adparent.ident.toString());
861 buf.writestringln("();");
862 }
863 }
864
865 override void visit(AST.UnitTestDeclaration utd)
866 {
867 debug (Debug_DtoH) mixin(traceVisit!utd);
868 }
869
870 override void visit(AST.VarDeclaration vd)
871 {
872 debug (Debug_DtoH) mixin(traceVisit!vd);
873
874 if (!shouldEmitAndMarkVisited(vd))
875 return;
876
877 // Tuple field are expanded into multiple VarDeclarations
878 // (we'll visit them later)
879 if (vd.type && vd.type.isTypeTuple())
880 {
881 assert(vd.aliassym);
882 vd.toAlias().accept(this);
883 return;
884 }
885
886 if (vd.originalType && vd.type == AST.Type.tsize_t)
887 origType = vd.originalType;
888 scope(exit) origType = null;
889
890 if (!vd.alignment.isDefault() && !vd.alignment.isUnknown())
891 {
892 buf.printf("// Ignoring var %s alignment %d", vd.toChars(), vd.alignment.get());
893 buf.writenl();
894 }
895
896 // Determine the variable type which might be missing inside of
897 // template declarations. Infer the type from the initializer then
898 AST.Type type = vd.type;
899 if (!type)
900 {
901 assert(tdparent);
902
903 // Just a precaution, implicit type without initializer should be rejected
904 if (!vd._init)
905 return;
906
907 if (auto ei = vd._init.isExpInitializer())
908 type = ei.exp.type;
909
910 // Can happen if the expression needs further semantic
911 if (!type)
912 {
913 ignored("%s because the type could not be determined", vd.toPrettyChars());
914 return;
915 }
916
917 // Apply const/immutable to the inferred type
918 if (vd.storage_class & (AST.STC.const_ | AST.STC.immutable_))
919 type = type.constOf();
920 }
921
922 if (vd.storage_class & AST.STC.manifest)
923 {
924 EnumKind kind = getEnumKind(type);
925
926 if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) {
927 ignored("enum `%s` because it is `%s`.", vd.toPrettyChars(), AST.visibilityToChars(vd.visibility.kind));
928 return;
929 }
930
931 writeProtection(vd.visibility.kind);
932
933 final switch (kind)
934 {
935 case EnumKind.Int, EnumKind.Numeric:
936 // 'enum : type' is only available from C++-11 onwards.
937 if (global.params.cplusplus < CppStdRevision.cpp11)
938 goto case;
939 buf.writestring("enum : ");
940 determineEnumType(type).accept(this);
941 buf.writestring(" { ");
942 writeIdentifier(vd, true);
943 buf.writestring(" = ");
944 auto ie = AST.initializerToExpression(vd._init).isIntegerExp();
945 visitInteger(ie.toInteger(), type);
946 buf.writestring(" };");
947 break;
948
949 case EnumKind.String, EnumKind.Enum:
950 buf.writestring("static ");
951 auto target = determineEnumType(type);
952 target.accept(this);
953 buf.writestring(" const ");
954 writeIdentifier(vd, true);
955 buf.writestring(" = ");
956 auto e = AST.initializerToExpression(vd._init);
957 printExpressionFor(target, e);
958 buf.writestring(";");
959 break;
960
961 case EnumKind.Other:
962 ignored("enum `%s` because type `%s` is currently not supported for enum constants.", vd.toPrettyChars(), type.toChars());
963 return;
964 }
965 buf.writenl();
966 buf.writenl();
967 return;
968 }
969
970 if (vd.storage_class & (AST.STC.static_ | AST.STC.extern_ | AST.STC.gshared) ||
971 vd.parent && vd.parent.isModule())
972 {
973 const vdLinkage = vd.resolvedLinkage();
974 if (vdLinkage != LINK.c && vdLinkage != LINK.cpp && !(tdparent && (this.linkage == LINK.c || this.linkage == LINK.cpp)))
975 {
976 ignored("variable %s because of linkage", vd.toPrettyChars());
977 return;
978 }
979 if (vd.mangleOverride && vdLinkage != LINK.c)
980 {
981 ignored("variable %s because C++ doesn't support explicit mangling", vd.toPrettyChars());
982 return;
983 }
984 if (!isSupportedType(type))
985 {
986 ignored("variable %s because its type cannot be mapped to C++", vd.toPrettyChars());
987 return;
988 }
989 if (auto kc = keywordClass(vd.ident))
990 {
991 ignored("variable %s because its name is a %s", vd.toPrettyChars(), kc);
992 return;
993 }
994 writeProtection(vd.visibility.kind);
995 if (vdLinkage == LINK.c)
996 buf.writestring("extern \"C\" ");
997 else if (!adparent)
998 buf.writestring("extern ");
999 if (adparent)
1000 buf.writestring("static ");
1001 typeToBuffer(type, vd);
1002 writeDeclEnd();
1003 return;
1004 }
1005
1006 if (adparent)
1007 {
1008 writeProtection(vd.visibility.kind);
1009 typeToBuffer(type, vd, true);
1010 buf.writestringln(";");
1011
1012 debug (Debug_DtoH_Checks)
1013 {
1014 checkbuf.level++;
1015 const pn = adparent.ident.toChars();
1016 const vn = vd.ident.toChars();
1017 const vo = vd.offset;
1018 checkbuf.printf("assert(offsetof(%s, %s) == %d);",
1019 pn, vn, vo);
1020 checkbuf.writenl();
1021 checkbuf.level--;
1022 }
1023 return;
1024 }
1025
1026 visit(cast(AST.Dsymbol)vd);
1027 }
1028
1029 override void visit(AST.TypeInfoDeclaration tid)
1030 {
1031 debug (Debug_DtoH) mixin(traceVisit!tid);
1032 }
1033
1034 override void visit(AST.AliasDeclaration ad)
1035 {
1036 debug (Debug_DtoH) mixin(traceVisit!ad);
1037
1038 if (!shouldEmitAndMarkVisited(ad))
1039 return;
1040
1041 writeProtection(ad.visibility.kind);
1042
1043 if (auto t = ad.type)
1044 {
1045 if (t.ty == AST.Tdelegate || t.ty == AST.Tident)
1046 {
1047 visit(cast(AST.Dsymbol)ad);
1048 return;
1049 }
1050
1051 // for function pointers we need to original type
1052 if (ad.originalType && ad.type.ty == AST.Tpointer &&
1053 (cast(AST.TypePointer)t).nextOf.ty == AST.Tfunction)
1054 {
1055 origType = ad.originalType;
1056 }
1057 scope(exit) origType = null;
1058
1059 buf.writestring("typedef ");
1060 typeToBuffer(origType !is null ? origType : t, ad);
1061 writeDeclEnd();
1062 return;
1063 }
1064 if (!ad.aliassym)
1065 {
1066 assert(0);
1067 }
1068 if (auto ti = ad.aliassym.isTemplateInstance())
1069 {
1070 visitTi(ti);
1071 return;
1072 }
1073 if (auto sd = ad.aliassym.isStructDeclaration())
1074 {
1075 buf.writestring("typedef ");
1076 sd.type.accept(this);
1077 buf.writestring(" ");
1078 writeIdentifier(ad);
1079 writeDeclEnd();
1080 return;
1081 }
1082 else if (auto td = ad.aliassym.isTemplateDeclaration())
1083 {
1084 if (global.params.cplusplus < CppStdRevision.cpp11)
1085 {
1086 ignored("%s because `using` declarations require C++ 11", ad.toPrettyChars());
1087 return;
1088 }
1089
1090 printTemplateParams(td);
1091 buf.writestring("using ");
1092 writeIdentifier(ad);
1093 buf.writestring(" = ");
1094 writeFullName(td);
1095 buf.writeByte('<');
1096
1097 foreach (const idx, const p; *td.parameters)
1098 {
1099 if (idx)
1100 buf.writestring(", ");
1101 writeIdentifier(p.ident, p.loc, "parameter", true);
1102 }
1103 buf.writestringln(">;");
1104 return;
1105 }
1106
1107 auto fd = ad.aliassym.isFuncDeclaration();
1108
1109 if (fd && (fd.isGenerated() || fd.isDtorDeclaration()))
1110 {
1111 // Ignore. It's taken care of while visiting FuncDeclaration
1112 return;
1113 }
1114
1115 // Recognize member function aliases, e.g. alias visit = Parent.visit;
1116 if (adparent && fd)
1117 {
1118 auto pd = fd.isMember();
1119 if (!pd)
1120 {
1121 ignored("%s because free functions cannot be aliased in C++", ad.toPrettyChars());
1122 }
1123 else if (global.params.cplusplus < CppStdRevision.cpp11)
1124 {
1125 ignored("%s because `using` declarations require C++ 11", ad.toPrettyChars());
1126 }
1127 else if (ad.ident != fd.ident)
1128 {
1129 ignored("%s because `using` cannot rename functions in aggregates", ad.toPrettyChars());
1130 }
1131 else if (fd.toAliasFunc().parent.isTemplateMixin())
1132 {
1133 // Member's of template mixins are directly emitted into the aggregate
1134 }
1135 else
1136 {
1137 buf.writestring("using ");
1138
1139 // Print prefix of the base class if this function originates from a superclass
1140 // because alias might be resolved through multiple classes, e.g.
1141 // e.g. for alias visit = typeof(super).visit in the visitors
1142 if (!fd.isIntroducing())
1143 printPrefix(ad.toParent().isClassDeclaration().baseClass);
1144 else
1145 printPrefix(pd);
1146
1147 buf.writestring(fd.ident.toChars());
1148 buf.writestringln(";");
1149 }
1150 return;
1151 }
1152
1153 ignored("%s %s", ad.aliassym.kind(), ad.aliassym.toPrettyChars());
1154 }
1155
1156 override void visit(AST.Nspace ns)
1157 {
1158 debug (Debug_DtoH) mixin(traceVisit!ns);
1159 handleNspace(ns, ns.members);
1160 }
1161
1162 override void visit(AST.CPPNamespaceDeclaration ns)
1163 {
1164 debug (Debug_DtoH) mixin(traceVisit!ns);
1165 handleNspace(ns, ns.decl);
1166 }
1167
1168 /// Writes the namespace declaration and visits all members
1169 private void handleNspace(AST.Dsymbol namespace, Dsymbols* members)
1170 {
1171 buf.writestring("namespace ");
1172 writeIdentifier(namespace);
1173 buf.writenl();
1174 buf.writestring("{");
1175 buf.writenl();
1176 buf.level++;
1177 foreach(decl;(*members))
1178 {
1179 decl.accept(this);
1180 }
1181 buf.level--;
1182 buf.writestring("}");
1183 buf.writenl();
1184 }
1185
1186 override void visit(AST.AnonDeclaration ad)
1187 {
1188 debug (Debug_DtoH) mixin(traceVisit!ad);
1189
1190 const anonStash = inAnonymousDecl;
1191 inAnonymousDecl = true;
1192 scope (exit) inAnonymousDecl = anonStash;
1193
1194 buf.writestringln(ad.isunion ? "union" : "struct");
1195 buf.writestringln("{");
1196 buf.level++;
1197 foreach (s; *ad.decl)
1198 {
1199 s.accept(this);
1200 }
1201 buf.level--;
1202 buf.writestringln("};");
1203 }
1204
1205 private bool memberField(AST.VarDeclaration vd)
1206 {
1207 if (!vd.type || !vd.type.deco || !vd.ident)
1208 return false;
1209 if (!vd.isField())
1210 return false;
1211 if (vd.type.ty == AST.Tfunction)
1212 return false;
1213 if (vd.type.ty == AST.Tsarray)
1214 return false;
1215 return true;
1216 }
1217
1218 override void visit(AST.StructDeclaration sd)
1219 {
1220 debug (Debug_DtoH) mixin(traceVisit!sd);
1221
1222 if (!shouldEmitAndMarkVisited(sd))
1223 return;
1224
1225 const ignoredStash = this.ignoredCounter;
1226 scope (exit) this.ignoredCounter = ignoredStash;
1227
1228 pushAlignToBuffer(sd.alignment);
1229
1230 writeProtection(sd.visibility.kind);
1231
1232 const structAsClass = sd.cppmangle == CPPMANGLE.asClass;
1233 if (sd.isUnionDeclaration())
1234 buf.writestring("union ");
1235 else
1236 buf.writestring(structAsClass ? "class " : "struct ");
1237
1238 writeIdentifier(sd);
1239 if (!sd.members)
1240 {
1241 buf.writestringln(";");
1242 buf.writenl();
1243 return;
1244 }
1245
1246 // D structs are always final
1247 if (!sd.isUnionDeclaration())
1248 buf.writestring(" final");
1249
1250 buf.writenl();
1251 buf.writestring("{");
1252
1253 const protStash = this.currentVisibility;
1254 this.currentVisibility = structAsClass ? AST.Visibility.Kind.private_ : AST.Visibility.Kind.public_;
1255 scope (exit) this.currentVisibility = protStash;
1256
1257 buf.level++;
1258 buf.writenl();
1259 auto save = adparent;
1260 adparent = sd;
1261
1262 foreach (m; *sd.members)
1263 {
1264 m.accept(this);
1265 }
1266 // Generate default ctor
1267 if (!sd.noDefaultCtor && !sd.isUnionDeclaration())
1268 {
1269 writeProtection(AST.Visibility.Kind.public_);
1270 buf.printf("%s()", sd.ident.toChars());
1271 size_t varCount;
1272 bool first = true;
1273 buf.level++;
1274 foreach (m; *sd.members)
1275 {
1276 if (auto vd = m.isVarDeclaration())
1277 {
1278 if (!memberField(vd))
1279 continue;
1280 varCount++;
1281
1282 if (!vd._init && !vd.type.isTypeBasic() && !vd.type.isTypePointer && !vd.type.isTypeStruct &&
1283 !vd.type.isTypeClass && !vd.type.isTypeDArray && !vd.type.isTypeSArray)
1284 {
1285 continue;
1286 }
1287 if (vd._init && vd._init.isVoidInitializer())
1288 continue;
1289
1290 if (first)
1291 {
1292 buf.writestringln(" :");
1293 first = false;
1294 }
1295 else
1296 {
1297 buf.writestringln(",");
1298 }
1299 writeIdentifier(vd, true);
1300 buf.writeByte('(');
1301
1302 if (vd._init)
1303 {
1304 auto e = AST.initializerToExpression(vd._init);
1305 printExpressionFor(vd.type, e, true);
1306 }
1307 buf.printf(")");
1308 }
1309 }
1310 buf.level--;
1311 buf.writenl();
1312 buf.writestringln("{");
1313 buf.writestringln("}");
1314 auto ctor = sd.ctor ? sd.ctor.isFuncDeclaration() : null;
1315 if (varCount && (!ctor || ctor.storage_class & AST.STC.disable))
1316 {
1317 buf.printf("%s(", sd.ident.toChars());
1318 first = true;
1319 foreach (m; *sd.members)
1320 {
1321 if (auto vd = m.isVarDeclaration())
1322 {
1323 if (!memberField(vd))
1324 continue;
1325 if (!first)
1326 buf.writestring(", ");
1327 assert(vd.type);
1328 assert(vd.ident);
1329 typeToBuffer(vd.type, vd, true);
1330 // Don't print default value for first parameter to not clash
1331 // with the default ctor defined above
1332 if (!first)
1333 {
1334 buf.writestring(" = ");
1335 printExpressionFor(vd.type, findDefaultInitializer(vd));
1336 }
1337 first = false;
1338 }
1339 }
1340 buf.writestring(") :");
1341 buf.level++;
1342 buf.writenl();
1343
1344 first = true;
1345 foreach (m; *sd.members)
1346 {
1347 if (auto vd = m.isVarDeclaration())
1348 {
1349 if (!memberField(vd))
1350 continue;
1351
1352 if (first)
1353 first = false;
1354 else
1355 buf.writestringln(",");
1356
1357 writeIdentifier(vd, true);
1358 buf.writeByte('(');
1359 writeIdentifier(vd, true);
1360 buf.writeByte(')');
1361 }
1362 }
1363 buf.writenl();
1364 buf.writestringln("{}");
1365 buf.level--;
1366 }
1367 }
1368
1369 buf.level--;
1370 adparent = save;
1371 buf.writestringln("};");
1372
1373 popAlignToBuffer(sd.alignment);
1374 buf.writenl();
1375
1376 // Workaround because size triggers a forward-reference error
1377 // for struct templates (the size is undetermined even if the
1378 // size doesn't depend on the parameters)
1379 debug (Debug_DtoH_Checks)
1380 if (!tdparent)
1381 {
1382 checkbuf.level++;
1383 const sn = sd.ident.toChars();
1384 const sz = sd.size(Loc.initial);
1385 checkbuf.printf("assert(sizeof(%s) == %llu);", sn, sz);
1386 checkbuf.writenl();
1387 checkbuf.level--;
1388 }
1389 }
1390
1391 /// Starts a custom alignment section using `#pragma pack` if
1392 /// `alignment` specifies a custom alignment
1393 private void pushAlignToBuffer(structalign_t alignment)
1394 {
1395 // DMD ensures alignment is a power of two
1396 //assert(alignment > 0 && ((alignment & (alignment - 1)) == 0),
1397 // "Invalid alignment size");
1398
1399 // When no alignment is specified, `uint.max` is the default
1400 // FIXME: alignment is 0 for structs templated members
1401 if (alignment.isDefault() || (tdparent && alignment.isUnknown()))
1402 {
1403 return;
1404 }
1405
1406 buf.printf("#pragma pack(push, %d)", alignment.get());
1407 buf.writenl();
1408 }
1409
1410 /// Ends a custom alignment section using `#pragma pack` if
1411 /// `alignment` specifies a custom alignment
1412 private void popAlignToBuffer(structalign_t alignment)
1413 {
1414 if (alignment.isDefault() || (tdparent && alignment.isUnknown()))
1415 return;
1416
1417 buf.writestringln("#pragma pack(pop)");
1418 }
1419
1420 override void visit(AST.ClassDeclaration cd)
1421 {
1422 debug (Debug_DtoH) mixin(traceVisit!cd);
1423
1424 if (cd.baseClass && shouldEmit(cd))
1425 includeSymbol(cd.baseClass);
1426
1427 if (!shouldEmitAndMarkVisited(cd))
1428 return;
1429
1430 writeProtection(cd.visibility.kind);
1431
1432 const classAsStruct = cd.cppmangle == CPPMANGLE.asStruct;
1433 buf.writestring(classAsStruct ? "struct " : "class ");
1434 writeIdentifier(cd);
1435
1436 if (cd.storage_class & AST.STC.final_ || (tdparent && this.storageClass & AST.STC.final_))
1437 buf.writestring(" final");
1438
1439 assert(cd.baseclasses);
1440
1441 foreach (i, base; *cd.baseclasses)
1442 {
1443 buf.writestring(i == 0 ? " : public " : ", public ");
1444
1445 // Base classes/interfaces might depend on template parameters,
1446 // e.g. class A(T) : B!T { ... }
1447 if (base.sym is null)
1448 {
1449 base.type.accept(this);
1450 }
1451 else
1452 {
1453 writeFullName(base.sym);
1454 }
1455 }
1456
1457 if (!cd.members)
1458 {
1459 buf.writestring(";");
1460 buf.writenl();
1461 buf.writenl();
1462 return;
1463 }
1464
1465 buf.writenl();
1466 buf.writestringln("{");
1467
1468 const protStash = this.currentVisibility;
1469 this.currentVisibility = classAsStruct ? AST.Visibility.Kind.public_ : AST.Visibility.Kind.private_;
1470 scope (exit) this.currentVisibility = protStash;
1471
1472 auto save = adparent;
1473 adparent = cd;
1474 buf.level++;
1475 foreach (m; *cd.members)
1476 {
1477 m.accept(this);
1478 }
1479 buf.level--;
1480 adparent = save;
1481
1482 buf.writestringln("};");
1483 buf.writenl();
1484 }
1485
1486 override void visit(AST.EnumDeclaration ed)
1487 {
1488 debug (Debug_DtoH) mixin(traceVisit!ed);
1489
1490 if (!shouldEmitAndMarkVisited(ed))
1491 return;
1492
1493 if (ed.isSpecial())
1494 {
1495 //ignored("%s because it is a special C++ type", ed.toPrettyChars());
1496 return;
1497 }
1498
1499 // we need to know a bunch of stuff about the enum...
1500 bool isAnonymous = ed.ident is null;
1501 const isOpaque = !ed.members;
1502 AST.Type type = ed.memtype;
1503 if (!type && !isOpaque)
1504 {
1505 // check all keys have matching type
1506 foreach (_m; *ed.members)
1507 {
1508 auto m = _m.isEnumMember();
1509 if (!type)
1510 type = m.type;
1511 else if (m.type !is type)
1512 {
1513 type = null;
1514 break;
1515 }
1516 }
1517 }
1518 EnumKind kind = getEnumKind(type);
1519
1520 if (isOpaque)
1521 {
1522 // Opaque enums were introduced in C++ 11 (workaround?)
1523 if (global.params.cplusplus < CppStdRevision.cpp11)
1524 {
1525 ignored("%s because opaque enums require C++ 11", ed.toPrettyChars());
1526 return;
1527 }
1528 // Opaque enum defaults to int but the type might not be set
1529 else if (!type)
1530 {
1531 kind = EnumKind.Int;
1532 }
1533 // Cannot apply namespace workaround for non-integral types
1534 else if (kind != EnumKind.Int && kind != EnumKind.Numeric)
1535 {
1536 ignored("enum %s because of its base type", ed.toPrettyChars());
1537 return;
1538 }
1539 }
1540
1541 // determine if this is an enum, or just a group of manifest constants
1542 bool manifestConstants = !isOpaque && (!type || (isAnonymous && kind == EnumKind.Other));
1543 assert(!manifestConstants || isAnonymous);
1544
1545 writeProtection(ed.visibility.kind);
1546
1547 // write the enum header
1548 if (!manifestConstants)
1549 {
1550 if (kind == EnumKind.Int || kind == EnumKind.Numeric)
1551 {
1552 buf.writestring("enum");
1553 // D enums are strong enums, but there exists only a direct mapping
1554 // with 'enum class' from C++-11 onwards.
1555 if (global.params.cplusplus >= CppStdRevision.cpp11)
1556 {
1557 if (!isAnonymous)
1558 {
1559 buf.writestring(" class ");
1560 writeIdentifier(ed);
1561 }
1562 if (kind == EnumKind.Numeric)
1563 {
1564 buf.writestring(" : ");
1565 determineEnumType(type).accept(this);
1566 }
1567 }
1568 else if (!isAnonymous)
1569 {
1570 buf.writeByte(' ');
1571 writeIdentifier(ed);
1572 }
1573 }
1574 else
1575 {
1576 buf.writestring("namespace");
1577 if(!isAnonymous)
1578 {
1579 buf.writeByte(' ');
1580 writeIdentifier(ed);
1581 }
1582 }
1583 // Opaque enums have no members, hence skip the body
1584 if (isOpaque)
1585 {
1586 buf.writestringln(";");
1587 return;
1588 }
1589 else
1590 {
1591 buf.writenl();
1592 buf.writestringln("{");
1593 }
1594 }
1595
1596 // emit constant for each member
1597 if (!manifestConstants)
1598 buf.level++;
1599
1600 foreach (_m; *ed.members)
1601 {
1602 auto m = _m.isEnumMember();
1603 AST.Type memberType = type ? type : m.type;
1604 const EnumKind memberKind = type ? kind : getEnumKind(memberType);
1605
1606 if (!manifestConstants && (kind == EnumKind.Int || kind == EnumKind.Numeric))
1607 {
1608 // C++-98 compatible enums must use the typename as a prefix to avoid
1609 // collisions with other identifiers in scope. For consistency with D,
1610 // the enum member `Type.member` is emitted as `Type_member` in C++-98.
1611 if (!isAnonymous && global.params.cplusplus < CppStdRevision.cpp11)
1612 {
1613 writeIdentifier(ed);
1614 buf.writeByte('_');
1615 }
1616 writeIdentifier(m, true);
1617 buf.writestring(" = ");
1618
1619 auto ie = cast(AST.IntegerExp)m.value;
1620 visitInteger(ie.toInteger(), memberType);
1621 buf.writestring(",");
1622 }
1623 else if (global.params.cplusplus >= CppStdRevision.cpp11 &&
1624 manifestConstants && (memberKind == EnumKind.Int || memberKind == EnumKind.Numeric))
1625 {
1626 buf.writestring("enum : ");
1627 determineEnumType(memberType).accept(this);
1628 buf.writestring(" { ");
1629 writeIdentifier(m, true);
1630 buf.writestring(" = ");
1631
1632 auto ie = cast(AST.IntegerExp)m.value;
1633 visitInteger(ie.toInteger(), memberType);
1634 buf.writestring(" };");
1635 }
1636 else
1637 {
1638 buf.writestring("static ");
1639 auto target = determineEnumType(memberType);
1640 target.accept(this);
1641 buf.writestring(" const ");
1642 writeIdentifier(m, true);
1643 buf.writestring(" = ");
1644 printExpressionFor(target, m.origValue);
1645 buf.writestring(";");
1646 }
1647 buf.writenl();
1648 }
1649
1650 if (!manifestConstants)
1651 buf.level--;
1652 // write the enum tail
1653 if (!manifestConstants)
1654 buf.writestring("};");
1655 buf.writenl();
1656 buf.writenl();
1657 }
1658
1659 override void visit(AST.EnumMember em)
1660 {
1661 assert(em.ed);
1662
1663 // Members of anonymous members are reachable without referencing the
1664 // EnumDeclaration, e.g. public import foo : someEnumMember;
1665 if (em.ed.isAnonymous())
1666 {
1667 visit(em.ed);
1668 return;
1669 }
1670
1671 assert(false, "This node type should be handled in the EnumDeclaration");
1672 }
1673
1674 override void visit(AST.TupleDeclaration tup)
1675 {
1676 debug (Debug_DtoH) mixin(traceVisit!tup);
1677
1678 tup.foreachVar((s) { s.accept(this); });
1679 }
1680
1681 /**
1682 * Prints a member/parameter/variable declaration into `buf`.
1683 *
1684 * Params:
1685 * t = the type (used if `this.origType` is null)
1686 * s = the symbol denoting the identifier
1687 * canFixup = whether the identifier may be changed without affecting
1688 * binary compatibility (forwarded to `writeIdentifier`)
1689 */
1690 private void typeToBuffer(AST.Type t, AST.Dsymbol s, const bool canFixup = false)
1691 {
1692 debug (Debug_DtoH)
1693 {
1694 printf("[typeToBuffer(AST.Type, AST.Dsymbol) enter] %s sym %s\n", t.toChars(), s.toChars());
1695 scope(exit) printf("[typeToBuffer(AST.Type, AST.Dsymbol) exit] %s sym %s\n", t.toChars(), s.toChars());
1696 }
1697
1698 // The context pointer (represented as `ThisDeclaration`) is named
1699 // `this` but accessible via `outer`
1700 if (auto td = s.isThisDeclaration())
1701 {
1702 import dmd.id;
1703 this.ident = Id.outer;
1704 }
1705 else
1706 this.ident = s.ident;
1707
1708 auto type = origType !is null ? origType : t;
1709 AST.Dsymbol customLength;
1710
1711 // Check for quirks that are usually resolved during semantic
1712 if (tdparent)
1713 {
1714 // Declarations within template declarations might use TypeAArray
1715 // instead of TypeSArray when the length is not an IntegerExp,
1716 // e.g. int[SOME_CONSTANT]
1717 if (auto taa = type.isTypeAArray())
1718 {
1719 // Try to resolve the symbol from the key if it's not an actual type
1720 Identifier id;
1721 if (auto ti = taa.index.isTypeIdentifier())
1722 id = ti.ident;
1723
1724 if (id)
1725 {
1726 auto sym = findSymbol(id, adparent ? adparent : tdparent);
1727 if (!sym)
1728 {
1729 // Couldn't resolve, assume actual AA
1730 }
1731 else if (AST.isType(sym))
1732 {
1733 // a real associative array, forward to visit
1734 }
1735 else if (auto vd = sym.isVarDeclaration())
1736 {
1737 // Actually a static array with length symbol
1738 customLength = sym;
1739 type = taa.next; // visit the element type, length is written below
1740 }
1741 else
1742 {
1743 printf("Resolved unexpected symbol while determining static array length: %s\n", sym.toChars());
1744 fflush(stdout);
1745 fatal();
1746 }
1747 }
1748 }
1749 }
1750 type.accept(this);
1751 if (this.ident)
1752 {
1753 buf.writeByte(' ');
1754 // Custom identifier doesn't need further checks
1755 if (this.ident !is s.ident)
1756 buf.writestring(this.ident.toString());
1757 else
1758 writeIdentifier(s, canFixup);
1759
1760 }
1761 this.ident = null;
1762
1763 // Size is either taken from the type or resolved above
1764 auto tsa = t.isTypeSArray();
1765 if (tsa || customLength)
1766 {
1767 buf.writeByte('[');
1768 if (tsa)
1769 tsa.dim.accept(this);
1770 else
1771 writeFullName(customLength);
1772 buf.writeByte(']');
1773 }
1774 else if (t.isTypeNoreturn())
1775 buf.writestring("[0]");
1776 }
1777
1778 override void visit(AST.Type t)
1779 {
1780 debug (Debug_DtoH) mixin(traceVisit!t);
1781 printf("Invalid type: %s\n", t.toPrettyChars());
1782 assert(0);
1783 }
1784
1785 override void visit(AST.TypeNoreturn t)
1786 {
1787 debug (Debug_DtoH) mixin(traceVisit!t);
1788
1789 buf.writestring("/* noreturn */ char");
1790 }
1791
1792 override void visit(AST.TypeIdentifier t)
1793 {
1794 debug (Debug_DtoH) mixin(traceVisit!t);
1795
1796 // Try to resolve the referenced symbol
1797 if (auto sym = findSymbol(t.ident))
1798 ensureDeclared(outermostSymbol(sym));
1799
1800 if (t.idents.length)
1801 buf.writestring("typename ");
1802
1803 writeIdentifier(t.ident, t.loc, "type", tdparent !is null);
1804
1805 foreach (arg; t.idents)
1806 {
1807 buf.writestring("::");
1808
1809 import dmd.root.rootobject;
1810 // Is this even possible?
1811 if (arg.dyncast != DYNCAST.identifier)
1812 {
1813 printf("arg.dyncast() = %d\n", arg.dyncast());
1814 assert(false);
1815 }
1816 buf.writestring((cast(Identifier) arg).toChars());
1817 }
1818 }
1819
1820 override void visit(AST.TypeNull t)
1821 {
1822 debug (Debug_DtoH) mixin(traceVisit!t);
1823
1824 if (global.params.cplusplus >= CppStdRevision.cpp11)
1825 buf.writestring("nullptr_t");
1826 else
1827 buf.writestring("void*");
1828
1829 }
1830
1831 override void visit(AST.TypeTypeof t)
1832 {
1833 debug (Debug_DtoH) mixin(traceVisit!t);
1834
1835 assert(t.exp);
1836
1837 if (t.exp.type)
1838 {
1839 t.exp.type.accept(this);
1840 }
1841 else if (t.exp.isThisExp())
1842 {
1843 // Short circuit typeof(this) => <Aggregate name>
1844 assert(adparent);
1845 buf.writestring(adparent.ident.toChars());
1846 }
1847 else
1848 {
1849 // Relying on C++'s typeof might produce wrong results
1850 // but it's the best we've got here.
1851 buf.writestring("typeof(");
1852 t.exp.accept(this);
1853 buf.writeByte(')');
1854 }
1855 }
1856
1857 override void visit(AST.TypeBasic t)
1858 {
1859 debug (Debug_DtoH) mixin(traceVisit!t);
1860
1861 if (t.isConst() || t.isImmutable())
1862 buf.writestring("const ");
1863 string typeName;
1864 switch (t.ty)
1865 {
1866 case AST.Tvoid: typeName = "void"; break;
1867 case AST.Tbool: typeName = "bool"; break;
1868 case AST.Tchar: typeName = "char"; break;
1869 case AST.Twchar: typeName = "char16_t"; break;
1870 case AST.Tdchar: typeName = "char32_t"; break;
1871 case AST.Tint8: typeName = "int8_t"; break;
1872 case AST.Tuns8: typeName = "uint8_t"; break;
1873 case AST.Tint16: typeName = "int16_t"; break;
1874 case AST.Tuns16: typeName = "uint16_t"; break;
1875 case AST.Tint32: typeName = "int32_t"; break;
1876 case AST.Tuns32: typeName = "uint32_t"; break;
1877 case AST.Tint64: typeName = "int64_t"; break;
1878 case AST.Tuns64: typeName = "uint64_t"; break;
1879 case AST.Tfloat32: typeName = "float"; break;
1880 case AST.Tfloat64: typeName = "double"; break;
1881 case AST.Tfloat80:
1882 typeName = "_d_real";
1883 hasReal = true;
1884 break;
1885 case AST.Tcomplex32: typeName = "_Complex float"; break;
1886 case AST.Tcomplex64: typeName = "_Complex double"; break;
1887 case AST.Tcomplex80:
1888 typeName = "_Complex _d_real";
1889 hasReal = true;
1890 break;
1891 // ???: This is not strictly correct, but it should be ignored
1892 // in all places where it matters most (variables, functions, ...).
1893 case AST.Timaginary32: typeName = "float"; break;
1894 case AST.Timaginary64: typeName = "double"; break;
1895 case AST.Timaginary80:
1896 typeName = "_d_real";
1897 hasReal = true;
1898 break;
1899 default:
1900 //t.print();
1901 assert(0);
1902 }
1903 buf.writestring(typeName);
1904 }
1905
1906 override void visit(AST.TypePointer t)
1907 {
1908 debug (Debug_DtoH) mixin(traceVisit!t);
1909
1910 auto ts = t.next.isTypeStruct();
1911 if (ts && !strcmp(ts.sym.ident.toChars(), "__va_list_tag"))
1912 {
1913 buf.writestring("va_list");
1914 return;
1915 }
1916
1917 // Pointer targets can be forward referenced
1918 const fwdSave = forwarding;
1919 forwarding = true;
1920 scope (exit) forwarding = fwdSave;
1921
1922 t.next.accept(this);
1923 if (t.next.ty != AST.Tfunction)
1924 buf.writeByte('*');
1925 if (t.isConst() || t.isImmutable())
1926 buf.writestring(" const");
1927 }
1928
1929 override void visit(AST.TypeSArray t)
1930 {
1931 debug (Debug_DtoH) mixin(traceVisit!t);
1932 t.next.accept(this);
1933 }
1934
1935 override void visit(AST.TypeAArray t)
1936 {
1937 debug (Debug_DtoH) mixin(traceVisit!t);
1938 AST.Type.tvoidptr.accept(this);
1939 }
1940
1941 override void visit(AST.TypeFunction tf)
1942 {
1943 debug (Debug_DtoH) mixin(traceVisit!tf);
1944
1945 tf.next.accept(this);
1946 buf.writeByte('(');
1947 buf.writeByte('*');
1948 if (ident)
1949 buf.writestring(ident.toChars());
1950 ident = null;
1951 buf.writeByte(')');
1952 buf.writeByte('(');
1953 foreach (i, fparam; tf.parameterList)
1954 {
1955 if (i)
1956 buf.writestring(", ");
1957 fparam.accept(this);
1958 }
1959 if (tf.parameterList.varargs)
1960 {
1961 if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1)
1962 buf.writestring(", ");
1963 buf.writestring("...");
1964 }
1965 buf.writeByte(')');
1966 }
1967
1968 /// Writes the type that represents `ed` into `buf`.
1969 /// (Might not be `ed` for special enums or enums that were emitted as namespaces)
1970 private void enumToBuffer(AST.EnumDeclaration ed)
1971 {
1972 debug (Debug_DtoH) mixin(traceVisit!ed);
1973
1974 if (ed.isSpecial())
1975 {
1976 if (ed.ident == DMDType.c_long)
1977 buf.writestring("long");
1978 else if (ed.ident == DMDType.c_ulong)
1979 buf.writestring("unsigned long");
1980 else if (ed.ident == DMDType.c_longlong)
1981 buf.writestring("long long");
1982 else if (ed.ident == DMDType.c_ulonglong)
1983 buf.writestring("unsigned long long");
1984 else if (ed.ident == DMDType.c_long_double)
1985 buf.writestring("long double");
1986 else if (ed.ident == DMDType.c_char)
1987 buf.writestring("char");
1988 else if (ed.ident == DMDType.c_wchar_t)
1989 buf.writestring("wchar_t");
1990 else if (ed.ident == DMDType.c_complex_float)
1991 buf.writestring("_Complex float");
1992 else if (ed.ident == DMDType.c_complex_double)
1993 buf.writestring("_Complex double");
1994 else if (ed.ident == DMDType.c_complex_real)
1995 buf.writestring("_Complex long double");
1996 else
1997 {
1998 //ed.print();
1999 assert(0);
2000 }
2001 return;
2002 }
2003
2004 const kind = getEnumKind(ed.memtype);
2005
2006 // Check if the enum was emitted as a real enum
2007 if (kind == EnumKind.Int || kind == EnumKind.Numeric)
2008 {
2009 writeFullName(ed);
2010 }
2011 else
2012 {
2013 // Use the base type if the enum was emitted as a namespace
2014 buf.printf("/* %s */ ", ed.ident.toChars());
2015 ed.memtype.accept(this);
2016 }
2017 }
2018
2019 override void visit(AST.TypeEnum t)
2020 {
2021 debug (Debug_DtoH) mixin(traceVisit!t);
2022
2023 if (t.isConst() || t.isImmutable())
2024 buf.writestring("const ");
2025 enumToBuffer(t.sym);
2026 }
2027
2028 override void visit(AST.TypeStruct t)
2029 {
2030 debug (Debug_DtoH) mixin(traceVisit!t);
2031
2032 if (t.isConst() || t.isImmutable())
2033 buf.writestring("const ");
2034 writeFullName(t.sym);
2035 }
2036
2037 override void visit(AST.TypeDArray t)
2038 {
2039 debug (Debug_DtoH) mixin(traceVisit!t);
2040
2041 if (t.isConst() || t.isImmutable())
2042 buf.writestring("const ");
2043 buf.writestring("_d_dynamicArray< ");
2044 t.next.accept(this);
2045 buf.writestring(" >");
2046 }
2047
2048 override void visit(AST.TypeInstance t)
2049 {
2050 visitTi(t.tempinst);
2051 }
2052
2053 private void visitTi(AST.TemplateInstance ti)
2054 {
2055 debug (Debug_DtoH) mixin(traceVisit!ti);
2056
2057 // Ensure that the TD appears before the instance
2058 if (auto td = findTemplateDeclaration(ti))
2059 ensureDeclared(td);
2060
2061 foreach (o; *ti.tiargs)
2062 {
2063 if (!AST.isType(o))
2064 return;
2065 }
2066 buf.writestring(ti.name.toChars());
2067 buf.writeByte('<');
2068 foreach (i, o; *ti.tiargs)
2069 {
2070 if (i)
2071 buf.writestring(", ");
2072 if (auto tt = AST.isType(o))
2073 {
2074 tt.accept(this);
2075 }
2076 else
2077 {
2078 //ti.print();
2079 //o.print();
2080 assert(0);
2081 }
2082 }
2083 buf.writestring(" >");
2084 }
2085
2086 override void visit(AST.TemplateDeclaration td)
2087 {
2088 debug (Debug_DtoH) mixin(traceVisit!td);
2089
2090 if (!shouldEmitAndMarkVisited(td))
2091 return;
2092
2093 if (!td.parameters || !td.onemember || (!td.onemember.isStructDeclaration && !td.onemember.isClassDeclaration && !td.onemember.isFuncDeclaration))
2094 {
2095 visit(cast(AST.Dsymbol)td);
2096 return;
2097 }
2098
2099 // Explicitly disallow templates with non-type parameters or specialization.
2100 foreach (p; *td.parameters)
2101 {
2102 if (!p.isTemplateTypeParameter() || p.specialization())
2103 {
2104 visit(cast(AST.Dsymbol)td);
2105 return;
2106 }
2107 }
2108
2109 auto save = tdparent;
2110 tdparent = td;
2111 const bookmark = buf.length;
2112 printTemplateParams(td);
2113
2114 const oldIgnored = this.ignoredCounter;
2115 td.onemember.accept(this);
2116
2117 // Remove "template<...>" if the symbol could not be emitted
2118 if (oldIgnored != this.ignoredCounter)
2119 buf.setsize(bookmark);
2120
2121 tdparent = save;
2122 }
2123
2124 /// Writes the template<...> header for the supplied template declaration
2125 private void printTemplateParams(const AST.TemplateDeclaration td)
2126 {
2127 buf.writestring("template <");
2128 bool first = true;
2129 foreach (p; *td.parameters)
2130 {
2131 if (first)
2132 first = false;
2133 else
2134 buf.writestring(", ");
2135 buf.writestring("typename ");
2136 writeIdentifier(p.ident, p.loc, "template parameter", true);
2137 }
2138 buf.writestringln(">");
2139 }
2140
2141 /// Emit declarations of the TemplateMixin in the current scope
2142 override void visit(AST.TemplateMixin tm)
2143 {
2144 debug (Debug_DtoH) mixin(traceVisit!tm);
2145
2146 auto members = tm.members;
2147
2148 // members are missing for instances inside of TemplateDeclarations, e.g.
2149 // template Foo(T) { mixin Bar!T; }
2150 if (!members)
2151 {
2152 if (auto td = findTemplateDeclaration(tm))
2153 members = td.members; // Emit members of the template
2154 else
2155 return; // Cannot emit mixin
2156 }
2157
2158 foreach (s; *members)
2159 {
2160 // kind is undefined without semantic
2161 const kind = s.visible().kind;
2162 if (kind == AST.Visibility.Kind.public_ || kind == AST.Visibility.Kind.undefined)
2163 s.accept(this);
2164 }
2165 }
2166
2167 /**
2168 * Finds a symbol with the identifier `name` by iterating the linked list of parent
2169 * symbols, starting from `context`.
2170 *
2171 * Returns: the symbol or `null` if missing
2172 */
2173 private AST.Dsymbol findSymbol(Identifier name, AST.Dsymbol context)
2174 {
2175 // Follow the declaration context
2176 for (auto par = context; par; par = par.toParentDecl())
2177 {
2178 // Check that `name` doesn't refer to a template parameter
2179 if (auto td = par.isTemplateDeclaration())
2180 {
2181 foreach (const p; *td.parameters)
2182 {
2183 if (p.ident == name)
2184 return null;
2185 }
2186 }
2187
2188 if (auto mem = findMember(par, name))
2189 {
2190 return mem;
2191 }
2192 }
2193 return null;
2194 }
2195
2196 /// ditto
2197 private AST.Dsymbol findSymbol(Identifier name)
2198 {
2199 AST.Dsymbol sym;
2200 if (adparent)
2201 sym = findSymbol(name, adparent);
2202
2203 if (!sym && tdparent)
2204 sym = findSymbol(name, tdparent);
2205
2206 return sym;
2207 }
2208
2209 /// Finds the template declaration for instance `ti`
2210 private AST.TemplateDeclaration findTemplateDeclaration(AST.TemplateInstance ti)
2211 {
2212 if (ti.tempdecl)
2213 return ti.tempdecl.isTemplateDeclaration();
2214
2215 assert(tdparent); // Only missing inside of templates
2216
2217 // Search for the TemplateDeclaration, starting from the enclosing scope
2218 // if known or the enclosing template.
2219 auto sym = findSymbol(ti.name, ti.parent ? ti.parent : tdparent);
2220 return sym ? sym.isTemplateDeclaration() : null;
2221 }
2222
2223 override void visit(AST.TypeClass t)
2224 {
2225 debug (Debug_DtoH) mixin(traceVisit!t);
2226
2227 // Classes are emitted as pointer and hence can be forwarded
2228 const fwdSave = forwarding;
2229 forwarding = true;
2230 scope (exit) forwarding = fwdSave;
2231
2232 if (t.isConst() || t.isImmutable())
2233 buf.writestring("const ");
2234 writeFullName(t.sym);
2235 buf.writeByte('*');
2236 if (t.isConst() || t.isImmutable())
2237 buf.writestring(" const");
2238 }
2239
2240 /**
2241 * Writes the function signature to `buf`.
2242 *
2243 * Params:
2244 * fd = the function to print
2245 * tf = fd's type
2246 */
2247 private void funcToBuffer(AST.TypeFunction tf, AST.FuncDeclaration fd)
2248 {
2249 debug (Debug_DtoH)
2250 {
2251 printf("[funcToBuffer(AST.TypeFunction) enter] %s\n", fd.toChars());
2252 scope(exit) printf("[funcToBuffer(AST.TypeFunction) exit] %s\n", fd.toChars());
2253 }
2254
2255 auto originalType = cast(AST.TypeFunction)fd.originalType;
2256
2257 if (fd.isCtorDeclaration() || fd.isDtorDeclaration())
2258 {
2259 if (fd.isDtorDeclaration())
2260 {
2261 buf.writeByte('~');
2262 }
2263 buf.writestring(adparent.toChars());
2264 if (!tf)
2265 {
2266 assert(fd.isDtorDeclaration());
2267 buf.writestring("()");
2268 return;
2269 }
2270 }
2271 else
2272 {
2273 import dmd.root.string : toDString;
2274 assert(tf.next, fd.loc.toChars().toDString());
2275
2276 tf.next == AST.Type.tsize_t ? originalType.next.accept(this) : tf.next.accept(this);
2277 if (tf.isref)
2278 buf.writeByte('&');
2279 buf.writeByte(' ');
2280 writeIdentifier(fd);
2281 }
2282
2283 buf.writeByte('(');
2284 foreach (i, fparam; tf.parameterList)
2285 {
2286 if (i)
2287 buf.writestring(", ");
2288 if (fparam.type == AST.Type.tsize_t && originalType)
2289 {
2290 fparam = originalType.parameterList[i];
2291 }
2292 fparam.accept(this);
2293 }
2294 if (tf.parameterList.varargs)
2295 {
2296 if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1)
2297 buf.writestring(", ");
2298 buf.writestring("...");
2299 }
2300 buf.writeByte(')');
2301 }
2302
2303 override void visit(AST.Parameter p)
2304 {
2305 debug (Debug_DtoH) mixin(traceVisit!p);
2306
2307 ident = p.ident;
2308
2309 {
2310 // Reference parameters can be forwarded
2311 const fwdStash = this.forwarding;
2312 this.forwarding = !!(p.storageClass & AST.STC.ref_);
2313 p.type.accept(this);
2314 this.forwarding = fwdStash;
2315 }
2316
2317 if (p.storageClass & AST.STC.ref_)
2318 buf.writeByte('&');
2319 buf.writeByte(' ');
2320 if (ident)
2321 // FIXME: Parameter is missing a Loc
2322 writeIdentifier(ident, Loc.initial, "parameter", true);
2323 ident = null;
2324
2325 if (p.defaultArg)
2326 {
2327 //printf("%s %d\n", p.defaultArg.toChars, p.defaultArg.op);
2328 buf.writestring(" = ");
2329 printExpressionFor(p.type, p.defaultArg);
2330 }
2331 }
2332
2333 /**
2334 * Prints `exp` as an expression of type `target` while inserting
2335 * appropriate code when implicit conversion does not translate
2336 * directly to C++, e.g. from an enum to its base type.
2337 *
2338 * Params:
2339 * target = the type `exp` is converted to
2340 * exp = the expression to print
2341 * isCtor = if `exp` is a ctor argument
2342 */
2343 private void printExpressionFor(AST.Type target, AST.Expression exp, const bool isCtor = false)
2344 {
2345 /// Determines if a static_cast is required
2346 static bool needsCast(AST.Type target, AST.Expression exp)
2347 {
2348 // import std.stdio;
2349 // writefln("%s:%s: target = %s, type = %s (%s)", exp.loc.linnum, exp.loc.charnum, target, exp.type, exp.op);
2350
2351 auto source = exp.type;
2352
2353 // DotVarExp resolve conversions, e.g from an enum to its base type
2354 if (auto dve = exp.isDotVarExp())
2355 source = dve.var.type;
2356
2357 if (!source)
2358 // Defensively assume that the cast is required
2359 return true;
2360
2361 // Conversions from enum class to base type require static_cast
2362 if (global.params.cplusplus >= CppStdRevision.cpp11 &&
2363 source.isTypeEnum && !target.isTypeEnum)
2364 return true;
2365
2366 return false;
2367 }
2368
2369 // Slices are emitted as a special struct, hence we need to fix up
2370 // any expression initialising a slice variable/member
2371 if (auto ta = target.isTypeDArray())
2372 {
2373 if (exp.isNullExp())
2374 {
2375 if (isCtor)
2376 {
2377 // Don't emit, use default ctor
2378 }
2379 else if (global.params.cplusplus >= CppStdRevision.cpp11)
2380 {
2381 // Prefer initializer list
2382 buf.writestring("{}");
2383 }
2384 else
2385 {
2386 // Write __d_dynamic_array<TYPE>()
2387 visit(ta);
2388 buf.writestring("()");
2389 }
2390 return;
2391 }
2392
2393 if (auto se = exp.isStringExp())
2394 {
2395 // Rewrite as <length> + <literal> pair optionally
2396 // wrapped in a initializer list/ctor call
2397
2398 const initList = global.params.cplusplus >= CppStdRevision.cpp11;
2399 if (!isCtor)
2400 {
2401 if (initList)
2402 buf.writestring("{ ");
2403 else
2404 {
2405 visit(ta);
2406 buf.writestring("( ");
2407 }
2408 }
2409
2410 buf.printf("%zu, ", se.len);
2411 visit(se);
2412
2413 if (!isCtor)
2414 buf.writestring(initList ? " }" : " )");
2415
2416 return;
2417 }
2418 }
2419 else if (auto ce = exp.isCastExp())
2420 {
2421 buf.writeByte('(');
2422 if (ce.to)
2423 ce.to.accept(this);
2424 else if (ce.e1.type)
2425 // Try the expression type with modifiers in case of cast(const) in templates
2426 ce.e1.type.castMod(ce.mod).accept(this);
2427 else
2428 // Fallback, not necessarily correct but the best we've got here
2429 target.accept(this);
2430 buf.writestring(") ");
2431 ce.e1.accept(this);
2432 }
2433 else if (needsCast(target, exp))
2434 {
2435 buf.writestring("static_cast<");
2436 target.accept(this);
2437 buf.writestring(">(");
2438 exp.accept(this);
2439 buf.writeByte(')');
2440 }
2441 else
2442 {
2443 exp.accept(this);
2444 }
2445 }
2446
2447 override void visit(AST.Expression e)
2448 {
2449 debug (Debug_DtoH) mixin(traceVisit!e);
2450
2451 // Valid in most cases, others should be overriden below
2452 // to use the appropriate operators (:: and ->)
2453 buf.writestring(e.toString());
2454 }
2455
2456 override void visit(AST.UnaExp e)
2457 {
2458 debug (Debug_DtoH) mixin(traceVisit!e);
2459
2460 buf.writestring(expToString(e.op));
2461 e.e1.accept(this);
2462 }
2463
2464 override void visit(AST.BinExp e)
2465 {
2466 debug (Debug_DtoH) mixin(traceVisit!e);
2467
2468 e.e1.accept(this);
2469 buf.writeByte(' ');
2470 buf.writestring(expToString(e.op));
2471 buf.writeByte(' ');
2472 e.e2.accept(this);
2473 }
2474
2475 /// Translates operator `op` into the C++ representation
2476 private extern(D) static string expToString(const EXP op)
2477 {
2478 switch (op) with (EXP)
2479 {
2480 case identity: return "==";
2481 case notIdentity: return "!=";
2482 default:
2483 return EXPtoString(op);
2484 }
2485 }
2486
2487 override void visit(AST.VarExp e)
2488 {
2489 debug (Debug_DtoH) mixin(traceVisit!e);
2490
2491 // Local members don't need another prefix and might've been renamed
2492 if (e.var.isThis())
2493 {
2494 includeSymbol(e.var);
2495 writeIdentifier(e.var, true);
2496 }
2497 else
2498 writeFullName(e.var);
2499 }
2500
2501 /// Partially prints the FQN including parent aggregates
2502 private void printPrefix(AST.Dsymbol var)
2503 {
2504 if (!var || var is adparent || var.isModule())
2505 return;
2506
2507 writeFullName(var);
2508 buf.writestring("::");
2509 }
2510
2511 override void visit(AST.CallExp e)
2512 {
2513 debug (Debug_DtoH) mixin(traceVisit!e);
2514
2515 // Dereferencing function pointers requires additional braces: (*f)(args)
2516 const isFp = e.e1.isPtrExp();
2517 if (isFp)
2518 buf.writeByte('(');
2519 else if (e.f)
2520 includeSymbol(outermostSymbol(e.f));
2521
2522 e.e1.accept(this);
2523
2524 if (isFp) buf.writeByte(')');
2525
2526 assert(e.arguments);
2527 buf.writeByte('(');
2528 foreach (i, arg; *e.arguments)
2529 {
2530 if (i)
2531 buf.writestring(", ");
2532 arg.accept(this);
2533 }
2534 buf.writeByte(')');
2535 }
2536
2537 override void visit(AST.DotVarExp e)
2538 {
2539 debug (Debug_DtoH) mixin(traceVisit!e);
2540
2541 if (auto sym = symbolFromType(e.e1.type))
2542 includeSymbol(outermostSymbol(sym));
2543
2544 // Accessing members through a pointer?
2545 if (auto pe = e.e1.isPtrExp)
2546 {
2547 pe.e1.accept(this);
2548 buf.writestring("->");
2549 }
2550 else
2551 {
2552 e.e1.accept(this);
2553 buf.writeByte('.');
2554 }
2555
2556 // Should only be used to access non-static members
2557 assert(e.var.isThis());
2558
2559 writeIdentifier(e.var, true);
2560 }
2561
2562 override void visit(AST.DotIdExp e)
2563 {
2564 debug (Debug_DtoH) mixin(traceVisit!e);
2565
2566 e.e1.accept(this);
2567 buf.writestring("::");
2568 buf.writestring(e.ident.toChars());
2569 }
2570
2571 override void visit(AST.ScopeExp e)
2572 {
2573 debug (Debug_DtoH) mixin(traceVisit!e);
2574
2575 // Usually a template instance in a TemplateDeclaration
2576 if (auto ti = e.sds.isTemplateInstance())
2577 visitTi(ti);
2578 else
2579 writeFullName(e.sds);
2580 }
2581
2582 override void visit(AST.NullExp e)
2583 {
2584 debug (Debug_DtoH) mixin(traceVisit!e);
2585
2586 if (global.params.cplusplus >= CppStdRevision.cpp11)
2587 buf.writestring("nullptr");
2588 else
2589 buf.writestring("NULL");
2590 }
2591
2592 override void visit(AST.ArrayLiteralExp e)
2593 {
2594 debug (Debug_DtoH) mixin(traceVisit!e);
2595 buf.writestring("arrayliteral");
2596 }
2597
2598 override void visit(AST.StringExp e)
2599 {
2600 debug (Debug_DtoH) mixin(traceVisit!e);
2601
2602 if (e.sz == 2)
2603 buf.writeByte('u');
2604 else if (e.sz == 4)
2605 buf.writeByte('U');
2606 buf.writeByte('"');
2607
2608 foreach (i; 0 .. e.len)
2609 {
2610 writeCharLiteral(*buf, e.getCodeUnit(i));
2611 }
2612 buf.writeByte('"');
2613 }
2614
2615 override void visit(AST.RealExp e)
2616 {
2617 debug (Debug_DtoH) mixin(traceVisit!e);
2618
2619 import dmd.root.ctfloat : CTFloat;
2620
2621 // Special case NaN and Infinity because floatToBuffer
2622 // uses D literals (`nan` and `infinity`)
2623 if (CTFloat.isNaN(e.value))
2624 {
2625 buf.writestring("NAN");
2626 }
2627 else if (CTFloat.isInfinity(e.value))
2628 {
2629 if (e.value < CTFloat.zero)
2630 buf.writeByte('-');
2631 buf.writestring("INFINITY");
2632 }
2633 else
2634 {
2635 import dmd.hdrgen;
2636 // Hex floating point literals were introduced in C++ 17
2637 const allowHex = global.params.cplusplus >= CppStdRevision.cpp17;
2638 floatToBuffer(e.type, e.value, buf, allowHex);
2639 }
2640 }
2641
2642 override void visit(AST.IntegerExp e)
2643 {
2644 debug (Debug_DtoH) mixin(traceVisit!e);
2645 visitInteger(e.toInteger, e.type);
2646 }
2647
2648 /// Writes `v` as type `t` into `buf`
2649 private void visitInteger(dinteger_t v, AST.Type t)
2650 {
2651 debug (Debug_DtoH) mixin(traceVisit!t);
2652
2653 switch (t.ty)
2654 {
2655 case AST.Tenum:
2656 auto te = cast(AST.TypeEnum)t;
2657 buf.writestring("(");
2658 enumToBuffer(te.sym);
2659 buf.writestring(")");
2660 visitInteger(v, te.sym.memtype);
2661 break;
2662 case AST.Tbool:
2663 buf.writestring(v ? "true" : "false");
2664 break;
2665 case AST.Tint8:
2666 buf.printf("%d", cast(byte)v);
2667 break;
2668 case AST.Tuns8:
2669 buf.printf("%uu", cast(ubyte)v);
2670 break;
2671 case AST.Tint16:
2672 buf.printf("%d", cast(short)v);
2673 break;
2674 case AST.Tuns16:
2675 case AST.Twchar:
2676 buf.printf("%uu", cast(ushort)v);
2677 break;
2678 case AST.Tint32:
2679 case AST.Tdchar:
2680 buf.printf("%d", cast(int)v);
2681 break;
2682 case AST.Tuns32:
2683 buf.printf("%uu", cast(uint)v);
2684 break;
2685 case AST.Tint64:
2686 buf.printf("%lldLL", v);
2687 break;
2688 case AST.Tuns64:
2689 buf.printf("%lluLLU", v);
2690 break;
2691 case AST.Tchar:
2692 if (v > 0x20 && v < 0x80)
2693 buf.printf("'%c'", cast(int)v);
2694 else
2695 buf.printf("%uu", cast(ubyte)v);
2696 break;
2697 default:
2698 //t.print();
2699 assert(0);
2700 }
2701 }
2702
2703 override void visit(AST.StructLiteralExp sle)
2704 {
2705 debug (Debug_DtoH) mixin(traceVisit!sle);
2706
2707 const isUnion = sle.sd.isUnionDeclaration();
2708 sle.sd.type.accept(this);
2709 buf.writeByte('(');
2710 foreach(i, e; *sle.elements)
2711 {
2712 if (i)
2713 buf.writestring(", ");
2714
2715 auto vd = sle.sd.fields[i];
2716
2717 // Expression may be null for unspecified elements
2718 if (!e)
2719 e = findDefaultInitializer(vd);
2720
2721 printExpressionFor(vd.type, e);
2722
2723 // Only emit the initializer of the first union member
2724 if (isUnion)
2725 break;
2726 }
2727 buf.writeByte(')');
2728 }
2729
2730 /// Finds the default initializer for the given VarDeclaration
2731 private static AST.Expression findDefaultInitializer(AST.VarDeclaration vd)
2732 {
2733 if (vd._init && !vd._init.isVoidInitializer())
2734 return AST.initializerToExpression(vd._init);
2735 else if (auto ts = vd.type.isTypeStruct())
2736 {
2737 if (!ts.sym.noDefaultCtor && !ts.sym.isUnionDeclaration())
2738 {
2739 // Generate a call to the default constructor that we've generated.
2740 auto sle = new AST.StructLiteralExp(Loc.initial, ts.sym, new AST.Expressions(0));
2741 sle.type = vd.type;
2742 return sle;
2743 }
2744 else
2745 return vd.type.defaultInitLiteral(Loc.initial);
2746 }
2747 else
2748 return vd.type.defaultInitLiteral(Loc.initial);
2749 }
2750
2751 static if (__VERSION__ < 2092)
2752 {
2753 private void ignored(const char* format, ...) nothrow
2754 {
2755 this.ignoredCounter++;
2756
2757 import core.stdc.stdarg;
2758 if (!printIgnored)
2759 return;
2760
2761 va_list ap;
2762 va_start(ap, format);
2763 buf.writestring("// Ignored ");
2764 buf.vprintf(format, ap);
2765 buf.writenl();
2766 va_end(ap);
2767 }
2768 }
2769 else
2770 {
2771 /// Writes a formatted message into `buf` if `printIgnored` is true
2772 /// and increments `ignoredCounter`
2773 pragma(printf)
2774 private void ignored(const char* format, ...) nothrow
2775 {
2776 this.ignoredCounter++;
2777
2778 import core.stdc.stdarg;
2779 if (!printIgnored)
2780 return;
2781
2782 va_list ap;
2783 va_start(ap, format);
2784 buf.writestring("// Ignored ");
2785 buf.vprintf(format, ap);
2786 buf.writenl();
2787 va_end(ap);
2788 }
2789 }
2790
2791 /**
2792 * Determines whether `s` should be emitted. This requires that `sym`
2793 * - is `extern(C[++]`)
2794 * - is not instantiated from a template (visits the `TemplateDeclaration` instead)
2795 *
2796 * Params:
2797 * sym = the symbol
2798 *
2799 * Returns: whether `sym` should be emitted
2800 */
2801 private bool shouldEmit(AST.Dsymbol sym)
2802 {
2803 import dmd.aggregate : ClassKind;
2804 debug (Debug_DtoH)
2805 {
2806 printf("[shouldEmitAndMarkVisited enter] %s\n", sym.toPrettyChars());
2807 scope(exit) printf("[shouldEmitAndMarkVisited exit] %s\n", sym.toPrettyChars());
2808 }
2809
2810 // Template *instances* should not be emitted
2811 if (sym.isInstantiated())
2812 return false;
2813
2814 // Matching linkage (except extern(C) classes which don't make sense)
2815 if (linkage == LINK.cpp || (linkage == LINK.c && !sym.isClassDeclaration()))
2816 return true;
2817
2818 // Check against the internal information which might be missing, e.g. inside of template declarations
2819 if (auto dec = sym.isDeclaration())
2820 {
2821 const l = dec.resolvedLinkage();
2822 return l == LINK.cpp || l == LINK.c;
2823 }
2824
2825 if (auto ad = sym.isAggregateDeclaration())
2826 return ad.classKind == ClassKind.cpp;
2827
2828 return false;
2829 }
2830
2831 /**
2832 * Determines whether `s` should be emitted. This requires that `sym`
2833 * - was not visited before
2834 * - is `extern(C[++]`)
2835 * - is not instantiated from a template (visits the `TemplateDeclaration` instead)
2836 * The result is cached in the visited nodes array.
2837 *
2838 * Params:
2839 * sym = the symbol
2840 *
2841 * Returns: whether `sym` should be emitted
2842 **/
2843 private bool shouldEmitAndMarkVisited(AST.Dsymbol sym)
2844 {
2845 debug (Debug_DtoH)
2846 {
2847 printf("[shouldEmitAndMarkVisited enter] %s\n", sym.toPrettyChars());
2848 scope(exit) printf("[shouldEmitAndMarkVisited exit] %s\n", sym.toPrettyChars());
2849 }
2850
2851 auto statePtr = (cast(void*) sym) in visited;
2852
2853 // `sym` was already emitted or skipped and isn't required
2854 if (statePtr && (*statePtr || !mustEmit))
2855 return false;
2856
2857 // Template *instances* should not be emitted, forward to the declaration
2858 if (auto ti = sym.isInstantiated())
2859 {
2860 auto td = findTemplateDeclaration(ti);
2861 assert(td);
2862 visit(td);
2863 return false;
2864 }
2865
2866 // Required or matching linkage (except extern(C) classes which don't make sense)
2867 bool res = mustEmit || linkage == LINK.cpp || (linkage == LINK.c && !sym.isClassDeclaration());
2868 if (!res)
2869 {
2870 // Check against the internal information which might be missing, e.g. inside of template declarations
2871 if (auto dec = sym.isDeclaration())
2872 {
2873 const l = dec.resolvedLinkage();
2874 res = (l == LINK.cpp || l == LINK.c);
2875 }
2876 }
2877
2878 // Remember result for later calls
2879 if (statePtr)
2880 *statePtr = res;
2881 else
2882 visited[(cast(void*) sym)] = res;
2883
2884 // Print a warning when the symbol is ignored for the first time
2885 // Might not be correct if it is required by symbol the is visited
2886 // AFTER the current node
2887 if (!statePtr && !res)
2888 ignored("%s %s because of linkage", sym.kind(), sym.toPrettyChars());
2889
2890 return res;
2891 }
2892
2893 /**
2894 * Ensures that `sym` is declared before the current position in `buf` by
2895 * either creating a forward reference in `fwdbuf` if possible or
2896 * calling `includeSymbol` to emit the entire declaration into `donebuf`.
2897 */
2898 private void ensureDeclared(AST.Dsymbol sym)
2899 {
2900 auto par = sym.toParent2();
2901 auto ed = sym.isEnumDeclaration();
2902
2903 // Eagerly include the symbol if we cannot create a valid forward declaration
2904 // Forwarding of scoped enums requires C++11 or above
2905 if (!forwarding || (par && !par.isModule()) || (ed && global.params.cplusplus < CppStdRevision.cpp11))
2906 {
2907 // Emit the entire enclosing declaration if any
2908 includeSymbol(outermostSymbol(sym));
2909 return;
2910 }
2911
2912 auto ti = sym.isInstantiated();
2913 auto td = ti ? findTemplateDeclaration(ti) : null;
2914 auto check = cast(void*) (td ? td : sym);
2915
2916 // Omit redundant fwd-declaration if we already emitted the entire declaration
2917 if (visited.get(check, false))
2918 return;
2919
2920 // Already created a fwd-declaration?
2921 if (check in forwarded)
2922 return;
2923 forwarded[check] = true;
2924
2925 // Print template<...>
2926 if (ti)
2927 {
2928 auto bufSave = buf;
2929 buf = fwdbuf;
2930 printTemplateParams(td);
2931 buf = bufSave;
2932 }
2933
2934 // Determine the kind of symbol that is forwared: struct, ...
2935 const(char)* kind;
2936
2937 if (auto ad = sym.isAggregateDeclaration())
2938 {
2939 // Look for extern(C++, class) <some aggregate>
2940 if (ad.cppmangle == CPPMANGLE.def)
2941 kind = ad.kind();
2942 else if (ad.cppmangle == CPPMANGLE.asStruct)
2943 kind = "struct";
2944 else
2945 kind = "class";
2946 }
2947 else if (ed)
2948 {
2949 // Only called from enumToBuffer, so should always be emitted as an actual enum
2950 kind = "enum class";
2951 }
2952 else
2953 kind = sym.kind(); // Should be unreachable but just to be sure
2954
2955 fwdbuf.writestring(kind);
2956 fwdbuf.writeByte(' ');
2957 fwdbuf.writestring(sym.toChars());
2958 fwdbuf.writestringln(";");
2959 }
2960
2961 /**
2962 * Writes the qualified name of `sym` into `buf` including parent
2963 * symbols and template parameters.
2964 *
2965 * Params:
2966 * sym = the symbol
2967 * mustInclude = whether sym may not be forward declared
2968 */
2969 private void writeFullName(AST.Dsymbol sym, const bool mustInclude = false)
2970 in
2971 {
2972 assert(sym);
2973 assert(sym.ident, sym.toString());
2974 // Should never be called directly with a TI, only onemember
2975 assert(!sym.isTemplateInstance(), sym.toString());
2976 }
2977 do
2978 {
2979 debug (Debug_DtoH)
2980 {
2981 printf("[writeFullName enter] %s\n", sym.toPrettyChars());
2982 scope(exit) printf("[writeFullName exit] %s\n", sym.toPrettyChars());
2983 }
2984
2985 // Explicit `pragma(mangle, "<some string>` overrides the declared name
2986 if (auto mn = getMangleOverride(sym))
2987 return buf.writestring(mn);
2988
2989 /// Checks whether `sym` is nested in `par` and hence doesn't need the FQN
2990 static bool isNestedIn(AST.Dsymbol sym, AST.Dsymbol par)
2991 {
2992 while (par)
2993 {
2994 if (sym is par)
2995 return true;
2996 par = par.toParent();
2997 }
2998 return false;
2999 }
3000 AST.TemplateInstance ti;
3001 bool nested;
3002
3003 // Check if the `sym` is nested into another symbol and hence requires `Parent::sym`
3004 if (auto par = sym.toParent())
3005 {
3006 // toParent() yields the template instance if `sym` is the onemember of a TI
3007 ti = par.isTemplateInstance();
3008
3009 // Skip the TI because Foo!int.Foo is folded into Foo<int>
3010 if (ti) par = ti.toParent();
3011
3012 // Prefix the name with any enclosing declaration
3013 // Stop at either module or enclosing aggregate
3014 nested = !par.isModule();
3015 if (nested && !isNestedIn(par, adparent))
3016 {
3017 writeFullName(par, true);
3018 buf.writestring("::");
3019 }
3020 }
3021
3022 if (!nested)
3023 {
3024 // Cannot forward the symbol when called recursively
3025 // for a nested symbol
3026 if (mustInclude)
3027 includeSymbol(sym);
3028 else
3029 ensureDeclared(sym);
3030 }
3031
3032 if (ti)
3033 visitTi(ti);
3034 else
3035 buf.writestring(sym.ident.toString());
3036 }
3037
3038 /// Returns: Explicit mangling for `sym` if present
3039 extern(D) static const(char)[] getMangleOverride(const AST.Dsymbol sym)
3040 {
3041 if (auto decl = sym.isDeclaration())
3042 return decl.mangleOverride;
3043
3044 return null;
3045 }
3046 }
3047
3048 /// Namespace for identifiers used to represent special enums in C++
3049 struct DMDType
3050 {
3051 __gshared Identifier c_long;
3052 __gshared Identifier c_ulong;
3053 __gshared Identifier c_longlong;
3054 __gshared Identifier c_ulonglong;
3055 __gshared Identifier c_long_double;
3056 __gshared Identifier c_char;
3057 __gshared Identifier c_wchar_t;
3058 __gshared Identifier c_complex_float;
3059 __gshared Identifier c_complex_double;
3060 __gshared Identifier c_complex_real;
3061
3062 static void _init()
3063 {
3064 c_long = Identifier.idPool("__c_long");
3065 c_ulong = Identifier.idPool("__c_ulong");
3066 c_longlong = Identifier.idPool("__c_longlong");
3067 c_ulonglong = Identifier.idPool("__c_ulonglong");
3068 c_long_double = Identifier.idPool("__c_long_double");
3069 c_wchar_t = Identifier.idPool("__c_wchar_t");
3070 c_char = Identifier.idPool("__c_char");
3071 c_complex_float = Identifier.idPool("__c_complex_float");
3072 c_complex_double = Identifier.idPool("__c_complex_double");
3073 c_complex_real = Identifier.idPool("__c_complex_real");
3074 }
3075 }
3076
3077 /// Initializes all data structures used by the header generator
3078 void initialize()
3079 {
3080 __gshared bool initialized;
3081
3082 if (!initialized)
3083 {
3084 initialized = true;
3085
3086 DMDType._init();
3087 }
3088 }
3089
3090 /// Writes `#if <content>` into the supplied buffer
3091 void hashIf(ref OutBuffer buf, string content)
3092 {
3093 buf.writestring("#if ");
3094 buf.writestringln(content);
3095 }
3096
3097 /// Writes `#elif <content>` into the supplied buffer
3098 void hashElIf(ref OutBuffer buf, string content)
3099 {
3100 buf.writestring("#elif ");
3101 buf.writestringln(content);
3102 }
3103
3104 /// Writes `#endif` into the supplied buffer
3105 void hashEndIf(ref OutBuffer buf)
3106 {
3107 buf.writestringln("#endif");
3108 }
3109
3110 /// Writes `#define <content>` into the supplied buffer
3111 void hashDefine(ref OutBuffer buf, string content)
3112 {
3113 buf.writestring("#define ");
3114 buf.writestringln(content);
3115 }
3116
3117 /// Writes `#include <content>` into the supplied buffer
3118 void hashInclude(ref OutBuffer buf, string content)
3119 {
3120 buf.writestring("#include ");
3121 buf.writestringln(content);
3122 }
3123
3124 /// Determines whether `ident` is a reserved keyword in C++
3125 /// Returns: the kind of keyword or `null`
3126 const(char*) keywordClass(const Identifier ident)
3127 {
3128 if (!ident)
3129 return null;
3130
3131 const name = ident.toString();
3132 switch (name)
3133 {
3134 // C++ operators
3135 case "and":
3136 case "and_eq":
3137 case "bitand":
3138 case "bitor":
3139 case "compl":
3140 case "not":
3141 case "not_eq":
3142 case "or":
3143 case "or_eq":
3144 case "xor":
3145 case "xor_eq":
3146 return "special operator in C++";
3147
3148 // C++ keywords
3149 case "_Complex":
3150 case "const_cast":
3151 case "delete":
3152 case "dynamic_cast":
3153 case "explicit":
3154 case "friend":
3155 case "inline":
3156 case "mutable":
3157 case "namespace":
3158 case "operator":
3159 case "register":
3160 case "reinterpret_cast":
3161 case "signed":
3162 case "static_cast":
3163 case "typedef":
3164 case "typename":
3165 case "unsigned":
3166 case "using":
3167 case "virtual":
3168 case "volatile":
3169 return "keyword in C++";
3170
3171 // Common macros imported by this header
3172 // stddef.h
3173 case "offsetof":
3174 case "NULL":
3175 return "default macro in C++";
3176
3177 // C++11 keywords
3178 case "alignas":
3179 case "alignof":
3180 case "char16_t":
3181 case "char32_t":
3182 case "constexpr":
3183 case "decltype":
3184 case "noexcept":
3185 case "nullptr":
3186 case "static_assert":
3187 case "thread_local":
3188 case "wchar_t":
3189 if (global.params.cplusplus >= CppStdRevision.cpp11)
3190 return "keyword in C++11";
3191 return null;
3192
3193 // C++20 keywords
3194 case "char8_t":
3195 case "consteval":
3196 case "constinit":
3197 // Concepts-related keywords
3198 case "concept":
3199 case "requires":
3200 // Coroutines-related keywords
3201 case "co_await":
3202 case "co_yield":
3203 case "co_return":
3204 if (global.params.cplusplus >= CppStdRevision.cpp20)
3205 return "keyword in C++20";
3206 return null;
3207
3208 default:
3209 // Identifiers starting with __ are reserved
3210 if (name.length >= 2 && name[0..2] == "__")
3211 return "reserved identifier in C++";
3212
3213 return null;
3214 }
3215 }
3216
3217 /// Finds the outermost symbol if `sym` is nested.
3218 /// Returns `sym` if it appears at module scope
3219 ASTCodegen.Dsymbol outermostSymbol(ASTCodegen.Dsymbol sym)
3220 {
3221 assert(sym);
3222 while (true)
3223 {
3224 auto par = sym.toParent();
3225 if (!par || par.isModule())
3226 return sym;
3227 sym = par;
3228 }
3229 }
3230
3231 /// Fetches the symbol for user-defined types from the type `t`
3232 /// if `t` is either `TypeClass`, `TypeStruct` or `TypeEnum`
3233 ASTCodegen.Dsymbol symbolFromType(ASTCodegen.Type t)
3234 {
3235 if (auto tc = t.isTypeClass())
3236 return tc.sym;
3237 if (auto ts = t.isTypeStruct())
3238 return ts.sym;
3239 if (auto te = t.isTypeEnum())
3240 return te.sym;
3241 return null;
3242 }
3243
3244 /**
3245 * Searches `sym` for a member with the given name.
3246 *
3247 * This method usually delegates to `Dsymbol.search` but might also
3248 * manually check the members if the symbol did not receive semantic
3249 * analysis.
3250 *
3251 * Params:
3252 * sym = symbol to search
3253 * name = identifier of the requested symbol
3254 *
3255 * Returns: the symbol or `null` if not found
3256 */
3257 ASTCodegen.Dsymbol findMember(ASTCodegen.Dsymbol sym, Identifier name)
3258 {
3259 if (auto mem = sym.search(Loc.initial, name, ASTCodegen.IgnoreErrors))
3260 return mem;
3261
3262 // search doesn't work for declarations inside of uninstantiated
3263 // `TemplateDeclaration`s due to the missing symtab.
3264 if (sym.semanticRun >= ASTCodegen.PASS.semanticdone)
3265 return null;
3266
3267 // Manually check the members if present
3268 auto sds = sym.isScopeDsymbol();
3269 if (!sds || !sds.members)
3270 return null;
3271
3272 /// Recursively searches for `name` without entering nested aggregates, ...
3273 static ASTCodegen.Dsymbol search(ASTCodegen.Dsymbols* members, Identifier name)
3274 {
3275 foreach (mem; *members)
3276 {
3277 if (mem.ident == name)
3278 return mem;
3279
3280 // Look inside of private:, ...
3281 if (auto ad = mem.isAttribDeclaration())
3282 {
3283 if (auto s = search(ad.decl, name))
3284 return s;
3285 }
3286 }
3287 return null;
3288 }
3289
3290 return search(sds.members, name);
3291 }
3292
3293 debug (Debug_DtoH)
3294 {
3295 /// Generates code to trace the entry and exit of the enclosing `visit` function
3296 string traceVisit(alias node)()
3297 {
3298 const type = typeof(node).stringof;
3299 const method = __traits(hasMember, node, "toPrettyChars") ? "toPrettyChars" : "toChars";
3300 const arg = __traits(identifier, node) ~ '.' ~ method;
3301
3302 return `printf("[` ~ type ~ ` enter] %s\n", ` ~ arg ~ `());
3303 scope(exit) printf("[` ~ type ~ ` exit] %s\n", ` ~ arg ~ `());`;
3304 }
3305 }
3306