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-2021 by The D Language Foundation, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://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.arraytypes;
20 import dmd.dsymbol;
21 import dmd.errors;
22 import dmd.globals;
23 import dmd.identifier;
24 import dmd.root.filename;
25 import dmd.visitor;
26 import dmd.tokens;
27 
28 import dmd.root.outbuffer;
29 import dmd.utils;
30 
31 //debug = Debug_DtoH;
32 
33 private struct DMDType
34 {
35     __gshared Identifier c_long;
36     __gshared Identifier c_ulong;
37     __gshared Identifier c_longlong;
38     __gshared Identifier c_ulonglong;
39     __gshared Identifier c_long_double;
40     __gshared Identifier c_wchar_t;
41     __gshared Identifier c_complex_float;
42     __gshared Identifier c_complex_double;
43     __gshared Identifier c_complex_real;
44 
45     static void _init()
46     {
47         c_long          = Identifier.idPool("__c_long");
48         c_ulong         = Identifier.idPool("__c_ulong");
49         c_longlong      = Identifier.idPool("__c_longlong");
50         c_ulonglong     = Identifier.idPool("__c_ulonglong");
51         c_long_double   = Identifier.idPool("__c_long_double");
52         c_wchar_t       = Identifier.idPool("__c_wchar_t");
53         c_complex_float  = Identifier.idPool("__c_complex_float");
54         c_complex_double = Identifier.idPool("__c_complex_double");
55         c_complex_real = Identifier.idPool("__c_complex_real");
56     }
57 }
58 
59 private void initialize()
60 {
61     __gshared bool initialized;
62 
63     if (!initialized)
64     {
65         initialized = true;
66 
67         DMDType._init();
68     }
69 }
70 
71 void hashIf(ref OutBuffer buf, string content)
72 {
73     buf.writestring("#if ");
74     buf.writestringln(content);
75 }
76 
77 void hashElIf(ref OutBuffer buf, string content)
78 {
79     buf.writestring("#elif ");
80     buf.writestringln(content);
81 }
82 
83 void hashEndIf(ref OutBuffer buf)
84 {
85     buf.writestringln("#endif");
86 }
87 
88 void hashDefine(ref OutBuffer buf, string content)
89 {
90     buf.writestring("# define ");
91     buf.writestringln(content);
92 }
93 
94 void hashInclude(ref OutBuffer buf, string content)
95 {
96     buf.writestring("#include ");
97     buf.writestringln(content);
98 }
99 
100 
101 
102 extern(C++) void genCppHdrFiles(ref Modules ms)
103 {
104     initialize();
105 
106     OutBuffer fwd;
107     OutBuffer check;
108     OutBuffer done;
109     OutBuffer decl;
110 
111     // enable indent by spaces on buffers
112     fwd.doindent = true;
113     fwd.spaces = true;
114     decl.doindent = true;
115     decl.spaces = true;
116     check.doindent = true;
117     check.spaces = true;
118 
119     scope v = new ToCppBuffer(&check, &fwd, &done, &decl);
120 
121     OutBuffer buf;
122     buf.doindent = true;
123     buf.spaces = true;
124 
125     foreach (m; ms)
126         m.accept(v);
127 
128     if (global.params.doCxxHdrGeneration == CxxHeaderMode.verbose)
129         buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber());
130     else
131         buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr);
132 
133     buf.writenl();
134     buf.writenl();
135     buf.writestringln("#pragma once");
136     buf.writenl();
137     hashInclude(buf, "<assert.h>");
138     hashInclude(buf, "<stddef.h>");
139     hashInclude(buf, "<stdint.h>");
140     hashInclude(buf, "<math.h>");
141 //    buf.writestring(buf, "#include <stdio.h>\n");
142 //    buf.writestring("#include <string.h>\n");
143 
144     // Emit array compatibility because extern(C++) types may have slices
145     // as members (as opposed to function parameters)
146     buf.writestring(`
147 #ifdef CUSTOM_D_ARRAY_TYPE
148 #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
149 #else
150 /// Represents a D [] array
151 template<typename T>
152 struct _d_dynamicArray
153 {
154     size_t length;
155     T *ptr;
156 
157     _d_dynamicArray() : length(0), ptr(NULL) { }
158 
159     _d_dynamicArray(size_t length_in, T *ptr_in)
160         : length(length_in), ptr(ptr_in) { }
161 
162     T& operator[](const size_t idx) {
163         assert(idx < length);
164         return ptr[idx];
165     }
166 
167     const T& operator[](const size_t idx) const {
168         assert(idx < length);
169         return ptr[idx];
170     }
171 };
172 #endif
173 `);
174 
175     if (v.hasReal)
176     {
177         hashIf(buf, "!defined(_d_real)");
178         {
179             hashDefine(buf, "_d_real long double");
180         }
181         hashEndIf(buf);
182     }
183     buf.writenl();
184     // buf.writestringln("// fwd:");
185     buf.write(&fwd);
186     if (fwd.length > 0)
187         buf.writenl();
188 
189     // buf.writestringln("// done:");
190     buf.write(&done);
191 
192     // buf.writestringln("// decl:");
193     buf.write(&decl);
194 
195     debug (Debug_DtoH)
196     {
197         // buf.writestringln("// check:");
198         buf.writestring(`
199 #if OFFSETS
200     template <class T>
201     size_t getSlotNumber(int dummy, ...)
202     {
203         T c;
204         va_list ap;
205         va_start(ap, dummy);
206 
207         void *f = va_arg(ap, void*);
208         for (size_t i = 0; ; i++)
209         {
210             if ( (*(void***)&c)[i] == f)
211             return i;
212         }
213         va_end(ap);
214     }
215 
216     void testOffsets()
217     {
218 `);
219         buf.write(&check);
220         buf.writestring(`
221     }
222 #endif
223 `);
224     }
225 
226     if (global.params.cxxhdrname is null)
227     {
228         // Write to stdout; assume it succeeds
229         size_t n = fwrite(buf[].ptr, 1, buf.length, stdout);
230         assert(n == buf.length); // keep gcc happy about return values
231     }
232     else
233     {
234         const(char)[] name = FileName.combine(global.params.cxxhdrdir, global.params.cxxhdrname);
235         writeFile(Loc.initial, name, buf[]);
236     }
237 }
238 
239 /****************************************************
240  */
241 extern(C++) final class ToCppBuffer : Visitor
242 {
243     alias visit = Visitor.visit;
244 public:
245     enum EnumKind
246     {
247         Int,
248         Numeric,
249         String,
250         Enum,
251         Other
252     }
253 
254     alias AST = ASTCodegen;
255 
256     bool[void*] visited;
257     bool[void*] forwarded;
258     OutBuffer* fwdbuf;
259     OutBuffer* checkbuf;
260     OutBuffer* donebuf;
261     OutBuffer* buf;
262     AST.AggregateDeclaration adparent;
263     AST.TemplateDeclaration tdparent;
264     Identifier ident;
265     LINK linkage = LINK.d;
266     bool forwardedAA;
267     AST.Type* origType;
268     /// Last written visibility level
269     AST.Visibility.Kind currentVisibility;
270     AST.STC storageClass; /// Currently applicable storage classes
271 
272     int ignoredCounter; /// How many symbols were ignored
273     bool hasReal;
274     const bool printIgnored;
275 
276     this(OutBuffer* checkbuf, OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf)
277     {
278         this.checkbuf = checkbuf;
279         this.fwdbuf = fwdbuf;
280         this.donebuf = donebuf;
281         this.buf = buf;
282         this.printIgnored = global.params.doCxxHdrGeneration == CxxHeaderMode.verbose;
283     }
284 
285     /// Visit `dsym` with `buf` while temporarily clearing **parent fields
286     private void visitAsRoot(AST.Dsymbol dsym, OutBuffer* buf)
287     {
288         const stcStash = this.storageClass;
289         auto adStash = this.adparent;
290         auto tdStash = this.tdparent;
291         auto bufStash = this.buf;
292         auto countStash = this.ignoredCounter;
293 
294         this.storageClass = AST.STC.undefined_;
295         this.adparent = null;
296         this.tdparent = null;
297         this.buf = buf;
298 
299         dsym.accept(this);
300 
301         this.storageClass = stcStash;
302         this.adparent = adStash;
303         this.tdparent = tdStash;
304         this.buf = bufStash;
305         this.ignoredCounter = countStash;
306     }
307 
308     private EnumKind getEnumKind(AST.Type type)
309     {
310         if (type) switch (type.ty)
311         {
312             case AST.Tint32:
313                 return EnumKind.Int;
314             case AST.Tbool,
315                 AST.Tchar, AST.Twchar, AST.Tdchar,
316                 AST.Tint8, AST.Tuns8,
317                 AST.Tint16, AST.Tuns16,
318                 AST.Tuns32,
319                 AST.Tint64, AST.Tuns64:
320                 return EnumKind.Numeric;
321             case AST.Tarray:
322                 if (type.isString())
323                     return EnumKind.String;
324                 break;
325             case AST.Tenum:
326                 return EnumKind.Enum;
327             default:
328                 break;
329         }
330         return EnumKind.Other;
331     }
332 
333     private AST.Type determineEnumType(AST.Type type)
334     {
335         if (auto arr = type.isTypeDArray())
336         {
337             switch (arr.next.ty)
338             {
339                 case AST.Tchar:  return AST.Type.tchar.constOf.pointerTo;
340                 case AST.Twchar: return AST.Type.twchar.constOf.pointerTo;
341                 case AST.Tdchar: return AST.Type.tdchar.constOf.pointerTo;
342                 default: break;
343             }
344         }
345         return type;
346     }
347 
348     void writeDeclEnd()
349     {
350         buf.writestringln(";");
351 
352         if (!adparent)
353             buf.writenl();
354     }
355 
356     /// Writes the corresponding access specifier if necessary
357     private void writeProtection(const AST.Visibility.Kind kind)
358     {
359         // Don't write visibility for global declarations
360         if (!adparent)
361             return;
362 
363         string token;
364 
365         switch(kind) with(AST.Visibility.Kind)
366         {
367             case none, private_:
368                 if (this.currentVisibility == AST.Visibility.Kind.private_)
369                     return;
370                 this.currentVisibility = AST.Visibility.Kind.private_;
371                 token = "private:";
372                 break;
373 
374             case package_, protected_:
375                 if (this.currentVisibility == AST.Visibility.Kind.protected_)
376                     return;
377                 this.currentVisibility = AST.Visibility.Kind.protected_;
378                 token = "protected:";
379                 break;
380 
381             case undefined, public_, export_:
382                 if (this.currentVisibility == AST.Visibility.Kind.public_)
383                     return;
384                 this.currentVisibility = AST.Visibility.Kind.public_;
385                 token = "public:";
386                 break;
387 
388             default:
389                 printf("Unexpected visibility: %d!\n", kind);
390                 assert(0);
391         }
392 
393         buf.level--;
394         buf.writestringln(token);
395         buf.level++;
396     }
397 
398     /**
399      * Writes an identifier into `buf` and checks for reserved identifiers. The
400      * parameter `canFix` determines how this function handles C++ keywords:
401      *
402      * `false` => Raise a warning and print the identifier as-is
403      * `true`  => Append an underscore to the identifier
404      *
405      * Params:
406      *   s        = the symbol denoting the identifier
407      *   canFixup = whether the identifier may be changed without affecting
408      *              binary compatibility
409      */
410     private void writeIdentifier(const AST.Dsymbol s, const bool canFix = false)
411     {
412         writeIdentifier(s.ident, s.loc, s.kind(), canFix);
413     }
414 
415     /** Overload of `writeIdentifier` used for all AST nodes not descending from Dsymbol **/
416     private void writeIdentifier(const Identifier ident, const Loc loc, const char* kind, const bool canFix = false)
417     {
418         bool needsFix;
419 
420         void warnCxxCompat(const(char)* reason)
421         {
422             if (canFix)
423             {
424                 needsFix = true;
425                 return;
426             }
427 
428             static bool warned = false;
429             warning(loc, "%s `%s` is a %s", kind, ident.toChars(), reason);
430 
431             if (!warned)
432             {
433                 warningSupplemental(loc, "The generated C++ header will contain " ~
434                                     "identifiers that are keywords in C++");
435                 warned = true;
436             }
437         }
438 
439         if (global.params.warnings != DiagnosticReporting.off || canFix)
440         {
441             // Warn about identifiers that are keywords in C++.
442             switch (ident.toString())
443             {
444                 // C++ operators
445                 case "and":
446                 case "and_eq":
447                 case "bitand":
448                 case "bitor":
449                 case "compl":
450                 case "not":
451                 case "not_eq":
452                 case "or":
453                 case "or_eq":
454                 case "xor":
455                 case "xor_eq":
456                     warnCxxCompat("special operator in C++");
457                     break;
458 
459                 // C++ keywords
460                 case "const_cast":
461                 case "delete":
462                 case "dynamic_cast":
463                 case "explicit":
464                 case "friend":
465                 case "inline":
466                 case "mutable":
467                 case "namespace":
468                 case "operator":
469                 case "register":
470                 case "reinterpret_cast":
471                 case "signed":
472                 case "static_cast":
473                 case "typedef":
474                 case "typename":
475                 case "unsigned":
476                 case "using":
477                 case "virtual":
478                 case "volatile":
479                     warnCxxCompat("keyword in C++");
480                     break;
481 
482                 // C++11 keywords
483                 case "alignas":
484                 case "alignof":
485                 case "char16_t":
486                 case "char32_t":
487                 case "constexpr":
488                 case "decltype":
489                 case "noexcept":
490                 case "nullptr":
491                 case "static_assert":
492                 case "thread_local":
493                     if (global.params.cplusplus >= CppStdRevision.cpp11)
494                         warnCxxCompat("keyword in C++11");
495                     break;
496 
497                 // C++20 keywords
498                 case "char8_t":
499                 case "consteval":
500                 case "constinit":
501                 // Concepts-related keywords
502                 case "concept":
503                 case "requires":
504                 // Coroutines-related keywords
505                 case "co_await":
506                 case "co_yield":
507                 case "co_return":
508                     if (global.params.cplusplus >= CppStdRevision.cpp20)
509                         warnCxxCompat("keyword in C++20");
510                     break;
511 
512                 default:
513                     break;
514             }
515         }
516         buf.writestring(ident.toString());
517         if (needsFix)
518             buf.writeByte('_');
519     }
520 
521     override void visit(AST.Dsymbol s)
522     {
523         debug (Debug_DtoH)
524         {
525             printf("[AST.Dsymbol enter] %s\n", s.toChars());
526             import dmd.asttypename;
527             printf("[AST.Dsymbol enter] %s\n", s.astTypeName().ptr);
528             scope(exit) printf("[AST.Dsymbol exit] %s\n", s.toChars());
529         }
530     }
531 
532     override void visit(AST.Import i)
533     {
534         debug (Debug_DtoH)
535         {
536             printf("[AST.Import enter] %s\n", i.toChars());
537             scope(exit) printf("[AST.Import exit] %s\n", i.toChars());
538         }
539     }
540 
541     override void visit(AST.AttribDeclaration pd)
542     {
543         debug (Debug_DtoH)
544         {
545             printf("[AST.AttribDeclaration enter] %s\n", pd.toChars());
546             scope(exit) printf("[AST.AttribDeclaration exit] %s\n", pd.toChars());
547         }
548         Dsymbols* decl = pd.include(null);
549         if (!decl)
550             return;
551 
552         foreach (s; *decl)
553         {
554             if (adparent || s.visible().kind >= AST.Visibility.Kind.public_)
555                 s.accept(this);
556         }
557     }
558 
559     override void visit(AST.StorageClassDeclaration scd)
560     {
561         debug (Debug_DtoH)
562         {
563             printf("[AST.StorageClassDeclaration enter] %s\n", scd.toChars());
564             scope(exit) printf("[AST.StorageClassDeclaration exit] %s\n", scd.toChars());
565         }
566         const stcStash = this.storageClass;
567         this.storageClass |= scd.stc;
568         visit(cast(AST.AttribDeclaration) scd);
569         this.storageClass = stcStash;
570     }
571 
572     override void visit(AST.LinkDeclaration ld)
573     {
574         debug (Debug_DtoH)
575         {
576             printf("[AST.LinkDeclaration enter] %s\n", ld.toChars());
577             scope(exit) printf("[AST.LinkDeclaration exit] %s\n", ld.toChars());
578         }
579         auto save = linkage;
580         linkage = ld.linkage;
581         visit(cast(AST.AttribDeclaration)ld);
582         linkage = save;
583     }
584 
585     override void visit(AST.CPPMangleDeclaration md)
586     {
587         const oldLinkage = this.linkage;
588         this.linkage = LINK.cpp;
589         visit(cast(AST.AttribDeclaration) md);
590         this.linkage = oldLinkage;
591     }
592 
593     override void visit(AST.Module m)
594     {
595         debug (Debug_DtoH)
596         {
597             printf("[AST.Module enter] %s\n", m.toChars());
598             scope(exit) printf("[AST.Module exit] %s\n", m.toChars());
599         }
600         foreach (s; *m.members)
601         {
602             if (s.visible().kind < AST.Visibility.Kind.public_)
603                 continue;
604             s.accept(this);
605         }
606     }
607 
608     override void visit(AST.FuncDeclaration fd)
609     {
610         debug (Debug_DtoH)
611         {
612             printf("[AST.FuncDeclaration enter] %s\n", fd.toChars());
613             scope(exit) printf("[AST.FuncDeclaration exit] %s\n", fd.toChars());
614         }
615         if (cast(void*)fd in visited)
616             return;
617         // printf("FuncDeclaration %s %s\n", fd.toPrettyChars(), fd.type.toChars());
618         visited[cast(void*)fd] = true;
619 
620         // Note that tf might be null for templated (member) functions
621         auto tf = cast(AST.TypeFunction)fd.type;
622         if ((tf && tf.linkage != LINK.c && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration()))
623         {
624             ignored("function %s because of linkage", fd.toPrettyChars());
625 
626             // Virtual extern(D) functions require a dummy declaration to ensure proper
627             // vtable layout - but omit redundant declarations - the slot was already
628             // reserved  in the base class
629             if (fd.isVirtual() && fd.introducing)
630             {
631                 // Hide placeholders because they are not ABI compatible
632                 writeProtection(AST.Visibility.Kind.private_);
633 
634                 __gshared int counter; // Ensure unique names in all cases
635                 buf.printf("virtual void __vtable_slot_%u();", counter++);
636                 buf.writenl();
637             }
638             return;
639         }
640         if (!adparent && !fd.fbody)
641         {
642             ignored("function %s because it is extern", fd.toPrettyChars());
643             return;
644         }
645         if (fd.visibility.kind == AST.Visibility.Kind.none || fd.visibility.kind == AST.Visibility.Kind.private_)
646         {
647             ignored("function %s because it is private", fd.toPrettyChars());
648             return;
649         }
650 
651         writeProtection(fd.visibility.kind);
652 
653         if (tf && tf.linkage == LINK.c)
654             buf.writestring("extern \"C\" ");
655         else if (!adparent)
656             buf.writestring("extern ");
657         if (adparent && fd.isStatic())
658             buf.writestring("static ");
659         else if (adparent && (
660             // Virtual functions in non-templated classes
661             (fd.vtblIndex != -1 && !fd.isOverride()) ||
662 
663             // Virtual functions in templated classes (fd.vtblIndex still -1)
664             (tdparent && adparent.isClassDeclaration() && !(this.storageClass & AST.STC.final_ || fd.isFinal))))
665                 buf.writestring("virtual ");
666 
667         if (adparent && !tdparent)
668         {
669             auto s = adparent.search(Loc.initial, fd.ident);
670             auto cd = adparent.isClassDeclaration();
671 
672             if (!(adparent.storage_class & AST.STC.abstract_) &&
673                 !(cd && cd.isAbstract()) &&
674                 s is fd && !fd.overnext)
675             {
676                 const cn = adparent.ident.toChars();
677                 const fn = fd.ident.toChars();
678                 const vi = fd.vtblIndex;
679 
680                 checkbuf.printf("assert(getSlotNumber <%s>(0, &%s::%s) == %d);",
681                                                        cn,     cn, fn,    vi);
682                 checkbuf.writenl();
683            }
684         }
685 
686         if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11)
687             writeProtection(AST.Visibility.Kind.private_);
688         funcToBuffer(tf, fd);
689         // FIXME: How to determine if fd is const without tf?
690         if (adparent && tf && (tf.isConst() || tf.isImmutable()))
691         {
692             bool fdOverridesAreConst = true;
693             foreach (fdv; fd.foverrides)
694             {
695                 auto tfv = cast(AST.TypeFunction)fdv.type;
696                 if (!tfv.isConst() && !tfv.isImmutable())
697                 {
698                     fdOverridesAreConst = false;
699                     break;
700                 }
701             }
702 
703             buf.writestring(fdOverridesAreConst ? " const" : " /* const */");
704         }
705         if (adparent && fd.isAbstract())
706             buf.writestring(" = 0");
707         if (adparent && fd.isDisabled && global.params.cplusplus >= CppStdRevision.cpp11)
708             buf.writestring(" = delete");
709         buf.writestringln(";");
710         if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11)
711             writeProtection(AST.Visibility.Kind.public_);
712 
713         if (!adparent)
714             buf.writenl();
715 
716     }
717 
718     override void visit(AST.UnitTestDeclaration utd)
719     {
720         debug (Debug_DtoH)
721         {
722             printf("[AST.UnitTestDeclaration enter] %s\n", utd.toChars());
723             scope(exit) printf("[AST.UnitTestDeclaration exit] %s\n", utd.toChars());
724         }
725     }
726 
727     override void visit(AST.VarDeclaration vd)
728     {
729         debug (Debug_DtoH)
730         {
731             printf("[AST.VarDeclaration enter] %s\n", vd.toChars());
732             scope(exit) printf("[AST.VarDeclaration exit] %s\n", vd.toChars());
733         }
734         if (cast(void*)vd in visited)
735             return;
736         visited[cast(void*)vd] = true;
737 
738         // Tuple field are expanded into multiple VarDeclarations
739         // (we'll visit them later)
740         if (vd.type && vd.type.isTypeTuple())
741             return;
742 
743         if (vd.type == AST.Type.tsize_t)
744             origType = &vd.originalType;
745         scope(exit) origType = null;
746 
747         if (vd.alignment != STRUCTALIGN_DEFAULT)
748         {
749             buf.printf("// Ignoring var %s alignment %u", vd.toChars(), vd.alignment);
750             buf.writenl();
751         }
752 
753         if (vd.storage_class & AST.STC.manifest &&
754             vd._init && vd._init.isExpInitializer() && vd.type !is null)
755         {
756             if (linkage != LINK.c && linkage != LINK.cpp)
757             {
758                 ignored("variable %s because of linkage", vd.toPrettyChars());
759                 return;
760             }
761             AST.Type type = vd.type;
762             EnumKind kind = getEnumKind(type);
763 
764             if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) {
765                 ignored("enum `%s` because it is `%s`.", vd.toPrettyChars(), AST.visibilityToChars(vd.visibility.kind));
766                 return;
767             }
768 
769             writeProtection(vd.visibility.kind);
770 
771             final switch (kind)
772             {
773                 case EnumKind.Int, EnumKind.Numeric:
774                     // 'enum : type' is only available from C++-11 onwards.
775                     if (global.params.cplusplus < CppStdRevision.cpp11)
776                         goto case;
777                     buf.writestring("enum : ");
778                     determineEnumType(type).accept(this);
779                     buf.writestring(" { ");
780                     writeIdentifier(vd, true);
781                     buf.writestring(" = ");
782                     auto ie = AST.initializerToExpression(vd._init).isIntegerExp();
783                     visitInteger(ie.toInteger(), type);
784                     buf.writestring(" };");
785                     break;
786 
787                 case EnumKind.String, EnumKind.Enum:
788                     buf.writestring("static ");
789                     auto target = determineEnumType(type);
790                     target.accept(this);
791                     buf.writestring(" const ");
792                     writeIdentifier(vd, true);
793                     buf.writestring(" = ");
794                     auto e = AST.initializerToExpression(vd._init);
795                     printExpressionFor(target, e);
796                     buf.writestring(";");
797                     break;
798 
799                 case EnumKind.Other:
800                     ignored("enum `%s` because type `%s` is currently not supported for enum constants.", vd.toPrettyChars(), type.toChars());
801                     return;
802             }
803             buf.writenl();
804             buf.writenl();
805             return;
806         }
807 
808         if (tdparent && vd.type && !vd.type.deco)
809         {
810             if (linkage != LINK.c && linkage != LINK.cpp)
811             {
812                 ignored("variable %s because of linkage", vd.toPrettyChars());
813                 return;
814             }
815             writeProtection(vd.visibility.kind);
816             typeToBuffer(vd.type, vd, adparent && !(vd.storage_class & (AST.STC.static_ | AST.STC.gshared)));
817             buf.writestringln(";");
818             return;
819         }
820 
821         if (vd.storage_class & (AST.STC.static_ | AST.STC.extern_ | AST.STC.tls | AST.STC.gshared) ||
822         vd.parent && vd.parent.isModule())
823         {
824             if (vd.linkage != LINK.c && vd.linkage != LINK.cpp)
825             {
826                 ignored("variable %s because of linkage", vd.toPrettyChars());
827                 return;
828             }
829             if (vd.storage_class & AST.STC.tls)
830             {
831                 ignored("variable %s because of thread-local storage", vd.toPrettyChars());
832                 return;
833             }
834             writeProtection(vd.visibility.kind);
835             if (vd.linkage == LINK.c)
836                 buf.writestring("extern \"C\" ");
837             else if (!adparent)
838                 buf.writestring("extern ");
839             if (adparent)
840                 buf.writestring("static ");
841             typeToBuffer(vd.type, vd);
842             writeDeclEnd();
843             return;
844         }
845 
846         if (adparent && vd.type && vd.type.deco)
847         {
848             writeProtection(vd.visibility.kind);
849             typeToBuffer(vd.type, vd, true);
850             buf.writestringln(";");
851 
852             if (auto t = vd.type.isTypeStruct())
853                 includeSymbol(t.sym);
854 
855             checkbuf.level++;
856             const pn = adparent.ident.toChars();
857             const vn = vd.ident.toChars();
858             const vo = vd.offset;
859             checkbuf.printf("assert(offsetof(%s, %s) == %d);",
860                                              pn, vn,    vo);
861             checkbuf.writenl();
862             checkbuf.level--;
863             return;
864         }
865 
866         visit(cast(AST.Dsymbol)vd);
867     }
868 
869     override void visit(AST.TypeInfoDeclaration tid)
870     {
871         debug (Debug_DtoH)
872         {
873             printf("[AST.TypeInfoDeclaration enter] %s\n", tid.toChars());
874             scope(exit) printf("[AST.TypeInfoDeclaration exit] %s\n", tid.toChars());
875         }
876     }
877 
878     override void visit(AST.AliasDeclaration ad)
879     {
880         debug (Debug_DtoH)
881         {
882             printf("[AST.AliasDeclaration enter] %s\n", ad.toChars());
883             scope(exit) printf("[AST.AliasDeclaration exit] %s\n", ad.toChars());
884         }
885         writeProtection(ad.visibility.kind);
886 
887         if (auto t = ad.type)
888         {
889             if (t.ty == AST.Tdelegate)
890             {
891                 visit(cast(AST.Dsymbol)ad);
892                 return;
893             }
894 
895             // for function pointers we need to original type
896             if (ad.type.ty == AST.Tpointer &&
897                 (cast(AST.TypePointer)t).nextOf.ty == AST.Tfunction)
898             {
899                 origType = &ad.originalType;
900             }
901             scope(exit) origType = null;
902 
903             buf.writestring("typedef ");
904             typeToBuffer(origType ? *origType : t, ad);
905             writeDeclEnd();
906             return;
907         }
908         if (!ad.aliassym)
909         {
910             assert(0);
911         }
912         if (auto ti = ad.aliassym.isTemplateInstance())
913         {
914             visitTi(ti);
915             return;
916         }
917         if (auto sd = ad.aliassym.isStructDeclaration())
918         {
919             buf.writestring("typedef ");
920             sd.type.accept(this);
921             buf.writestring(" ");
922             writeIdentifier(ad);
923             writeDeclEnd();
924             return;
925         }
926         else if (auto td = ad.aliassym.isTemplateDeclaration())
927         {
928             if (global.params.cplusplus < CppStdRevision.cpp11)
929             {
930                 ignored("%s because `using` declarations require C++ 11", ad.toPrettyChars());
931                 return;
932             }
933 
934             printTemplateParams(td);
935             buf.writestring("using ");
936             writeIdentifier(ad);
937             buf.printf(" = %s<", td.ident.toChars());
938 
939             foreach (const idx, const p; *td.parameters)
940             {
941                 if (idx)
942                     buf.writestring(", ");
943                 writeIdentifier(p.ident, p.loc, "parameter", true);
944             }
945             buf.writestringln(">;");
946             return;
947         }
948 
949         if (ad.aliassym.isDtorDeclaration())
950         {
951             // Ignore. It's taken care of while visiting FuncDeclaration
952             return;
953         }
954 
955         ignored("%s %s", ad.aliassym.kind(), ad.aliassym.toPrettyChars());
956     }
957 
958     override void visit(AST.Nspace ns)
959     {
960         handleNspace(ns, ns.members);
961     }
962 
963     override void visit(AST.CPPNamespaceDeclaration ns)
964     {
965         handleNspace(ns, ns.decl);
966     }
967 
968     void handleNspace(AST.Dsymbol namespace, Dsymbols* members)
969     {
970         buf.writestring("namespace ");
971         writeIdentifier(namespace);
972         buf.writenl();
973         buf.writestring("{");
974         buf.writenl();
975         buf.level++;
976         foreach(decl;(*members))
977         {
978             decl.accept(this);
979         }
980         buf.level--;
981         buf.writestring("}");
982         buf.writenl();
983     }
984 
985     override void visit(AST.AnonDeclaration ad)
986     {
987         debug (Debug_DtoH)
988         {
989             printf("[AST.AnonDeclaration enter] %s\n", ad.toChars());
990             scope(exit) printf("[AST.AnonDeclaration exit] %s\n", ad.toChars());
991         }
992 
993         buf.writestringln(ad.isunion ? "union" : "struct");
994         buf.writestringln("{");
995         buf.level++;
996         foreach (s; *ad.decl)
997         {
998             s.accept(this);
999         }
1000         buf.level--;
1001         buf.writestringln("};");
1002     }
1003 
1004     private bool memberField(AST.VarDeclaration vd)
1005     {
1006         if (!vd.type || !vd.type.deco || !vd.ident)
1007             return false;
1008         if (!vd.isField())
1009             return false;
1010         if (vd.type.ty == AST.Tfunction)
1011             return false;
1012         if (vd.type.ty == AST.Tsarray)
1013             return false;
1014         return true;
1015     }
1016 
1017     override void visit(AST.StructDeclaration sd)
1018     {
1019         debug (Debug_DtoH)
1020         {
1021             printf("[AST.StructDeclaration enter] %s\n", sd.toChars());
1022             scope(exit) printf("[AST.StructDeclaration exit] %s\n", sd.toChars());
1023         }
1024 
1025         if (isSkippableTemplateInstance(sd))
1026             return;
1027 
1028         if (cast(void*)sd in visited)
1029             return;
1030 
1031         visited[cast(void*)sd] = true;
1032         if (linkage != LINK.c && linkage != LINK.cpp)
1033         {
1034             ignored("non-cpp struct %s because of linkage", sd.toChars());
1035             return;
1036         }
1037 
1038         const ignoredStash = this.ignoredCounter;
1039         scope (exit) this.ignoredCounter = ignoredStash;
1040 
1041         pushAlignToBuffer(sd.alignment);
1042 
1043         writeProtection(sd.visibility.kind);
1044 
1045         const structAsClass = sd.cppmangle == CPPMANGLE.asClass;
1046         if (sd.isUnionDeclaration())
1047             buf.writestring("union ");
1048         else
1049             buf.writestring(structAsClass ? "class " : "struct ");
1050 
1051         writeIdentifier(sd);
1052         if (!sd.members)
1053         {
1054             buf.writestringln(";");
1055             buf.writenl();
1056             return;
1057         }
1058 
1059         buf.writenl();
1060         buf.writestring("{");
1061 
1062         const protStash = this.currentVisibility;
1063         this.currentVisibility = structAsClass ? AST.Visibility.Kind.private_ : AST.Visibility.Kind.public_;
1064         scope (exit) this.currentVisibility = protStash;
1065 
1066         buf.level++;
1067         buf.writenl();
1068         auto save = adparent;
1069         adparent = sd;
1070 
1071         foreach (m; *sd.members)
1072         {
1073             m.accept(this);
1074         }
1075         // Generate default ctor
1076         if (!sd.noDefaultCtor && !sd.isUnionDeclaration())
1077         {
1078             writeProtection(AST.Visibility.Kind.public_);
1079             buf.printf("%s()", sd.ident.toChars());
1080             size_t varCount;
1081             bool first = true;
1082             buf.level++;
1083             foreach (m; *sd.members)
1084             {
1085                 if (auto vd = m.isVarDeclaration())
1086                 {
1087                     if (!memberField(vd))
1088                         continue;
1089                     varCount++;
1090 
1091                     if (!vd._init && !vd.type.isTypeBasic() && !vd.type.isTypePointer && !vd.type.isTypeStruct &&
1092                         !vd.type.isTypeClass && !vd.type.isTypeDArray && !vd.type.isTypeSArray)
1093                     {
1094                         continue;
1095                     }
1096                     if (vd._init && vd._init.isVoidInitializer())
1097                         continue;
1098 
1099                     if (first)
1100                     {
1101                         buf.writestringln(" :");
1102                         first = false;
1103                     }
1104                     else
1105                     {
1106                         buf.writestringln(",");
1107                     }
1108                     writeIdentifier(vd, true);
1109                     buf.writeByte('(');
1110 
1111                     if (vd._init)
1112                     {
1113                         auto e = AST.initializerToExpression(vd._init);
1114                         printExpressionFor(vd.type, e, true);
1115                     }
1116                     buf.printf(")");
1117                 }
1118             }
1119             buf.level--;
1120             buf.writenl();
1121             buf.writestringln("{");
1122             buf.writestringln("}");
1123             auto ctor = sd.ctor ? sd.ctor.isFuncDeclaration() : null;
1124             if (varCount && (!ctor || ctor.storage_class & AST.STC.disable))
1125             {
1126                 buf.printf("%s(", sd.ident.toChars());
1127                 first = true;
1128                 foreach (m; *sd.members)
1129                 {
1130                     if (auto vd = m.isVarDeclaration())
1131                     {
1132                         if (!memberField(vd))
1133                             continue;
1134                         if (!first)
1135                             buf.writestring(", ");
1136                         assert(vd.type);
1137                         assert(vd.ident);
1138                         typeToBuffer(vd.type, vd, true);
1139                         // Don't print default value for first parameter to not clash
1140                         // with the default ctor defined above
1141                         if (!first)
1142                         {
1143                             buf.writestring(" = ");
1144                             if (vd._init && !vd._init.isVoidInitializer())
1145                                 printExpressionFor(vd.type, AST.initializerToExpression(vd._init));
1146                             else
1147                                 printExpressionFor(vd.type, vd.type.defaultInitLiteral(Loc.initial));
1148                         }
1149                         first = false;
1150                     }
1151                 }
1152                 buf.writestring(") :");
1153                 buf.level++;
1154                 buf.writenl();
1155 
1156                 first = true;
1157                 foreach (m; *sd.members)
1158                 {
1159                     if (auto vd = m.isVarDeclaration())
1160                     {
1161                         if (!memberField(vd))
1162                             continue;
1163 
1164                         if (first)
1165                             first = false;
1166                         else
1167                             buf.writestringln(",");
1168 
1169                         writeIdentifier(vd, true);
1170                         buf.writeByte('(');
1171                         writeIdentifier(vd, true);
1172                         buf.writeByte(')');
1173                     }
1174                 }
1175                 buf.writenl();
1176                 buf.writestringln("{}");
1177                 buf.level--;
1178             }
1179         }
1180 
1181         buf.level--;
1182         adparent = save;
1183         buf.writestringln("};");
1184 
1185         popAlignToBuffer(sd.alignment);
1186         buf.writenl();
1187 
1188         // Workaround because size triggers a forward-reference error
1189         // for struct templates (the size is undetermined even if the
1190         // size doesn't depend on the parameters)
1191         if (!tdparent)
1192         {
1193             checkbuf.level++;
1194             const sn = sd.ident.toChars();
1195             const sz = sd.size(Loc.initial);
1196             checkbuf.printf("assert(sizeof(%s) == %llu);", sn, sz);
1197             checkbuf.writenl();
1198             checkbuf.level--;
1199         }
1200     }
1201 
1202     private void pushAlignToBuffer(uint alignment)
1203     {
1204         // DMD ensures alignment is a power of two
1205         //assert(alignment > 0 && ((alignment & (alignment - 1)) == 0),
1206         //       "Invalid alignment size");
1207 
1208         // When no alignment is specified, `uint.max` is the default
1209         // FIXME: alignment is 0 for structs templated members
1210         if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
1211         {
1212             return;
1213         }
1214 
1215         buf.printf("#pragma pack(push, %d)", alignment);
1216         buf.writenl();
1217     }
1218 
1219     private void popAlignToBuffer(uint alignment)
1220     {
1221         if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
1222             return;
1223 
1224         buf.writestringln("#pragma pack(pop)");
1225     }
1226 
1227     private void includeSymbol(AST.Dsymbol ds)
1228     {
1229         debug (Debug_DtoH)
1230         {
1231             printf("[includeSymbol(AST.Dsymbol) enter] %s\n", ds.toChars());
1232             scope(exit) printf("[includeSymbol(AST.Dsymbol) exit] %s\n", ds.toChars());
1233         }
1234         if (cast(void*) ds in visited)
1235             return;
1236 
1237         OutBuffer decl;
1238         decl.doindent = true;
1239         decl.spaces = true;
1240         visitAsRoot(ds, &decl);
1241         donebuf.writestring(decl.peekChars());
1242     }
1243 
1244     override void visit(AST.ClassDeclaration cd)
1245     {
1246         debug (Debug_DtoH)
1247         {
1248             printf("[AST.ClassDeclaration enter] %s\n", cd.toChars());
1249             scope(exit) printf("[AST.ClassDeclaration exit] %s\n", cd.toChars());
1250         }
1251 
1252         if (isSkippableTemplateInstance(cd))
1253             return;
1254 
1255         if (cast(void*)cd in visited)
1256             return;
1257         visited[cast(void*)cd] = true;
1258         if (!cd.isCPPclass() && !(tdparent && linkage == LINK.cpp)) // FIXME: ClassKind not set for templated classes?
1259         {
1260             ignored("non-cpp class %s", cd.toChars());
1261             return;
1262         }
1263 
1264         writeProtection(cd.visibility.kind);
1265 
1266         const classAsStruct = cd.cppmangle == CPPMANGLE.asStruct;
1267         buf.writestring(classAsStruct ? "struct " : "class ");
1268         writeIdentifier(cd);
1269 
1270         if (cd.storage_class & AST.STC.final_ || (tdparent && this.storageClass & AST.STC.final_))
1271             buf.writestring(" final");
1272 
1273         assert(cd.baseclasses);
1274 
1275         foreach (i, base; *cd.baseclasses)
1276         {
1277             buf.writestring(i == 0 ? " : public " : ", public ");
1278 
1279             // Base classes/interfaces might depend on template parameters,
1280             // e.g. class A(T) : B!T { ... }
1281             if (base.sym is null)
1282             {
1283                 base.type.accept(this);
1284             }
1285             // base.type references onemember for template instances
1286             // and hence skips the TypeInstance...
1287             else if (auto ti = base.sym.isInstantiated())
1288             {
1289                 visitTi(ti);
1290             }
1291             else
1292             {
1293                 buf.writestring(base.sym.toChars());
1294                 includeSymbol(base.sym);
1295             }
1296         }
1297 
1298         if (!cd.members)
1299         {
1300             buf.writestring(";");
1301             buf.writenl();
1302             buf.writenl();
1303             return;
1304         }
1305 
1306         buf.writenl();
1307         buf.writestringln("{");
1308 
1309         const protStash = this.currentVisibility;
1310         this.currentVisibility = classAsStruct ? AST.Visibility.Kind.public_ : AST.Visibility.Kind.private_;
1311         scope (exit) this.currentVisibility = protStash;
1312 
1313         auto save = adparent;
1314         adparent = cd;
1315         buf.level++;
1316         foreach (m; *cd.members)
1317         {
1318             m.accept(this);
1319         }
1320         buf.level--;
1321         adparent = save;
1322 
1323         buf.writestringln("};");
1324         buf.writenl();
1325     }
1326 
1327     override void visit(AST.EnumDeclaration ed)
1328     {
1329         debug (Debug_DtoH)
1330         {
1331             printf("[AST.EnumDeclaration enter] %s\n", ed.toChars());
1332             scope(exit) printf("[AST.EnumDeclaration exit] %s\n", ed.toChars());
1333         }
1334         if (cast(void*)ed in visited)
1335             return;
1336 
1337         visited[cast(void*)ed] = true;
1338 
1339         //if (linkage != LINK.c && linkage != LINK.cpp)
1340         //{
1341             //ignored("non-cpp enum %s because of linkage\n", ed.toChars());
1342             //return;
1343         //}
1344 
1345         if (ed.isSpecial())
1346         {
1347             //ignored("%s because it is a special C++ type", ed.toPrettyChars());
1348             return;
1349         }
1350 
1351         // we need to know a bunch of stuff about the enum...
1352         bool isAnonymous = ed.ident is null;
1353         const isOpaque = !ed.members;
1354         AST.Type type = ed.memtype;
1355         if (!type && !isOpaque)
1356         {
1357             // check all keys have matching type
1358             foreach (_m; *ed.members)
1359             {
1360                 auto m = _m.isEnumMember();
1361                 if (!type)
1362                     type = m.type;
1363                 else if (m.type !is type)
1364                 {
1365                     type = null;
1366                     break;
1367                 }
1368             }
1369         }
1370         EnumKind kind = getEnumKind(type);
1371 
1372         if (isOpaque)
1373         {
1374             // Opaque enums were introduced in C++ 11 (workaround?)
1375             if (global.params.cplusplus < CppStdRevision.cpp11)
1376             {
1377                 ignored("%s because opaque enums require C++ 11", ed.toPrettyChars());
1378                 return;
1379             }
1380             // Opaque enum defaults to int but the type might not be set
1381             else if (!type)
1382             {
1383                 kind = EnumKind.Int;
1384             }
1385             // Cannot apply namespace workaround for non-integral types
1386             else if (kind != EnumKind.Int && kind != EnumKind.Numeric)
1387             {
1388                 ignored("enum %s because of its base type", ed.toPrettyChars());
1389                 return;
1390             }
1391         }
1392 
1393         // determine if this is an enum, or just a group of manifest constants
1394         bool manifestConstants = !isOpaque && (!type || (isAnonymous && kind == EnumKind.Other));
1395         assert(!manifestConstants || isAnonymous);
1396 
1397         writeProtection(ed.visibility.kind);
1398 
1399         // write the enum header
1400         if (!manifestConstants)
1401         {
1402             if (kind == EnumKind.Int || kind == EnumKind.Numeric)
1403             {
1404                 buf.writestring("enum");
1405                 // D enums are strong enums, but there exists only a direct mapping
1406                 // with 'enum class' from C++-11 onwards.
1407                 if (global.params.cplusplus >= CppStdRevision.cpp11)
1408                 {
1409                     if (!isAnonymous)
1410                     {
1411                         buf.writestring(" class ");
1412                         writeIdentifier(ed);
1413                     }
1414                     if (kind == EnumKind.Numeric)
1415                     {
1416                         buf.writestring(" : ");
1417                         determineEnumType(type).accept(this);
1418                     }
1419                 }
1420                 else if (!isAnonymous)
1421                 {
1422                     buf.writeByte(' ');
1423                     writeIdentifier(ed);
1424                 }
1425             }
1426             else
1427             {
1428                 buf.writestring("namespace");
1429                 if(!isAnonymous)
1430                 {
1431                     buf.writeByte(' ');
1432                     writeIdentifier(ed);
1433                 }
1434             }
1435             // Opaque enums have no members, hence skip the body
1436             if (isOpaque)
1437             {
1438                 buf.writestringln(";");
1439                 return;
1440             }
1441             else
1442             {
1443                 buf.writenl();
1444                 buf.writestringln("{");
1445             }
1446         }
1447 
1448         // emit constant for each member
1449         if (!manifestConstants)
1450             buf.level++;
1451 
1452         foreach (_m; *ed.members)
1453         {
1454             auto m = _m.isEnumMember();
1455             AST.Type memberType = type ? type : m.type;
1456             const EnumKind memberKind = type ? kind : getEnumKind(memberType);
1457 
1458             if (!manifestConstants && (kind == EnumKind.Int || kind == EnumKind.Numeric))
1459             {
1460                 // C++-98 compatible enums must use the typename as a prefix to avoid
1461                 // collisions with other identifiers in scope.  For consistency with D,
1462                 // the enum member `Type.member` is emitted as `Type_member` in C++-98.
1463                 if (!isAnonymous && global.params.cplusplus < CppStdRevision.cpp11)
1464                 {
1465                     writeIdentifier(ed);
1466                     buf.writeByte('_');
1467                 }
1468                 writeIdentifier(m, true);
1469                 buf.writestring(" = ");
1470 
1471                 auto ie = cast(AST.IntegerExp)m.value;
1472                 visitInteger(ie.toInteger(), memberType);
1473                 buf.writestring(",");
1474             }
1475             else if (global.params.cplusplus >= CppStdRevision.cpp11 &&
1476                      manifestConstants && (memberKind == EnumKind.Int || memberKind == EnumKind.Numeric))
1477             {
1478                 buf.writestring("enum : ");
1479                 determineEnumType(memberType).accept(this);
1480                 buf.writestring(" { ");
1481                 writeIdentifier(m, true);
1482                 buf.writestring(" = ");
1483 
1484                 auto ie = cast(AST.IntegerExp)m.value;
1485                 visitInteger(ie.toInteger(), memberType);
1486                 buf.writestring(" };");
1487             }
1488             else
1489             {
1490                 buf.writestring("static ");
1491                 auto target = determineEnumType(memberType);
1492                 target.accept(this);
1493                 buf.writestring(" const ");
1494                 writeIdentifier(m, true);
1495                 buf.writestring(" = ");
1496                 printExpressionFor(target, m.origValue);
1497                 buf.writestring(";");
1498             }
1499             buf.writenl();
1500         }
1501 
1502         if (!manifestConstants)
1503             buf.level--;
1504         // write the enum tail
1505         if (!manifestConstants)
1506             buf.writestring("};");
1507         buf.writenl();
1508         buf.writenl();
1509     }
1510 
1511     override void visit(AST.EnumMember em)
1512     {
1513         assert(false, "This node type should be handled in the EnumDeclaration");
1514     }
1515 
1516     /**
1517      * Prints a member/parameter/variable declaration into `buf`.
1518      *
1519      * Params:
1520      *   t        = the type (used if `this.origType` is null)
1521      *   s        = the symbol denoting the identifier
1522      *   canFixup = whether the identifier may be changed without affecting
1523      *              binary compatibility (forwarded to `writeIdentifier`)
1524      */
1525     private void typeToBuffer(AST.Type t, AST.Dsymbol s, const bool canFixup = false)
1526     {
1527         debug (Debug_DtoH)
1528         {
1529             printf("[typeToBuffer(AST.Type, AST.Dsymbol) enter] %s sym %s\n", t.toChars(), s.toChars());
1530             scope(exit) printf("[typeToBuffer(AST.Type, AST.Dsymbol) exit] %s sym %s\n", t.toChars(), s.toChars());
1531         }
1532 
1533         this.ident = s.ident;
1534         origType ? origType.accept(this) : t.accept(this);
1535         if (this.ident)
1536         {
1537             buf.writeByte(' ');
1538             writeIdentifier(s, canFixup);
1539         }
1540         this.ident = null;
1541         if (auto tsa = t.isTypeSArray())
1542         {
1543             buf.writeByte('[');
1544             tsa.dim.accept(this);
1545             buf.writeByte(']');
1546         }
1547     }
1548 
1549     override void visit(AST.Type t)
1550     {
1551         debug (Debug_DtoH)
1552         {
1553             printf("[AST.Type enter] %s\n", t.toChars());
1554             scope(exit) printf("[AST.Type exit] %s\n", t.toChars());
1555         }
1556         printf("Invalid type: %s\n", t.toPrettyChars());
1557         assert(0);
1558     }
1559 
1560     override void visit(AST.TypeIdentifier t)
1561     {
1562         debug (Debug_DtoH)
1563         {
1564             printf("[AST.TypeIdentifier enter] %s\n", t.toChars());
1565             scope(exit) printf("[AST.TypeIdentifier exit] %s\n", t.toChars());
1566         }
1567         if (t.idents.length)
1568             buf.writestring("typename ");
1569 
1570         writeIdentifier(t.ident, t.loc, "type", tdparent !is null);
1571 
1572         foreach (arg; t.idents)
1573         {
1574             buf.writestring("::");
1575 
1576             import dmd.root.rootobject;
1577             // Is this even possible?
1578             if (arg.dyncast != DYNCAST.identifier)
1579             {
1580                 printf("arg.dyncast() = %d\n", arg.dyncast());
1581                 assert(false);
1582             }
1583             buf.writestring((cast(Identifier) arg).toChars());
1584         }
1585     }
1586 
1587     override void visit(AST.TypeNull t)
1588     {
1589         debug (Debug_DtoH)
1590         {
1591             printf("[AST.TypeNull enter] %s\n", t.toChars());
1592             scope(exit) printf("[AST.TypeNull exit] %s\n", t.toChars());
1593         }
1594         if (global.params.cplusplus >= CppStdRevision.cpp11)
1595             buf.writestring("nullptr_t");
1596         else
1597             buf.writestring("void*");
1598 
1599     }
1600 
1601     override void visit(AST.TypeTypeof t)
1602     {
1603         debug (Debug_DtoH)
1604         {
1605             printf("[AST.TypeInstance enter] %s\n", t.toChars());
1606             scope(exit) printf("[AST.TypeInstance exit] %s\n", t.toChars());
1607         }
1608         assert(t.exp);
1609 
1610         if (t.exp.type)
1611         {
1612             t.exp.type.accept(this);
1613         }
1614         else if (t.exp.isThisExp())
1615         {
1616             // Short circuit typeof(this) => <Aggregate name>
1617             assert(adparent);
1618             buf.writestring(adparent.ident.toChars());
1619         }
1620         else
1621         {
1622             // Relying on C++'s typeof might produce wrong results
1623             // but it's the best we've got here.
1624             buf.writestring("typeof(");
1625             t.exp.accept(this);
1626             buf.writeByte(')');
1627         }
1628     }
1629 
1630     override void visit(AST.TypeBasic t)
1631     {
1632         debug (Debug_DtoH)
1633         {
1634             printf("[AST.TypeBasic enter] %s\n", t.toChars());
1635             scope(exit) printf("[AST.TypeBasic exit] %s\n", t.toChars());
1636         }
1637         if (t.isConst() || t.isImmutable())
1638             buf.writestring("const ");
1639         string typeName;
1640         switch (t.ty)
1641         {
1642             case AST.Tvoid:     typeName = "void";      break;
1643             case AST.Tbool:     typeName = "bool";      break;
1644             case AST.Tchar:     typeName = "char";      break;
1645             case AST.Twchar:    typeName = "char16_t";  break;
1646             case AST.Tdchar:    typeName = "char32_t";  break;
1647             case AST.Tint8:     typeName = "int8_t";    break;
1648             case AST.Tuns8:     typeName = "uint8_t";   break;
1649             case AST.Tint16:    typeName = "int16_t";   break;
1650             case AST.Tuns16:    typeName = "uint16_t";  break;
1651             case AST.Tint32:    typeName = "int32_t";   break;
1652             case AST.Tuns32:    typeName = "uint32_t";  break;
1653             case AST.Tint64:    typeName = "int64_t";   break;
1654             case AST.Tuns64:    typeName = "uint64_t";  break;
1655             case AST.Tfloat32:  typeName = "float";     break;
1656             case AST.Tfloat64:  typeName = "double";    break;
1657             case AST.Tfloat80:
1658                 typeName = "_d_real";
1659                 hasReal = true;
1660                 break;
1661             default:
1662                 //t.print();
1663                 assert(0);
1664         }
1665         buf.writestring(typeName);
1666     }
1667 
1668     override void visit(AST.TypePointer t)
1669     {
1670         debug (Debug_DtoH)
1671         {
1672             printf("[AST.TypePointer enter] %s\n", t.toChars());
1673             scope(exit) printf("[AST.TypePointer exit] %s\n", t.toChars());
1674         }
1675         auto ts = t.next.isTypeStruct();
1676         if (ts && !strcmp(ts.sym.ident.toChars(), "__va_list_tag"))
1677         {
1678             buf.writestring("va_list");
1679             return;
1680         }
1681         t.next.accept(this);
1682         if (t.next.ty != AST.Tfunction)
1683             buf.writeByte('*');
1684         if (t.isConst() || t.isImmutable())
1685             buf.writestring(" const");
1686     }
1687 
1688     override void visit(AST.TypeSArray t)
1689     {
1690         debug (Debug_DtoH)
1691         {
1692             printf("[AST.TypeSArray enter] %s\n", t.toChars());
1693             scope(exit) printf("[AST.TypeSArray exit] %s\n", t.toChars());
1694         }
1695         t.next.accept(this);
1696     }
1697 
1698     override void visit(AST.TypeAArray t)
1699     {
1700         debug (Debug_DtoH)
1701         {
1702             printf("[AST.TypeAArray enter] %s\n", t.toChars());
1703             scope(exit) printf("[AST.TypeAArray exit] %s\n", t.toChars());
1704         }
1705         AST.Type.tvoidptr.accept(this);
1706     }
1707 
1708     override void visit(AST.TypeFunction tf)
1709     {
1710         debug (Debug_DtoH)
1711         {
1712             printf("[AST.TypeFunction enter] %s\n", tf.toChars());
1713             scope(exit) printf("[AST.TypeFunction exit] %s\n", tf.toChars());
1714         }
1715         tf.next.accept(this);
1716         buf.writeByte('(');
1717         buf.writeByte('*');
1718         if (ident)
1719             buf.writestring(ident.toChars());
1720         ident = null;
1721         buf.writeByte(')');
1722         buf.writeByte('(');
1723         foreach (i, fparam; tf.parameterList)
1724         {
1725             if (i)
1726                 buf.writestring(", ");
1727             fparam.accept(this);
1728         }
1729         if (tf.parameterList.varargs)
1730         {
1731             if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1)
1732                 buf.writestring(", ");
1733             buf.writestring("...");
1734         }
1735         buf.writeByte(')');
1736     }
1737 
1738     private void enumToBuffer(AST.EnumDeclaration ed)
1739     {
1740         debug (Debug_DtoH)
1741         {
1742             printf("[enumToBuffer(AST.EnumDeclaration) enter] %s\n", ed.toChars());
1743             scope(exit) printf("[enumToBuffer(AST.EnumDeclaration) exit] %s\n", ed.toChars());
1744         }
1745         if (ed.isSpecial())
1746         {
1747             if (ed.ident == DMDType.c_long)
1748                 buf.writestring("long");
1749             else if (ed.ident == DMDType.c_ulong)
1750                 buf.writestring("unsigned long");
1751             else if (ed.ident == DMDType.c_longlong)
1752                 buf.writestring("long long");
1753             else if (ed.ident == DMDType.c_ulonglong)
1754                 buf.writestring("unsigned long long");
1755             else if (ed.ident == DMDType.c_long_double)
1756                 buf.writestring("long double");
1757             else if (ed.ident == DMDType.c_wchar_t)
1758                 buf.writestring("wchar_t");
1759             else if (ed.ident == DMDType.c_complex_float)
1760                 buf.writestring("_Complex float");
1761             else if (ed.ident == DMDType.c_complex_double)
1762                 buf.writestring("_Complex double");
1763             else if (ed.ident == DMDType.c_complex_real)
1764                 buf.writestring("_Complex long double");
1765             else
1766             {
1767                 //ed.print();
1768                 assert(0);
1769             }
1770             return;
1771         }
1772 
1773         const kind = getEnumKind(ed.memtype);
1774 
1775         // Check if the enum was emitted as a real enum
1776         if (kind == EnumKind.Int || kind == EnumKind.Numeric)
1777             buf.writestring(ed.toChars());
1778         else
1779         {
1780             // Use the base type if the enum was emitted as a namespace
1781             buf.printf("/* %s */ ", ed.ident.toChars());
1782             ed.memtype.accept(this);
1783         }
1784     }
1785 
1786     override void visit(AST.TypeEnum t)
1787     {
1788         debug (Debug_DtoH)
1789         {
1790             printf("[AST.TypeEnum enter] %s\n", t.toChars());
1791             scope(exit) printf("[AST.TypeEnum exit] %s\n", t.toChars());
1792         }
1793         if (cast(void*)t.sym !in forwarded)
1794         {
1795             forwarded[cast(void*)t.sym] = true;
1796             //printf("Visiting enum %s from module %s %s\n", t.sym.toPrettyChars(), t.toChars(), t.sym.loc.toChars());
1797             visitAsRoot(t.sym, fwdbuf);
1798         }
1799         if (t.isConst() || t.isImmutable())
1800             buf.writestring("const ");
1801         enumToBuffer(t.sym);
1802     }
1803 
1804     override void visit(AST.TypeStruct t)
1805     {
1806         debug (Debug_DtoH)
1807         {
1808             printf("[AST.TypeStruct enter] %s\n", t.toChars());
1809             scope(exit) printf("[AST.TypeStruct exit] %s\n", t.toChars());
1810         }
1811         if (cast(void*)t.sym !in forwarded && !t.sym.parent.isTemplateInstance())
1812         {
1813             forwarded[cast(void*)t.sym] = true;
1814             fwdbuf.writestring(t.sym.isUnionDeclaration() ? "union " : "struct ");
1815             fwdbuf.writestring(t.sym.toChars());
1816             fwdbuf.writestringln(";");
1817         }
1818 
1819         if (t.isConst() || t.isImmutable())
1820             buf.writestring("const ");
1821         if (auto ti = t.sym.parent.isTemplateInstance())
1822         {
1823             visitTi(ti);
1824             return;
1825         }
1826         buf.writestring(t.sym.toChars());
1827     }
1828 
1829     override void visit(AST.TypeDArray t)
1830     {
1831         debug (Debug_DtoH)
1832         {
1833             printf("[AST.TypeDArray enter] %s\n", t.toChars());
1834             scope(exit) printf("[AST.TypeDArray exit] %s\n", t.toChars());
1835         }
1836         if (t.isConst() || t.isImmutable())
1837             buf.writestring("const ");
1838         buf.writestring("_d_dynamicArray< ");
1839         t.next.accept(this);
1840         buf.writestring(" >");
1841     }
1842 
1843     override void visit(AST.TypeInstance t)
1844     {
1845         debug (Debug_DtoH)
1846         {
1847             printf("[AST.TypeInstance enter] %s\n", t.toChars());
1848             scope(exit) printf("[AST.TypeInstance exit] %s\n", t.toChars());
1849         }
1850         visitTi(t.tempinst);
1851     }
1852 
1853     private void visitTi(AST.TemplateInstance ti)
1854     {
1855         debug (Debug_DtoH)
1856         {
1857             printf("[visitTi(AST.TemplateInstance) enter] %s\n", ti.toChars());
1858             scope(exit) printf("[visitTi(AST.TemplateInstance) exit] %s\n", ti.toChars());
1859         }
1860 
1861 
1862         foreach (o; *ti.tiargs)
1863         {
1864             if (!AST.isType(o))
1865                 return;
1866         }
1867         buf.writestring(ti.name.toChars());
1868         buf.writeByte('<');
1869         foreach (i, o; *ti.tiargs)
1870         {
1871             if (i)
1872                 buf.writestring(", ");
1873             if (auto tt = AST.isType(o))
1874             {
1875                 tt.accept(this);
1876             }
1877             else
1878             {
1879                 //ti.print();
1880                 //o.print();
1881                 assert(0);
1882             }
1883         }
1884         buf.writestring(" >");
1885     }
1886 
1887     override void visit(AST.TemplateDeclaration td)
1888     {
1889         debug (Debug_DtoH)
1890         {
1891             printf("[AST.TemplateDeclaration enter] %s\n", td.toChars());
1892             scope(exit) printf("[AST.TemplateDeclaration exit] %s\n", td.toChars());
1893         }
1894         if (cast(void*)td in visited)
1895             return;
1896         visited[cast(void*)td] = true;
1897 
1898         if (!td.parameters || !td.onemember || (!td.onemember.isStructDeclaration && !td.onemember.isClassDeclaration && !td.onemember.isFuncDeclaration))
1899         {
1900             visit(cast(AST.Dsymbol)td);
1901             return;
1902         }
1903 
1904         // Explicitly disallow templates with non-type parameters or specialization.
1905         foreach (p; *td.parameters)
1906         {
1907             if (!p.isTemplateTypeParameter() || p.specialization())
1908             {
1909                 visit(cast(AST.Dsymbol)td);
1910                 return;
1911             }
1912         }
1913 
1914         if (linkage != LINK.c && linkage != LINK.cpp)
1915         {
1916             ignored("template %s because of linkage", td.toPrettyChars());
1917             return;
1918         }
1919 
1920         auto save = tdparent;
1921         tdparent = td;
1922         const bookmark = buf.length;
1923         printTemplateParams(td);
1924 
1925         const oldIgnored = this.ignoredCounter;
1926         td.onemember.accept(this);
1927 
1928         // Remove "template<...>" if the symbol could not be emitted
1929         if (oldIgnored != this.ignoredCounter)
1930             buf.setsize(bookmark);
1931 
1932         tdparent = save;
1933     }
1934 
1935     private void printTemplateParams(const AST.TemplateDeclaration td)
1936     {
1937         buf.writestring("template <");
1938         bool first = true;
1939         foreach (p; *td.parameters)
1940         {
1941             if (first)
1942                 first = false;
1943             else
1944                 buf.writestring(", ");
1945             buf.writestring("typename ");
1946             writeIdentifier(p.ident, p.loc, "template parameter", true);
1947         }
1948         buf.writestringln(">");
1949     }
1950 
1951     override void visit(AST.TypeClass t)
1952     {
1953         debug (Debug_DtoH)
1954         {
1955             printf("[AST.TypeClass enter] %s\n", t.toChars());
1956             scope(exit) printf("[AST.TypeClass exit] %s\n", t.toChars());
1957         }
1958         if (cast(void*)t.sym !in forwarded)
1959         {
1960             forwarded[cast(void*)t.sym] = true;
1961             fwdbuf.writestring("class ");
1962             fwdbuf.writestring(t.sym.toChars());
1963             fwdbuf.writestringln(";");
1964         }
1965 
1966         if (t.isConst() || t.isImmutable())
1967             buf.writestring("const ");
1968         buf.writestring(t.sym.toChars());
1969         buf.writeByte('*');
1970         if (t.isConst() || t.isImmutable())
1971             buf.writestring(" const");
1972     }
1973 
1974     /**
1975      * Writes the function signature to `buf`.
1976      *
1977      * Params:
1978      *   fd     = the function to print
1979      *   tf     = fd's type
1980      */
1981     private void funcToBuffer(AST.TypeFunction tf, AST.FuncDeclaration fd)
1982     {
1983         debug (Debug_DtoH)
1984         {
1985             printf("[funcToBuffer(AST.TypeFunction) enter] %s\n", fd.toChars());
1986             scope(exit) printf("[funcToBuffer(AST.TypeFunction) exit] %s\n", fd.toChars());
1987         }
1988 
1989         auto originalType = cast(AST.TypeFunction)fd.originalType;
1990 
1991         if (fd.isCtorDeclaration() || fd.isDtorDeclaration())
1992         {
1993             if (fd.isDtorDeclaration())
1994             {
1995                 buf.writeByte('~');
1996             }
1997             buf.writestring(adparent.toChars());
1998             if (!tf)
1999             {
2000                 assert(fd.isDtorDeclaration());
2001                 buf.writestring("()");
2002                 return;
2003             }
2004         }
2005         else
2006         {
2007             import dmd.root.string : toDString;
2008             assert(tf.next, fd.loc.toChars().toDString());
2009 
2010             tf.next == AST.Type.tsize_t ? originalType.next.accept(this) : tf.next.accept(this);
2011             if (tf.isref)
2012                 buf.writeByte('&');
2013             buf.writeByte(' ');
2014             writeIdentifier(fd);
2015         }
2016 
2017         buf.writeByte('(');
2018         foreach (i, fparam; tf.parameterList)
2019         {
2020             if (i)
2021                 buf.writestring(", ");
2022             if (fparam.type == AST.Type.tsize_t && originalType)
2023             {
2024                 fparam = originalType.parameterList[i];
2025             }
2026             fparam.accept(this);
2027         }
2028         if (tf.parameterList.varargs)
2029         {
2030             if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1)
2031                 buf.writestring(", ");
2032             buf.writestring("...");
2033         }
2034         buf.writeByte(')');
2035     }
2036 
2037     override void visit(AST.Parameter p)
2038     {
2039         debug (Debug_DtoH)
2040         {
2041             printf("[AST.Parameter enter] %s\n", p.toChars());
2042             scope(exit) printf("[AST.Parameter exit] %s\n", p.toChars());
2043         }
2044         ident = p.ident;
2045         p.type.accept(this);
2046         if (p.storageClass & AST.STC.ref_)
2047             buf.writeByte('&');
2048         buf.writeByte(' ');
2049         if (ident)
2050             // FIXME: Parameter is missing a Loc
2051             writeIdentifier(ident, Loc.initial, "parameter", true);
2052         ident = null;
2053 
2054         if (p.defaultArg)
2055         {
2056             //printf("%s %d\n", p.defaultArg.toChars, p.defaultArg.op);
2057             buf.writestring(" = ");
2058             printExpressionFor(p.type, p.defaultArg);
2059         }
2060     }
2061 
2062     /**
2063      * Prints `exp` as an expression of type `target` while inserting
2064      * appropriate code when implicit conversion does not translate
2065      * directly to C++, e.g. from an enum to its base type.
2066      *
2067      * Params:
2068      *   target = the type `exp` is converted to
2069      *   exp    = the expression to print
2070      *   isCtor = if `exp` is a ctor argument
2071      */
2072     private void printExpressionFor(AST.Type target, AST.Expression exp, const bool isCtor = false)
2073     {
2074         /// Determines if a static_cast is required
2075         static bool needsCast(AST.Type target, AST.Expression exp)
2076         {
2077             // import std.stdio;
2078             // writefln("%s:%s: target = %s, type = %s (%s)", exp.loc.linnum, exp.loc.charnum, target, exp.type, exp.op);
2079 
2080             auto source = exp.type;
2081 
2082             // DotVarExp resolve conversions, e.g from an enum to its base type
2083             if (auto dve = exp.isDotVarExp())
2084                 source = dve.var.type;
2085 
2086             if (!source)
2087                 // Defensively assume that the cast is required
2088                 return true;
2089 
2090             // Conversions from enum class to base type require static_cast
2091             if (global.params.cplusplus >= CppStdRevision.cpp11 &&
2092                 source.isTypeEnum && !target.isTypeEnum)
2093                 return true;
2094 
2095             return false;
2096         }
2097 
2098         // Slices are emitted as a special struct, hence we need to fix up
2099         // any expression initialising a slice variable/member
2100         if (auto ta = target.isTypeDArray())
2101         {
2102             if (exp.isNullExp())
2103             {
2104                 if (isCtor)
2105                 {
2106                     // Don't emit, use default ctor
2107                 }
2108                 else if (global.params.cplusplus >= CppStdRevision.cpp11)
2109                 {
2110                     // Prefer initializer list
2111                     buf.writestring("{}");
2112                 }
2113                 else
2114                 {
2115                     // Write __d_dynamic_array<TYPE>()
2116                     visit(ta);
2117                     buf.writestring("()");
2118                 }
2119                 return;
2120             }
2121 
2122             if (auto se = exp.isStringExp())
2123             {
2124                 // Rewrite as <length> + <literal> pair optionally
2125                 // wrapped in a initializer list/ctor call
2126 
2127                 const initList = global.params.cplusplus >= CppStdRevision.cpp11;
2128                 if (!isCtor)
2129                 {
2130                     if (initList)
2131                         buf.writestring("{ ");
2132                     else
2133                     {
2134                         visit(ta);
2135                         buf.writestring("( ");
2136                     }
2137                 }
2138 
2139                 buf.printf("%zu, ", se.len);
2140                 visit(se);
2141 
2142                 if (!isCtor)
2143                     buf.writestring(initList ? " }" : " )");
2144 
2145                 return;
2146             }
2147         }
2148         else if (needsCast(target, exp))
2149         {
2150             buf.writestring("static_cast<");
2151             target.accept(this);
2152             buf.writestring(">(");
2153             exp.accept(this);
2154             buf.writeByte(')');
2155         }
2156         else
2157         {
2158             exp.accept(this);
2159         }
2160     }
2161 
2162     override void visit(AST.Expression e)
2163     {
2164         debug (Debug_DtoH)
2165         {
2166             printf("[AST.Expression enter] %s\n", e.toChars());
2167             scope(exit) printf("[AST.Expression exit] %s\n", e.toChars());
2168         }
2169         // Valid in most cases, others should be overriden below
2170         // to use the appropriate operators  (:: and ->)
2171         buf.writestring(e.toString());
2172     }
2173 
2174     override void visit(AST.VarExp e)
2175     {
2176         debug (Debug_DtoH)
2177         {
2178             printf("[AST.VarExp enter] %s\n", e.toChars());
2179             scope(exit) printf("[AST.VarExp exit] %s\n", e.toChars());
2180         }
2181 
2182         /// Partially prints the FQN including parent aggregates
2183         void printPrefix(AST.Dsymbol var)
2184         {
2185             if (!var || !var.isAggregateDeclaration())
2186                 return;
2187             printPrefix(var.parent);
2188             includeSymbol(var);
2189 
2190             buf.writestring(var.toString());
2191             buf.writestring("::");
2192         }
2193 
2194         // Static members are not represented as DotVarExp, hence
2195         // manually print the full name here
2196         if (e.var.storage_class & AST.STC.static_)
2197             printPrefix(e.var.parent);
2198 
2199         includeSymbol(e.var);
2200         writeIdentifier(e.var, !!e.var.isThis());
2201     }
2202 
2203     override void visit(AST.CallExp e)
2204     {
2205         debug (Debug_DtoH)
2206         {
2207             printf("[AST.CallExp enter] %s\n", e.toChars());
2208             scope(exit) printf("[AST.CallExp exit] %s\n", e.toChars());
2209         }
2210 
2211         // Dereferencing function pointers requires additional braces: (*f)(args)
2212         const isFp = e.e1.isPtrExp();
2213         if (isFp)
2214             buf.writeByte('(');
2215         else if (e.f)
2216             includeSymbol(e.f);
2217 
2218         e.e1.accept(this);
2219 
2220         if (isFp) buf.writeByte(')');
2221 
2222         assert(e.arguments);
2223         buf.writeByte('(');
2224         foreach (i, arg; *e.arguments)
2225         {
2226             if (i)
2227                 buf.writestring(", ");
2228             arg.accept(this);
2229         }
2230         buf.writeByte(')');
2231     }
2232 
2233     override void visit(AST.DotVarExp e)
2234     {
2235         debug (Debug_DtoH)
2236         {
2237             printf("[AST.DotVarExp enter] %s\n", e.toChars());
2238             scope(exit) printf("[AST.DotVarExp exit] %s\n", e.toChars());
2239         }
2240 
2241         // Accessing members through a pointer?
2242         if (auto pe = e.e1.isPtrExp)
2243         {
2244             pe.e1.accept(this);
2245             buf.writestring("->");
2246         }
2247         else
2248         {
2249             e.e1.accept(this);
2250             buf.writeByte('.');
2251         }
2252 
2253         // Should only be used to access non-static members
2254         assert(e.var.isThis());
2255 
2256         writeIdentifier(e.var, true);
2257     }
2258 
2259     override void visit(AST.DotIdExp e)
2260     {
2261         debug (Debug_DtoH)
2262         {
2263             printf("[AST.DotIdExp enter] %s\n", e.toChars());
2264             scope(exit) printf("[AST.DotIdExp exit] %s\n", e.toChars());
2265         }
2266 
2267         e.e1.accept(this);
2268         buf.writestring("::");
2269         buf.writestring(e.ident.toChars());
2270     }
2271 
2272     override void visit(AST.NullExp e)
2273     {
2274         debug (Debug_DtoH)
2275         {
2276             printf("[AST.NullExp enter] %s\n", e.toChars());
2277             scope(exit) printf("[AST.NullExp exit] %s\n", e.toChars());
2278         }
2279         if (global.params.cplusplus >= CppStdRevision.cpp11)
2280             buf.writestring("nullptr");
2281         else
2282             buf.writestring("NULL");
2283     }
2284 
2285     override void visit(AST.ArrayLiteralExp e)
2286     {
2287         debug (Debug_DtoH)
2288         {
2289             printf("[AST.ArrayLiteralExp enter] %s\n", e.toChars());
2290             scope(exit) printf("[AST.ArrayLiteralExp exit] %s\n", e.toChars());
2291         }
2292         buf.writestring("arrayliteral");
2293     }
2294 
2295     override void visit(AST.StringExp e)
2296     {
2297         debug (Debug_DtoH)
2298         {
2299             printf("[AST.StringExp enter] %s\n", e.toChars());
2300             scope(exit) printf("[AST.StringExp exit] %s\n", e.toChars());
2301         }
2302 
2303         if (e.sz == 2)
2304             buf.writeByte('u');
2305         else if (e.sz == 4)
2306             buf.writeByte('U');
2307         buf.writeByte('"');
2308 
2309         for (size_t i = 0; i < e.len; i++)
2310         {
2311             uint c = e.charAt(i);
2312             switch (c)
2313             {
2314                 case '"':
2315                 case '\\':
2316                     buf.writeByte('\\');
2317                     goto default;
2318                 default:
2319                     if (c <= 0xFF)
2320                     {
2321                         if (c >= 0x20 && c < 0x80)
2322                             buf.writeByte(c);
2323                         else
2324                             buf.printf("\\x%02x", c);
2325                     }
2326                     else if (c <= 0xFFFF)
2327                         buf.printf("\\u%04x", c);
2328                     else
2329                         buf.printf("\\U%08x", c);
2330                     break;
2331             }
2332         }
2333         buf.writeByte('"');
2334     }
2335 
2336     override void visit(AST.RealExp e)
2337     {
2338         debug (Debug_DtoH)
2339         {
2340             printf("[AST.RealExp enter] %s\n", e.toChars());
2341             scope(exit) printf("[AST.RealExp exit] %s\n", e.toChars());
2342         }
2343 
2344         import dmd.root.ctfloat : CTFloat;
2345 
2346         // Special case NaN and Infinity because floatToBuffer
2347         // uses D literals (`nan` and `infinity`)
2348         if (CTFloat.isNaN(e.value))
2349         {
2350             buf.writestring("NAN");
2351         }
2352         else if (CTFloat.isInfinity(e.value))
2353         {
2354             if (e.value < 0)
2355                 buf.writeByte('-');
2356             buf.writestring("INFINITY");
2357         }
2358         else
2359         {
2360             import dmd.hdrgen;
2361             // Hex floating point literals were introduced in C++ 17
2362             const allowHex = global.params.cplusplus >= CppStdRevision.cpp17;
2363             floatToBuffer(e.type, e.value, buf, allowHex);
2364         }
2365     }
2366 
2367     override void visit(AST.IntegerExp e)
2368     {
2369         debug (Debug_DtoH)
2370         {
2371             printf("[AST.IntegerExp enter] %s\n", e.toChars());
2372             scope(exit) printf("[AST.IntegerExp exit] %s\n", e.toChars());
2373         }
2374         visitInteger(e.toInteger, e.type);
2375     }
2376 
2377     private void visitInteger(dinteger_t v, AST.Type t)
2378     {
2379         debug (Debug_DtoH)
2380         {
2381             printf("[visitInteger(AST.Type) enter] %s\n", t.toChars());
2382             scope(exit) printf("[visitInteger(AST.Type) exit] %s\n", t.toChars());
2383         }
2384         switch (t.ty)
2385         {
2386             case AST.Tenum:
2387                 auto te = cast(AST.TypeEnum)t;
2388                 buf.writestring("(");
2389                 enumToBuffer(te.sym);
2390                 buf.writestring(")");
2391                 visitInteger(v, te.sym.memtype);
2392                 break;
2393             case AST.Tbool:
2394                 buf.writestring(v ? "true" : "false");
2395                 break;
2396             case AST.Tint8:
2397                 buf.printf("%d", cast(byte)v);
2398                 break;
2399             case AST.Tuns8:
2400                 buf.printf("%uu", cast(ubyte)v);
2401                 break;
2402             case AST.Tint16:
2403                 buf.printf("%d", cast(short)v);
2404                 break;
2405             case AST.Tuns16:
2406             case AST.Twchar:
2407                 buf.printf("%uu", cast(ushort)v);
2408                 break;
2409             case AST.Tint32:
2410             case AST.Tdchar:
2411                 buf.printf("%d", cast(int)v);
2412                 break;
2413             case AST.Tuns32:
2414                 buf.printf("%uu", cast(uint)v);
2415                 break;
2416             case AST.Tint64:
2417                 buf.printf("%lldLL", v);
2418                 break;
2419             case AST.Tuns64:
2420                 buf.printf("%lluLLU", v);
2421                 break;
2422             case AST.Tchar:
2423                 if (v > 0x20 && v < 0x80)
2424                     buf.printf("'%c'", cast(int)v);
2425                 else
2426                     buf.printf("%uu", cast(ubyte)v);
2427                 break;
2428             default:
2429                 //t.print();
2430                 assert(0);
2431         }
2432     }
2433 
2434     override void visit(AST.StructLiteralExp sle)
2435     {
2436         debug (Debug_DtoH)
2437         {
2438             printf("[AST.StructLiteralExp enter] %s\n", sle.toChars());
2439             scope(exit) printf("[AST.StructLiteralExp exit] %s\n", sle.toChars());
2440         }
2441         sle.sd.type.accept(this);
2442         buf.writeByte('(');
2443         foreach(i, e; *sle.elements)
2444         {
2445             if (i)
2446                 buf.writestring(", ");
2447             printExpressionFor(sle.sd.fields[i].type, e);
2448         }
2449         buf.writeByte(')');
2450     }
2451 
2452     static if (__VERSION__ < 2092)
2453     {
2454         private void ignored(const char* format, ...) nothrow
2455         {
2456             this.ignoredCounter++;
2457 
2458             import core.stdc.stdarg;
2459             if (!printIgnored)
2460                 return;
2461 
2462             va_list ap;
2463             va_start(ap, format);
2464             buf.writestring("// Ignored ");
2465             buf.vprintf(format, ap);
2466             buf.writenl();
2467             va_end(ap);
2468         }
2469     }
2470     else
2471     {
2472         // Writes a formatted message into `buf` if `printIgnored` is true.
2473         pragma(printf)
2474         private void ignored(const char* format, ...) nothrow
2475         {
2476             this.ignoredCounter++;
2477 
2478             import core.stdc.stdarg;
2479             if (!printIgnored)
2480                 return;
2481 
2482             va_list ap;
2483             va_start(ap, format);
2484             buf.writestring("// Ignored ");
2485             buf.vprintf(format, ap);
2486             buf.writenl();
2487             va_end(ap);
2488         }
2489     }
2490 
2491     /** Checks whether s is a template instance that should not be emitted **/
2492     private bool isSkippableTemplateInstance(AST.Dsymbol s)
2493     {
2494         auto td = s.isInstantiated();
2495         return td && td !is tdparent;
2496     }
2497 }