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