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