1 /** 2 * Do mangling for C++ linkage. 3 * 4 * This is the POSIX side of the implementation. 5 * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`. 6 * 7 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 8 * Authors: Walter Bright, http://www.digitalmars.com 9 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 10 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d) 11 * Documentation: https://dlang.org/phobos/dmd_cppmangle.html 12 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d 13 * 14 * References: 15 * Follows Itanium C++ ABI 1.86 section 5.1 16 * http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling 17 * which is where the grammar comments come from. 18 * 19 * Bugs: 20 * https://issues.dlang.org/query.cgi 21 * enter `C++, mangling` as the keywords. 22 */ 23 24 module dmd.cppmangle; 25 26 import core.stdc.string; 27 import core.stdc.stdio; 28 29 import dmd.arraytypes; 30 import dmd.attrib; 31 import dmd.declaration; 32 import dmd.dsymbol; 33 import dmd.dtemplate; 34 import dmd.errors; 35 import dmd.expression; 36 import dmd.func; 37 import dmd.globals; 38 import dmd.id; 39 import dmd.identifier; 40 import dmd.mtype; 41 import dmd.nspace; 42 import dmd.root.array; 43 import dmd.root.outbuffer; 44 import dmd.root.rootobject; 45 import dmd.root.string; 46 import dmd.target; 47 import dmd.tokens; 48 import dmd.typesem; 49 import dmd.visitor; 50 51 52 // helper to check if an identifier is a C++ operator 53 enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown } 54 package CppOperator isCppOperator(Identifier id) 55 { 56 __gshared const(Identifier)[] operators = null; 57 if (!operators) 58 operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign]; 59 foreach (i, op; operators) 60 { 61 if (op == id) 62 return cast(CppOperator)i; 63 } 64 return CppOperator.Unknown; 65 } 66 67 /// 68 extern(C++) const(char)* toCppMangleItanium(Dsymbol s) 69 { 70 //printf("toCppMangleItanium(%s)\n", s.toChars()); 71 OutBuffer buf; 72 scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); 73 v.mangleOf(s); 74 return buf.extractChars(); 75 } 76 77 /// 78 extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s) 79 { 80 //printf("cppTypeInfoMangle(%s)\n", s.toChars()); 81 OutBuffer buf; 82 buf.writestring("_ZTI"); // "TI" means typeinfo structure 83 scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); 84 v.cpp_mangle_name(s, false); 85 return buf.extractChars(); 86 } 87 88 /****************************** 89 * Determine if sym is the 'primary' destructor, that is, 90 * the most-aggregate destructor (the one that is defined as __xdtor) 91 * Params: 92 * sym = Dsymbol 93 * Returns: 94 * true if sym is the primary destructor for an aggregate 95 */ 96 bool isPrimaryDtor(const Dsymbol sym) 97 { 98 const dtor = sym.isDtorDeclaration(); 99 if (!dtor) 100 return false; 101 const ad = dtor.isMember(); 102 assert(ad); 103 return dtor == ad.primaryDtor; 104 } 105 106 /// Context used when processing pre-semantic AST 107 private struct Context 108 { 109 /// Template instance of the function being mangled 110 TemplateInstance ti; 111 /// Function declaration we're mangling 112 FuncDeclaration fd; 113 /// Current type / expression being processed (semantically analyzed) 114 RootObject res; 115 116 @disable ref Context opAssign(ref Context other); 117 @disable ref Context opAssign(Context other); 118 119 /** 120 * Helper function to track `res` 121 * 122 * Params: 123 * next = Value to set `this.res` to. 124 * If `this.res` is `null`, the expression is not evalutated. 125 * This allow this code to be used even when no context is needed. 126 * 127 * Returns: 128 * The previous state of this `Context` object 129 */ 130 private Context push(lazy RootObject next) 131 { 132 auto r = this.res; 133 if (r !is null) 134 this.res = next; 135 return Context(this.ti, this.fd, r); 136 } 137 138 /** 139 * Reset the context to a previous one, making any adjustment necessary 140 */ 141 private void pop(ref Context prev) 142 { 143 this.res = prev.res; 144 } 145 } 146 147 private final class CppMangleVisitor : Visitor 148 { 149 /// Context used when processing pre-semantic AST 150 private Context context; 151 152 ABITagContainer abiTags; /// Container for already-written ABI tags 153 Objects components; /// array of components available for substitution 154 OutBuffer* buf; /// append the mangling to buf[] 155 Loc loc; /// location for use in error messages 156 157 /** 158 * Constructor 159 * 160 * Params: 161 * buf = `OutBuffer` to write the mangling to 162 * loc = `Loc` of the symbol being mangled 163 */ 164 this(OutBuffer* buf, Loc loc) 165 { 166 this.buf = buf; 167 this.loc = loc; 168 } 169 170 /***** 171 * Entry point. Append mangling to buf[] 172 * Params: 173 * s = symbol to mangle 174 */ 175 void mangleOf(Dsymbol s) 176 { 177 if (VarDeclaration vd = s.isVarDeclaration()) 178 { 179 mangle_variable(vd, vd.cppnamespace !is null); 180 } 181 else if (FuncDeclaration fd = s.isFuncDeclaration()) 182 { 183 mangle_function(fd); 184 } 185 else 186 { 187 assert(0); 188 } 189 } 190 191 /** 192 * Mangle the return type of a function 193 * 194 * This is called on a templated function type. 195 * Context is set to the `FuncDeclaration`. 196 * 197 * Params: 198 * preSemantic = the `FuncDeclaration`'s `originalType` 199 */ 200 void mangleReturnType(TypeFunction preSemantic) 201 { 202 auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type; 203 Type rt = preSemantic.nextOf(); 204 if (tf.isref) 205 rt = rt.referenceTo(); 206 auto prev = this.context.push(tf.nextOf()); 207 scope (exit) this.context.pop(prev); 208 this.headOfType(rt); 209 } 210 211 /** 212 * Write a seq-id from an index number, excluding the terminating '_' 213 * 214 * Params: 215 * idx = the index in a substitution list. 216 * Note that index 0 has no value, and `S0_` would be the 217 * substitution at index 1 in the list. 218 * 219 * See-Also: 220 * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id 221 */ 222 private void writeSequenceFromIndex(size_t idx) 223 { 224 if (idx) 225 { 226 void write_seq_id(size_t i) 227 { 228 if (i >= 36) 229 { 230 write_seq_id(i / 36); 231 i %= 36; 232 } 233 i += (i < 10) ? '0' : 'A' - 10; 234 buf.writeByte(cast(char)i); 235 } 236 237 write_seq_id(idx - 1); 238 } 239 } 240 241 /** 242 * Attempt to perform substitution on `p` 243 * 244 * If `p` already appeared in the mangling, it is stored as 245 * a 'part', and short references in the form of `SX_` can be used. 246 * Note that `p` can be anything: template declaration, struct declaration, 247 * class declaration, namespace... 248 * 249 * Params: 250 * p = The object to attempt to substitute 251 * nested = Whether or not `p` is to be considered nested. 252 * When `true`, `N` will be prepended before the substitution. 253 * 254 * Returns: 255 * Whether `p` already appeared in the mangling, 256 * and substitution has been written to `this.buf`. 257 */ 258 bool substitute(RootObject p, bool nested = false) 259 { 260 //printf("substitute %s\n", p ? p.toChars() : null); 261 auto i = find(p); 262 if (i >= 0) 263 { 264 //printf("\tmatch\n"); 265 /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ... 266 */ 267 if (nested) 268 buf.writeByte('N'); 269 buf.writeByte('S'); 270 writeSequenceFromIndex(i); 271 buf.writeByte('_'); 272 return true; 273 } 274 return false; 275 } 276 277 /****** 278 * See if `p` exists in components[] 279 * 280 * Note that components can contain `null` entries, 281 * as the index used in mangling is based on the index in the array. 282 * 283 * If called with an object whose dynamic type is `Nspace`, 284 * calls the `find(Nspace)` overload. 285 * 286 * Returns: 287 * index if found, -1 if not 288 */ 289 int find(RootObject p) 290 { 291 //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null); 292 scope v = new ComponentVisitor(p); 293 foreach (i, component; components) 294 { 295 if (component) 296 component.visitObject(v); 297 if (v.result) 298 return cast(int)i; 299 } 300 return -1; 301 } 302 303 /********************* 304 * Append p to components[] 305 */ 306 void append(RootObject p) 307 { 308 //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null"); 309 components.push(p); 310 } 311 312 /** 313 * Write an identifier preceded by its length 314 * 315 * Params: 316 * ident = `Identifier` to write to `this.buf` 317 */ 318 void writeIdentifier(const ref Identifier ident) 319 { 320 const name = ident.toString(); 321 this.buf.print(name.length); 322 this.buf.writestring(name); 323 } 324 325 /** 326 * Insert the leftover ABI tags to the buffer 327 * 328 * This inset ABI tags that hasn't already been written 329 * after the mangled name of the function. 330 * For more details, see the `abiTags` variable. 331 * 332 * Params: 333 * off = Offset to insert at 334 * fd = Type of the function to mangle the return type of 335 */ 336 void writeRemainingTags(size_t off, TypeFunction tf) 337 { 338 scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written); 339 tf.next.accept(remainingVisitor); 340 OutBuffer b2; 341 foreach (se; remainingVisitor.toWrite) 342 { 343 auto tag = se.peekString(); 344 // We can only insert a slice, and each insert is a memmove, 345 // so use a temporary buffer to keep it efficient. 346 b2.reset(); 347 b2.writestring("B"); 348 b2.print(tag.length); 349 b2.writestring(tag); 350 this.buf.insert(off, b2[]); 351 off += b2.length; 352 } 353 } 354 355 /************************ 356 * Determine if symbol is indeed the global ::std namespace. 357 * Params: 358 * s = symbol to check 359 * Returns: 360 * true if it is ::std 361 */ 362 static bool isStd(Dsymbol s) 363 { 364 if (!s) 365 return false; 366 367 if (auto cnd = s.isCPPNamespaceDeclaration()) 368 return isStd(cnd); 369 370 return (s.ident == Id.std && // the right name 371 s.isNspace() && // g++ disallows global "std" for other than a namespace 372 !getQualifier(s)); // at global level 373 } 374 375 /// Ditto 376 static bool isStd(CPPNamespaceDeclaration s) 377 { 378 return s && s.cppnamespace is null && s.ident == Id.std; 379 } 380 381 /************************ 382 * Determine if type is a C++ fundamental type. 383 * Params: 384 * t = type to check 385 * Returns: 386 * true if it is a fundamental type 387 */ 388 static bool isFundamentalType(Type t) 389 { 390 // First check the target whether some specific ABI is being followed. 391 bool isFundamental = void; 392 if (target.cpp.fundamentalType(t, isFundamental)) 393 return isFundamental; 394 395 if (auto te = t.isTypeEnum()) 396 { 397 // Peel off enum type from special types. 398 if (te.sym.isSpecial()) 399 t = te.memType(); 400 } 401 402 // Fundamental arithmetic types: 403 // 1. integral types: bool, char, int, ... 404 // 2. floating point types: float, double, real 405 // 3. void 406 // 4. null pointer: std::nullptr_t (since C++11) 407 if (t.ty == Tvoid || t.ty == Tbool) 408 return true; 409 else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) 410 return true; 411 else 412 return t.isTypeBasic() && (t.isintegral() || t.isreal()); 413 } 414 415 /****************************** 416 * Write the mangled representation of a template argument. 417 * Params: 418 * ti = the template instance 419 * arg = the template argument index 420 */ 421 void template_arg(TemplateInstance ti, size_t arg) 422 { 423 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); 424 assert(td); 425 TemplateParameter tp = (*td.parameters)[arg]; 426 RootObject o = (*ti.tiargs)[arg]; 427 428 auto prev = this.context.push({ 429 TemplateInstance parentti; 430 if (this.context.res.dyncast() == DYNCAST.dsymbol) 431 parentti = this.context.res.asFuncDecl().parent.isTemplateInstance(); 432 else 433 parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance(); 434 return (*parentti.tiargs)[arg]; 435 }()); 436 scope (exit) this.context.pop(prev); 437 438 if (tp.isTemplateTypeParameter()) 439 { 440 Type t = isType(o); 441 assert(t); 442 t.accept(this); 443 } 444 else if (TemplateValueParameter tv = tp.isTemplateValueParameter()) 445 { 446 // <expr-primary> ::= L <type> <value number> E # integer literal 447 if (tv.valType.isintegral()) 448 { 449 Expression e = isExpression(o); 450 assert(e); 451 buf.writeByte('L'); 452 tv.valType.accept(this); 453 auto val = e.toUInteger(); 454 if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0) 455 { 456 val = -val; 457 buf.writeByte('n'); 458 } 459 buf.print(val); 460 buf.writeByte('E'); 461 } 462 else 463 { 464 ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars()); 465 fatal(); 466 } 467 } 468 else if (tp.isTemplateAliasParameter()) 469 { 470 // Passing a function as alias parameter is the same as passing 471 // `&function` 472 Dsymbol d = isDsymbol(o); 473 Expression e = isExpression(o); 474 if (d && d.isFuncDeclaration()) 475 { 476 // X .. E => template parameter is an expression 477 // 'ad' => unary operator ('&') 478 // L .. E => is a <expr-primary> 479 buf.writestring("XadL"); 480 mangle_function(d.isFuncDeclaration()); 481 buf.writestring("EE"); 482 } 483 else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration()) 484 { 485 VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); 486 buf.writeByte('L'); 487 mangle_variable(vd, true); 488 buf.writeByte('E'); 489 } 490 else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) 491 { 492 if (!substitute(d)) 493 { 494 cpp_mangle_name(d, false); 495 } 496 } 497 else 498 { 499 ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars()); 500 fatal(); 501 } 502 } 503 else if (tp.isTemplateThisParameter()) 504 { 505 ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars()); 506 fatal(); 507 } 508 else 509 { 510 assert(0); 511 } 512 } 513 514 /****************************** 515 * Write the mangled representation of the template arguments. 516 * Params: 517 * ti = the template instance 518 * firstArg = index of the first template argument to mangle 519 * (used for operator overloading) 520 * Returns: 521 * true if any arguments were written 522 */ 523 bool template_args(TemplateInstance ti, int firstArg = 0) 524 { 525 /* <template-args> ::= I <template-arg>+ E 526 */ 527 if (!ti || ti.tiargs.dim <= firstArg) // could happen if std::basic_string is not a template 528 return false; 529 buf.writeByte('I'); 530 foreach (i; firstArg .. ti.tiargs.dim) 531 { 532 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); 533 assert(td); 534 TemplateParameter tp = (*td.parameters)[i]; 535 536 /* 537 * <template-arg> ::= <type> # type or template 538 * ::= X <expression> E # expression 539 * ::= <expr-primary> # simple expressions 540 * ::= J <template-arg>* E # argument pack 541 * 542 * Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.template-arg 543 */ 544 if (TemplateTupleParameter tt = tp.isTemplateTupleParameter()) 545 { 546 buf.writeByte('J'); // argument pack 547 548 // mangle the rest of the arguments as types 549 foreach (j; i .. (*ti.tiargs).dim) 550 { 551 Type t = isType((*ti.tiargs)[j]); 552 assert(t); 553 t.accept(this); 554 } 555 556 buf.writeByte('E'); 557 break; 558 } 559 560 template_arg(ti, i); 561 } 562 buf.writeByte('E'); 563 return true; 564 } 565 566 /** 567 * Write the symbol `p` if not null, then execute the delegate 568 * 569 * Params: 570 * p = Symbol to write 571 * dg = Delegate to execute 572 */ 573 void writeChained(Dsymbol p, scope void delegate() dg) 574 { 575 if (p && !p.isModule()) 576 { 577 buf.writestring("N"); 578 source_name(p, true); 579 dg(); 580 buf.writestring("E"); 581 } 582 else 583 dg(); 584 } 585 586 /** 587 * Write the name of `s` to the buffer 588 * 589 * Params: 590 * s = Symbol to write the name of 591 * haveNE = Whether `N..E` is already part of the mangling 592 * Because `Nspace` and `CPPNamespaceAttribute` can be 593 * mixed, this is a mandatory hack. 594 */ 595 void source_name(Dsymbol s, bool haveNE = false) 596 { 597 version (none) 598 { 599 printf("source_name(%s)\n", s.toChars()); 600 auto sl = this.buf.peekSlice(); 601 assert(sl.length == 0 || haveNE || s.cppnamespace is null || sl != "_ZN"); 602 } 603 if (TemplateInstance ti = s.isTemplateInstance()) 604 { 605 bool needsTa = false; 606 607 // https://issues.dlang.org/show_bug.cgi?id=20413 608 // N..E is not needed when substituting members of the std namespace. 609 // This is observed in the GCC and Clang implementations. 610 // The Itanium specification is not clear enough on this specific case. 611 // References: 612 // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name 613 // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression 614 Dsymbol q = getQualifier(ti.tempdecl); 615 Dsymbol ns = ti.tempdecl.cppnamespace; 616 const inStd = ns && isStd(ns) || q && isStd(q); 617 const isNested = !inStd && (ns || q); 618 619 if (substitute(ti.tempdecl, !haveNE && isNested)) 620 { 621 template_args(ti); 622 if (!haveNE && isNested) 623 buf.writeByte('E'); 624 } 625 else if (this.writeStdSubstitution(ti, needsTa)) 626 { 627 this.abiTags.writeSymbol(ti, this); 628 if (needsTa) 629 template_args(ti); 630 } 631 else 632 { 633 this.writeNamespace( 634 s.cppnamespace, () { 635 this.writeIdentifier(ti.tempdecl.toAlias().ident); 636 append(ti.tempdecl); 637 this.abiTags.writeSymbol(ti.tempdecl, this); 638 template_args(ti); 639 }, haveNE); 640 } 641 } 642 else 643 this.writeNamespace(s.cppnamespace, () { 644 this.writeIdentifier(s.ident); 645 this.abiTags.writeSymbol(s, this); 646 }, 647 haveNE); 648 } 649 650 /******** 651 * See if s is actually an instance of a template 652 * Params: 653 * s = symbol 654 * Returns: 655 * if s is instance of a template, return the instance, otherwise return s 656 */ 657 static Dsymbol getInstance(Dsymbol s) 658 { 659 Dsymbol p = s.toParent(); 660 if (p) 661 { 662 if (TemplateInstance ti = p.isTemplateInstance()) 663 return ti; 664 } 665 return s; 666 } 667 668 /// Get the namespace of a template instance 669 CPPNamespaceDeclaration getTiNamespace(TemplateInstance ti) 670 { 671 // If we receive a pre-semantic `TemplateInstance`, 672 // `cppnamespace` is always `null` 673 return ti.tempdecl ? ti.cppnamespace 674 : this.context.res.asType().toDsymbol(null).cppnamespace; 675 } 676 677 /******** 678 * Get qualifier for `s`, meaning the symbol 679 * that s is in the symbol table of. 680 * The module does not count as a qualifier, because C++ 681 * does not have modules. 682 * Params: 683 * s = symbol that may have a qualifier 684 * s is rewritten to be TemplateInstance if s is one 685 * Returns: 686 * qualifier, null if none 687 */ 688 static Dsymbol getQualifier(Dsymbol s) 689 { 690 Dsymbol p = s.toParent(); 691 return (p && !p.isModule()) ? p : null; 692 } 693 694 // Detect type char 695 static bool isChar(RootObject o) 696 { 697 Type t = isType(o); 698 return (t && t.equals(Type.tchar)); 699 } 700 701 // Detect type ::std::char_traits<char> 702 bool isChar_traits_char(RootObject o) 703 { 704 return isIdent_char(Id.char_traits, o); 705 } 706 707 // Detect type ::std::allocator<char> 708 bool isAllocator_char(RootObject o) 709 { 710 return isIdent_char(Id.allocator, o); 711 } 712 713 // Detect type ::std::ident<char> 714 bool isIdent_char(Identifier ident, RootObject o) 715 { 716 Type t = isType(o); 717 if (!t || t.ty != Tstruct) 718 return false; 719 Dsymbol s = (cast(TypeStruct)t).toDsymbol(null); 720 if (s.ident != ident) 721 return false; 722 Dsymbol p = s.toParent(); 723 if (!p) 724 return false; 725 TemplateInstance ti = p.isTemplateInstance(); 726 if (!ti) 727 return false; 728 Dsymbol q = getQualifier(ti); 729 const bool inStd = isStd(q) || isStd(this.getTiNamespace(ti)); 730 return inStd && ti.tiargs.dim == 1 && isChar((*ti.tiargs)[0]); 731 } 732 733 /*** 734 * Detect template args <char, ::std::char_traits<char>> 735 * and write st if found. 736 * Returns: 737 * true if found 738 */ 739 bool char_std_char_traits_char(TemplateInstance ti, string st) 740 { 741 if (ti.tiargs.dim == 2 && 742 isChar((*ti.tiargs)[0]) && 743 isChar_traits_char((*ti.tiargs)[1])) 744 { 745 buf.writestring(st.ptr); 746 return true; 747 } 748 return false; 749 } 750 751 752 void prefix_name(Dsymbol s) 753 { 754 //printf("prefix_name(%s)\n", s.toChars()); 755 if (substitute(s)) 756 return; 757 if (isStd(s)) 758 return buf.writestring("St"); 759 760 auto si = getInstance(s); 761 Dsymbol p = getQualifier(si); 762 if (p) 763 { 764 if (isStd(p)) 765 { 766 bool needsTa; 767 auto ti = si.isTemplateInstance(); 768 if (this.writeStdSubstitution(ti, needsTa)) 769 { 770 this.abiTags.writeSymbol(ti, this); 771 if (needsTa) 772 { 773 template_args(ti); 774 append(ti); 775 } 776 return; 777 } 778 buf.writestring("St"); 779 } 780 else 781 prefix_name(p); 782 } 783 source_name(si, true); 784 if (!isStd(si)) 785 /* Do this after the source_name() call to keep components[] 786 * in the right order. 787 * https://issues.dlang.org/show_bug.cgi?id=17947 788 */ 789 append(si); 790 } 791 792 /** 793 * Write common substitution for standard types, such as std::allocator 794 * 795 * This function assumes that the symbol `ti` is in the namespace `std`. 796 * 797 * Params: 798 * ti = Template instance to consider 799 * needsTa = If this function returns `true`, this value indicates 800 * if additional template argument mangling is needed 801 * 802 * Returns: 803 * `true` if a special std symbol was found 804 */ 805 bool writeStdSubstitution(TemplateInstance ti, out bool needsTa) 806 { 807 if (!ti) 808 return false; 809 if (!isStd(this.getTiNamespace(ti)) && !isStd(getQualifier(ti))) 810 return false; 811 812 if (ti.name == Id.allocator) 813 { 814 buf.writestring("Sa"); 815 needsTa = true; 816 return true; 817 } 818 if (ti.name == Id.basic_string) 819 { 820 // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>> 821 if (ti.tiargs.dim == 3 && 822 isChar((*ti.tiargs)[0]) && 823 isChar_traits_char((*ti.tiargs)[1]) && 824 isAllocator_char((*ti.tiargs)[2])) 825 826 { 827 buf.writestring("Ss"); 828 return true; 829 } 830 buf.writestring("Sb"); // ::std::basic_string 831 needsTa = true; 832 return true; 833 } 834 835 // ::std::basic_istream<char, ::std::char_traits<char>> 836 if (ti.name == Id.basic_istream && 837 char_std_char_traits_char(ti, "Si")) 838 return true; 839 840 // ::std::basic_ostream<char, ::std::char_traits<char>> 841 if (ti.name == Id.basic_ostream && 842 char_std_char_traits_char(ti, "So")) 843 return true; 844 845 // ::std::basic_iostream<char, ::std::char_traits<char>> 846 if (ti.name == Id.basic_iostream && 847 char_std_char_traits_char(ti, "Sd")) 848 return true; 849 850 return false; 851 } 852 853 854 void cpp_mangle_name(Dsymbol s, bool qualified) 855 { 856 //printf("cpp_mangle_name(%s, %d)\n", s.toChars(), qualified); 857 Dsymbol p = s.toParent(); 858 Dsymbol se = s; 859 bool write_prefix = true; 860 if (p && p.isTemplateInstance()) 861 { 862 se = p; 863 if (find(p.isTemplateInstance().tempdecl) >= 0) 864 write_prefix = false; 865 p = p.toParent(); 866 } 867 if (p && !p.isModule()) 868 { 869 /* The N..E is not required if: 870 * 1. the parent is 'std' 871 * 2. 'std' is the initial qualifier 872 * 3. there is no CV-qualifier or a ref-qualifier for a member function 873 * ABI 5.1.8 874 */ 875 if (isStd(p) && !qualified) 876 { 877 TemplateInstance ti = se.isTemplateInstance(); 878 if (s.ident == Id.allocator) 879 { 880 buf.writestring("Sa"); // "Sa" is short for ::std::allocator 881 template_args(ti); 882 } 883 else if (s.ident == Id.basic_string) 884 { 885 // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>> 886 if (ti.tiargs.dim == 3 && 887 isChar((*ti.tiargs)[0]) && 888 isChar_traits_char((*ti.tiargs)[1]) && 889 isAllocator_char((*ti.tiargs)[2])) 890 { 891 buf.writestring("Ss"); 892 return; 893 } 894 buf.writestring("Sb"); // ::std::basic_string 895 template_args(ti); 896 } 897 else 898 { 899 // ::std::basic_istream<char, ::std::char_traits<char>> 900 if (s.ident == Id.basic_istream) 901 { 902 if (char_std_char_traits_char(ti, "Si")) 903 return; 904 } 905 else if (s.ident == Id.basic_ostream) 906 { 907 if (char_std_char_traits_char(ti, "So")) 908 return; 909 } 910 else if (s.ident == Id.basic_iostream) 911 { 912 if (char_std_char_traits_char(ti, "Sd")) 913 return; 914 } 915 buf.writestring("St"); 916 source_name(se, true); 917 } 918 } 919 else 920 { 921 buf.writeByte('N'); 922 if (write_prefix) 923 { 924 if (isStd(p)) 925 buf.writestring("St"); 926 else 927 prefix_name(p); 928 } 929 source_name(se, true); 930 buf.writeByte('E'); 931 } 932 } 933 else 934 source_name(se, false); 935 append(s); 936 } 937 938 /** 939 * Write CV-qualifiers to the buffer 940 * 941 * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const 942 * 943 * See_Also: 944 * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers 945 */ 946 void CV_qualifiers(const Type t) 947 { 948 if (t.isConst()) 949 buf.writeByte('K'); 950 } 951 952 /** 953 * Mangles a variable 954 * 955 * Params: 956 * d = Variable declaration to mangle 957 * isNested = Whether this variable is nested, e.g. a template parameter 958 * or within a namespace 959 */ 960 void mangle_variable(VarDeclaration d, bool isNested) 961 { 962 // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525 963 if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared))) 964 { 965 d.error("Internal Compiler Error: C++ static non-`__gshared` non-`extern` variables not supported"); 966 fatal(); 967 } 968 Dsymbol p = d.toParent(); 969 if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE" 970 { 971 buf.writestring("_ZN"); 972 prefix_name(p); 973 source_name(d, true); 974 buf.writeByte('E'); 975 } 976 else if (isNested) 977 { 978 buf.writestring("_Z"); 979 source_name(d, false); 980 } 981 else 982 { 983 if (auto varTags = ABITagContainer.forSymbol(d)) 984 { 985 buf.writestring("_Z"); 986 source_name(d, false); 987 return; 988 } 989 if (auto typeTags = ABITagContainer.forSymbol(d.type.toDsymbol(null))) 990 { 991 buf.writestring("_Z"); 992 source_name(d, false); 993 this.abiTags.write(*this.buf, typeTags); 994 return; 995 } 996 //char beta[6] should mangle as "beta" 997 buf.writestring(d.ident.toString()); 998 } 999 } 1000 1001 void mangle_function(FuncDeclaration d) 1002 { 1003 //printf("mangle_function(%s)\n", d.toChars()); 1004 /* 1005 * <mangled-name> ::= _Z <encoding> 1006 * <encoding> ::= <function name> <bare-function-type> 1007 * ::= <data name> 1008 * ::= <special-name> 1009 */ 1010 TypeFunction tf = cast(TypeFunction)d.type; 1011 buf.writestring("_Z"); 1012 1013 if (TemplateDeclaration ftd = getFuncTemplateDecl(d)) 1014 { 1015 /* It's an instance of a function template 1016 */ 1017 TemplateInstance ti = d.parent.isTemplateInstance(); 1018 assert(ti); 1019 this.mangleTemplatedFunction(d, tf, ftd, ti); 1020 } 1021 else 1022 { 1023 Dsymbol p = d.toParent(); 1024 if (p && !p.isModule() && tf.linkage == LINK.cpp) 1025 { 1026 this.mangleNestedFuncPrefix(tf, p); 1027 1028 if (auto ctor = d.isCtorDeclaration()) 1029 buf.writestring(ctor.isCpCtor ? "C2" : "C1"); 1030 else if (d.isPrimaryDtor()) 1031 buf.writestring("D1"); 1032 else if (d.ident && d.ident == Id.assign) 1033 buf.writestring("aS"); 1034 else if (d.ident && d.ident == Id.eq) 1035 buf.writestring("eq"); 1036 else if (d.ident && d.ident == Id.index) 1037 buf.writestring("ix"); 1038 else if (d.ident && d.ident == Id.call) 1039 buf.writestring("cl"); 1040 else 1041 source_name(d, true); 1042 buf.writeByte('E'); 1043 } 1044 else 1045 { 1046 source_name(d, false); 1047 } 1048 1049 // Save offset for potentially writing tags 1050 const size_t off = this.buf.length(); 1051 1052 // Template args accept extern "C" symbols with special mangling 1053 if (tf.linkage == LINK.cpp) 1054 mangleFunctionParameters(tf.parameterList); 1055 1056 if (!tf.next.isTypeBasic()) 1057 this.writeRemainingTags(off, tf); 1058 } 1059 } 1060 1061 /** 1062 * Recursively mangles a non-scoped namespace 1063 * 1064 * Parameters: 1065 * ns = Namespace to mangle 1066 * dg = A delegate to write the identifier in this namespace 1067 * haveNE = When `false` (the default), surround the namespace / dg 1068 * call with nested name qualifier (`N..E`). 1069 * Otherwise, they are already present (e.g. `Nspace` was used). 1070 */ 1071 void writeNamespace(CPPNamespaceDeclaration ns, scope void delegate() dg, 1072 bool haveNE = false) 1073 { 1074 void runDg () { if (dg !is null) dg(); } 1075 1076 if (ns is null) 1077 return runDg(); 1078 1079 if (isStd(ns)) 1080 { 1081 if (!substitute(ns)) 1082 buf.writestring("St"); 1083 runDg(); 1084 } 1085 else if (dg !is null) 1086 { 1087 if (!haveNE) 1088 buf.writestring("N"); 1089 if (!substitute(ns)) 1090 { 1091 this.writeNamespace(ns.cppnamespace, null); 1092 this.writeIdentifier(ns.ident); 1093 append(ns); 1094 } 1095 dg(); 1096 if (!haveNE) 1097 buf.writestring("E"); 1098 } 1099 else if (!substitute(ns)) 1100 { 1101 this.writeNamespace(ns.cppnamespace, null); 1102 this.writeIdentifier(ns.ident); 1103 append(ns); 1104 } 1105 } 1106 1107 /** 1108 * Mangles a function template to C++ 1109 * 1110 * Params: 1111 * d = Function declaration 1112 * tf = Function type (casted d.type) 1113 * ftd = Template declaration (ti.templdecl) 1114 * ti = Template instance (d.parent) 1115 */ 1116 void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf, 1117 TemplateDeclaration ftd, TemplateInstance ti) 1118 { 1119 Dsymbol p = ti.toParent(); 1120 // Check if this function is *not* nested 1121 if (!p || p.isModule() || tf.linkage != LINK.cpp) 1122 { 1123 this.context.ti = ti; 1124 this.context.fd = d; 1125 this.context.res = d; 1126 TypeFunction preSemantic = cast(TypeFunction)d.originalType; 1127 auto nspace = ti.toParent(); 1128 if (nspace && nspace.isNspace()) 1129 this.writeChained(ti.toParent(), () => source_name(ti, true)); 1130 else 1131 source_name(ti, false); 1132 this.mangleReturnType(preSemantic); 1133 this.mangleFunctionParameters(ParameterList(preSemantic.parameterList.parameters, tf.parameterList.varargs)); 1134 return; 1135 } 1136 1137 // It's a nested function (e.g. a member of an aggregate) 1138 this.mangleNestedFuncPrefix(tf, p); 1139 1140 if (d.isCtorDeclaration()) 1141 { 1142 buf.writestring("C1"); 1143 } 1144 else if (d.isPrimaryDtor()) 1145 { 1146 buf.writestring("D1"); 1147 } 1148 else 1149 { 1150 int firstTemplateArg = 0; 1151 bool appendReturnType = true; 1152 bool isConvertFunc = false; 1153 string symName; 1154 1155 // test for special symbols 1156 CppOperator whichOp = isCppOperator(ti.name); 1157 final switch (whichOp) 1158 { 1159 case CppOperator.Unknown: 1160 break; 1161 case CppOperator.Cast: 1162 symName = "cv"; 1163 firstTemplateArg = 1; 1164 isConvertFunc = true; 1165 appendReturnType = false; 1166 break; 1167 case CppOperator.Assign: 1168 symName = "aS"; 1169 break; 1170 case CppOperator.Eq: 1171 symName = "eq"; 1172 break; 1173 case CppOperator.Index: 1174 symName = "ix"; 1175 break; 1176 case CppOperator.Call: 1177 symName = "cl"; 1178 break; 1179 case CppOperator.Unary: 1180 case CppOperator.Binary: 1181 case CppOperator.OpAssign: 1182 TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); 1183 assert(td); 1184 assert(ti.tiargs.dim >= 1); 1185 TemplateParameter tp = (*td.parameters)[0]; 1186 TemplateValueParameter tv = tp.isTemplateValueParameter(); 1187 if (!tv || !tv.valType.isString()) 1188 break; // expecting a string argument to operators! 1189 Expression exp = (*ti.tiargs)[0].isExpression(); 1190 StringExp str = exp.toStringExp(); 1191 switch (whichOp) 1192 { 1193 case CppOperator.Unary: 1194 switch (str.peekString()) 1195 { 1196 case "*": symName = "de"; goto continue_template; 1197 case "++": symName = "pp"; goto continue_template; 1198 case "--": symName = "mm"; goto continue_template; 1199 case "-": symName = "ng"; goto continue_template; 1200 case "+": symName = "ps"; goto continue_template; 1201 case "~": symName = "co"; goto continue_template; 1202 default: break; 1203 } 1204 break; 1205 case CppOperator.Binary: 1206 switch (str.peekString()) 1207 { 1208 case ">>": symName = "rs"; goto continue_template; 1209 case "<<": symName = "ls"; goto continue_template; 1210 case "*": symName = "ml"; goto continue_template; 1211 case "-": symName = "mi"; goto continue_template; 1212 case "+": symName = "pl"; goto continue_template; 1213 case "&": symName = "an"; goto continue_template; 1214 case "/": symName = "dv"; goto continue_template; 1215 case "%": symName = "rm"; goto continue_template; 1216 case "^": symName = "eo"; goto continue_template; 1217 case "|": symName = "or"; goto continue_template; 1218 default: break; 1219 } 1220 break; 1221 case CppOperator.OpAssign: 1222 switch (str.peekString()) 1223 { 1224 case "*": symName = "mL"; goto continue_template; 1225 case "+": symName = "pL"; goto continue_template; 1226 case "-": symName = "mI"; goto continue_template; 1227 case "/": symName = "dV"; goto continue_template; 1228 case "%": symName = "rM"; goto continue_template; 1229 case ">>": symName = "rS"; goto continue_template; 1230 case "<<": symName = "lS"; goto continue_template; 1231 case "&": symName = "aN"; goto continue_template; 1232 case "|": symName = "oR"; goto continue_template; 1233 case "^": symName = "eO"; goto continue_template; 1234 default: break; 1235 } 1236 break; 1237 default: 1238 assert(0); 1239 continue_template: 1240 firstTemplateArg = 1; 1241 break; 1242 } 1243 break; 1244 } 1245 if (symName.length == 0) 1246 source_name(ti, true); 1247 else 1248 { 1249 buf.writestring(symName); 1250 if (isConvertFunc) 1251 template_arg(ti, 0); 1252 appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType; 1253 } 1254 buf.writeByte('E'); 1255 if (appendReturnType) 1256 headOfType(tf.nextOf()); // mangle return type 1257 } 1258 mangleFunctionParameters(tf.parameterList); 1259 } 1260 1261 /** 1262 * Mangle the parameters of a function 1263 * 1264 * For templated functions, `context.res` is set to the `FuncDeclaration` 1265 * 1266 * Params: 1267 * parameters = Array of `Parameter` to mangle 1268 * varargs = if != 0, this function has varargs parameters 1269 */ 1270 void mangleFunctionParameters(ParameterList parameterList) 1271 { 1272 int numparams = 0; 1273 1274 foreach (n, fparam; parameterList) 1275 { 1276 Type t = target.cpp.parameterType(fparam); 1277 if (t.ty == Tsarray) 1278 { 1279 // Static arrays in D are passed by value; no counterpart in C++ 1280 .error(loc, "Internal Compiler Error: unable to pass static array `%s` to extern(C++) function, use pointer instead", 1281 t.toChars()); 1282 fatal(); 1283 } 1284 auto prev = this.context.push({ 1285 TypeFunction tf; 1286 if (isDsymbol(this.context.res)) 1287 tf = cast(TypeFunction)this.context.res.asFuncDecl().type; 1288 else 1289 tf = this.context.res.asType().isTypeFunction(); 1290 assert(tf); 1291 return (*tf.parameterList.parameters)[n].type; 1292 }()); 1293 scope (exit) this.context.pop(prev); 1294 1295 if (this.context.ti && global.params.cplusplus >= CppStdRevision.cpp11) 1296 handleParamPack(t, this.context.ti.tempdecl.isTemplateDeclaration().parameters); 1297 1298 headOfType(t); 1299 ++numparams; 1300 } 1301 1302 if (parameterList.varargs == VarArg.variadic) 1303 buf.writeByte('z'); 1304 else if (!numparams) 1305 buf.writeByte('v'); // encode (void) parameters 1306 } 1307 1308 /****** The rest is type mangling ************/ 1309 1310 void error(Type t) 1311 { 1312 const(char)* p; 1313 if (t.isImmutable()) 1314 p = "`immutable` "; 1315 else if (t.isShared()) 1316 p = "`shared` "; 1317 else 1318 p = ""; 1319 .error(loc, "Internal Compiler Error: %stype `%s` cannot be mapped to C++\n", p, t.toChars()); 1320 fatal(); //Fatal, because this error should be handled in frontend 1321 } 1322 1323 /**************************** 1324 * Mangle a type, 1325 * treating it as a Head followed by a Tail. 1326 * Params: 1327 * t = Head of a type 1328 */ 1329 void headOfType(Type t) 1330 { 1331 if (t.ty == Tclass) 1332 { 1333 mangleTypeClass(cast(TypeClass)t, true); 1334 } 1335 else 1336 { 1337 // For value types, strip const/immutable/shared from the head of the type 1338 auto prev = this.context.push(this.context.res.asType().mutableOf().unSharedOf()); 1339 scope (exit) this.context.pop(prev); 1340 t.mutableOf().unSharedOf().accept(this); 1341 } 1342 } 1343 1344 /****** 1345 * Write out 1 or 2 character basic type mangling. 1346 * Handle const and substitutions. 1347 * Params: 1348 * t = type to mangle 1349 * p = if not 0, then character prefix 1350 * c = mangling character 1351 */ 1352 void writeBasicType(Type t, char p, char c) 1353 { 1354 // Only do substitutions for non-fundamental types. 1355 if (!isFundamentalType(t) || t.isConst()) 1356 { 1357 if (substitute(t)) 1358 return; 1359 else 1360 append(t); 1361 } 1362 CV_qualifiers(t); 1363 if (p) 1364 buf.writeByte(p); 1365 buf.writeByte(c); 1366 } 1367 1368 1369 /**************** 1370 * Write structs and enums. 1371 * Params: 1372 * t = TypeStruct or TypeEnum 1373 */ 1374 void doSymbol(Type t) 1375 { 1376 if (substitute(t)) 1377 return; 1378 CV_qualifiers(t); 1379 1380 // Handle any target-specific struct types. 1381 if (auto tm = target.cpp.typeMangle(t)) 1382 { 1383 buf.writestring(tm); 1384 } 1385 else 1386 { 1387 Dsymbol s = t.toDsymbol(null); 1388 Dsymbol p = s.toParent(); 1389 if (p && p.isTemplateInstance()) 1390 { 1391 /* https://issues.dlang.org/show_bug.cgi?id=17947 1392 * Substitute the template instance symbol, not the struct/enum symbol 1393 */ 1394 if (substitute(p)) 1395 return; 1396 } 1397 if (!substitute(s)) 1398 cpp_mangle_name(s, false); 1399 } 1400 if (t.isConst()) 1401 append(t); 1402 } 1403 1404 1405 1406 /************************ 1407 * Mangle a class type. 1408 * If it's the head, treat the initial pointer as a value type. 1409 * Params: 1410 * t = class type 1411 * head = true for head of a type 1412 */ 1413 void mangleTypeClass(TypeClass t, bool head) 1414 { 1415 if (t.isImmutable() || t.isShared()) 1416 return error(t); 1417 1418 /* Mangle as a <pointer to><struct> 1419 */ 1420 if (substitute(t)) 1421 return; 1422 if (!head) 1423 CV_qualifiers(t); 1424 buf.writeByte('P'); 1425 1426 CV_qualifiers(t); 1427 1428 { 1429 Dsymbol s = t.toDsymbol(null); 1430 Dsymbol p = s.toParent(); 1431 if (p && p.isTemplateInstance()) 1432 { 1433 /* https://issues.dlang.org/show_bug.cgi?id=17947 1434 * Substitute the template instance symbol, not the class symbol 1435 */ 1436 if (substitute(p)) 1437 return; 1438 } 1439 } 1440 1441 if (!substitute(t.sym)) 1442 { 1443 cpp_mangle_name(t.sym, false); 1444 } 1445 if (t.isConst()) 1446 append(null); // C++ would have an extra type here 1447 append(t); 1448 } 1449 1450 /** 1451 * Mangle the prefix of a nested (e.g. member) function 1452 * 1453 * Params: 1454 * tf = Type of the nested function 1455 * parent = Parent in which the function is nested 1456 */ 1457 void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent) 1458 { 1459 /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E 1460 * ::= N [<CV-qualifiers>] <template-prefix> <template-args> E 1461 */ 1462 buf.writeByte('N'); 1463 CV_qualifiers(tf); 1464 1465 /* <prefix> ::= <prefix> <unqualified-name> 1466 * ::= <template-prefix> <template-args> 1467 * ::= <template-param> 1468 * ::= # empty 1469 * ::= <substitution> 1470 * ::= <prefix> <data-member-prefix> 1471 */ 1472 prefix_name(parent); 1473 } 1474 1475 /** 1476 * Write `Dp` (C++11 function parameter pack prefix) if 't' is a TemplateSequenceParameter (T...). 1477 * 1478 * Params: 1479 * t = Parameter type 1480 * params = Template parameters of the function 1481 */ 1482 private void handleParamPack(Type t, TemplateParameters* params) 1483 { 1484 if (t.isTypeReference()) 1485 t = t.nextOf(); 1486 auto ti = t.isTypeIdentifier(); 1487 if (!ti) 1488 return; 1489 1490 auto idx = templateParamIndex(ti.ident, params); 1491 if (idx < params.length && (*params)[idx].isTemplateTupleParameter()) 1492 buf.writestring("Dp"); 1493 } 1494 1495 /** 1496 * Helper function to write a `T..._` template index. 1497 * 1498 * Params: 1499 * idx = Index of `param` in the template argument list 1500 * param = Template parameter to mangle 1501 */ 1502 private void writeTemplateArgIndex(size_t idx, TemplateParameter param) 1503 { 1504 // expressions are mangled in <X..E> 1505 if (param.isTemplateValueParameter()) 1506 buf.writeByte('X'); 1507 buf.writeByte('T'); 1508 writeSequenceFromIndex(idx); 1509 buf.writeByte('_'); 1510 if (param.isTemplateValueParameter()) 1511 buf.writeByte('E'); 1512 } 1513 1514 /** 1515 * Given an array of template parameters and an identifier, 1516 * returns the index of the identifier in that array. 1517 * 1518 * Params: 1519 * ident = Identifier for which substitution is attempted 1520 * (e.g. `void func(T)(T param)` => `T` from `T param`) 1521 * params = `TemplateParameters` of the enclosing symbol 1522 * (in the previous example, `func`'s template parameters) 1523 * 1524 * Returns: 1525 * The index of the identifier match in `params`, 1526 * or `params.length` if there wasn't any match. 1527 */ 1528 private static size_t templateParamIndex( 1529 const ref Identifier ident, TemplateParameters* params) 1530 { 1531 foreach (idx, param; *params) 1532 if (param.ident == ident) 1533 return idx; 1534 return params.length; 1535 } 1536 1537 /** 1538 * Given a template instance `t`, write its qualified name 1539 * without the template parameter list 1540 * 1541 * Params: 1542 * t = Post-parsing `TemplateInstance` pointing to the symbol 1543 * to mangle (one level deep) 1544 * dg = Delegate to execute after writing the qualified symbol 1545 * 1546 */ 1547 private void writeQualified(TemplateInstance t, scope void delegate() dg) 1548 { 1549 auto type = isType(this.context.res); 1550 if (!type) 1551 { 1552 this.writeIdentifier(t.name); 1553 return dg(); 1554 } 1555 auto sym1 = type.toDsymbol(null); 1556 if (!sym1) 1557 { 1558 this.writeIdentifier(t.name); 1559 return dg(); 1560 } 1561 // Get the template instance 1562 auto sym = getQualifier(sym1); 1563 auto sym2 = getQualifier(sym); 1564 if (sym2 && isStd(sym2)) // Nspace path 1565 { 1566 bool unused; 1567 assert(sym.isTemplateInstance()); 1568 if (this.writeStdSubstitution(sym.isTemplateInstance(), unused)) 1569 return dg(); 1570 // std names don't require `N..E` 1571 buf.writestring("St"); 1572 this.writeIdentifier(t.name); 1573 this.append(t); 1574 return dg(); 1575 } 1576 else if (sym2) 1577 { 1578 buf.writestring("N"); 1579 if (!this.substitute(sym2)) 1580 sym2.accept(this); 1581 } 1582 this.writeNamespace( 1583 sym1.cppnamespace, () { 1584 this.writeIdentifier(t.name); 1585 this.append(t); 1586 dg(); 1587 }); 1588 if (sym2) 1589 buf.writestring("E"); 1590 } 1591 1592 extern(C++): 1593 1594 alias visit = Visitor.visit; 1595 1596 override void visit(TypeNull t) 1597 { 1598 if (t.isImmutable() || t.isShared()) 1599 return error(t); 1600 1601 writeBasicType(t, 'D', 'n'); 1602 } 1603 1604 override void visit(TypeBasic t) 1605 { 1606 if (t.isImmutable() || t.isShared()) 1607 return error(t); 1608 1609 // Handle any target-specific basic types. 1610 if (auto tm = target.cpp.typeMangle(t)) 1611 { 1612 // Only do substitutions for non-fundamental types. 1613 if (!isFundamentalType(t) || t.isConst()) 1614 { 1615 if (substitute(t)) 1616 return; 1617 else 1618 append(t); 1619 } 1620 CV_qualifiers(t); 1621 buf.writestring(tm); 1622 return; 1623 } 1624 1625 /* <builtin-type>: 1626 * v void 1627 * w wchar_t 1628 * b bool 1629 * c char 1630 * a signed char 1631 * h unsigned char 1632 * s short 1633 * t unsigned short 1634 * i int 1635 * j unsigned int 1636 * l long 1637 * m unsigned long 1638 * x long long, __int64 1639 * y unsigned long long, __int64 1640 * n __int128 1641 * o unsigned __int128 1642 * f float 1643 * d double 1644 * e long double, __float80 1645 * g __float128 1646 * z ellipsis 1647 * Dd 64 bit IEEE 754r decimal floating point 1648 * De 128 bit IEEE 754r decimal floating point 1649 * Df 32 bit IEEE 754r decimal floating point 1650 * Dh 16 bit IEEE 754r half-precision floating point 1651 * Di char32_t 1652 * Ds char16_t 1653 * u <source-name> # vendor extended type 1654 */ 1655 char c; 1656 char p = 0; 1657 switch (t.ty) 1658 { 1659 case Tvoid: c = 'v'; break; 1660 case Tint8: c = 'a'; break; 1661 case Tuns8: c = 'h'; break; 1662 case Tint16: c = 's'; break; 1663 case Tuns16: c = 't'; break; 1664 case Tint32: c = 'i'; break; 1665 case Tuns32: c = 'j'; break; 1666 case Tfloat32: c = 'f'; break; 1667 case Tint64: 1668 c = target.c.longsize == 8 ? 'l' : 'x'; 1669 break; 1670 case Tuns64: 1671 c = target.c.longsize == 8 ? 'm' : 'y'; 1672 break; 1673 case Tint128: c = 'n'; break; 1674 case Tuns128: c = 'o'; break; 1675 case Tfloat64: c = 'd'; break; 1676 case Tfloat80: c = 'e'; break; 1677 case Tbool: c = 'b'; break; 1678 case Tchar: c = 'c'; break; 1679 case Twchar: p = 'D'; c = 's'; break; // since C++11 1680 case Tdchar: p = 'D'; c = 'i'; break; // since C++11 1681 case Timaginary32: p = 'G'; c = 'f'; break; // 'G' means imaginary 1682 case Timaginary64: p = 'G'; c = 'd'; break; 1683 case Timaginary80: p = 'G'; c = 'e'; break; 1684 case Tcomplex32: p = 'C'; c = 'f'; break; // 'C' means complex 1685 case Tcomplex64: p = 'C'; c = 'd'; break; 1686 case Tcomplex80: p = 'C'; c = 'e'; break; 1687 1688 default: 1689 return error(t); 1690 } 1691 writeBasicType(t, p, c); 1692 } 1693 1694 override void visit(TypeVector t) 1695 { 1696 if (t.isImmutable() || t.isShared()) 1697 return error(t); 1698 1699 if (substitute(t)) 1700 return; 1701 append(t); 1702 CV_qualifiers(t); 1703 1704 // Handle any target-specific vector types. 1705 if (auto tm = target.cpp.typeMangle(t)) 1706 { 1707 buf.writestring(tm); 1708 } 1709 else 1710 { 1711 assert(t.basetype && t.basetype.ty == Tsarray); 1712 assert((cast(TypeSArray)t.basetype).dim); 1713 version (none) 1714 { 1715 buf.writestring("Dv"); 1716 buf.print((cast(TypeSArray *)t.basetype).dim.toInteger()); // -- Gnu ABI v.4 1717 buf.writeByte('_'); 1718 } 1719 else 1720 buf.writestring("U8__vector"); //-- Gnu ABI v.3 1721 t.basetype.nextOf().accept(this); 1722 } 1723 } 1724 1725 override void visit(TypeSArray t) 1726 { 1727 if (t.isImmutable() || t.isShared()) 1728 return error(t); 1729 1730 if (!substitute(t)) 1731 append(t); 1732 CV_qualifiers(t); 1733 buf.writeByte('A'); 1734 buf.print(t.dim ? t.dim.toInteger() : 0); 1735 buf.writeByte('_'); 1736 t.next.accept(this); 1737 } 1738 1739 override void visit(TypePointer t) 1740 { 1741 if (t.isImmutable() || t.isShared()) 1742 return error(t); 1743 1744 // Check for const - Since we cannot represent C++'s `char* const`, 1745 // and `const char* const` (a.k.a `const(char*)` in D) is mangled 1746 // the same as `const char*` (`const(char)*` in D), we need to add 1747 // an extra `K` if `nextOf()` is `const`, before substitution 1748 CV_qualifiers(t); 1749 if (substitute(t)) 1750 return; 1751 buf.writeByte('P'); 1752 auto prev = this.context.push(this.context.res.asType().nextOf()); 1753 scope (exit) this.context.pop(prev); 1754 t.next.accept(this); 1755 append(t); 1756 } 1757 1758 override void visit(TypeReference t) 1759 { 1760 if (substitute(t)) 1761 return; 1762 buf.writeByte('R'); 1763 CV_qualifiers(t.nextOf()); 1764 headOfType(t.nextOf()); 1765 if (t.nextOf().isConst()) 1766 append(t.nextOf()); 1767 append(t); 1768 } 1769 1770 override void visit(TypeFunction t) 1771 { 1772 /* 1773 * <function-type> ::= F [Y] <bare-function-type> E 1774 * <bare-function-type> ::= <signature type>+ 1775 * # types are possible return type, then parameter types 1776 */ 1777 /* ABI says: 1778 "The type of a non-static member function is considered to be different, 1779 for the purposes of substitution, from the type of a namespace-scope or 1780 static member function whose type appears similar. The types of two 1781 non-static member functions are considered to be different, for the 1782 purposes of substitution, if the functions are members of different 1783 classes. In other words, for the purposes of substitution, the class of 1784 which the function is a member is considered part of the type of 1785 function." 1786 1787 BUG: Right now, types of functions are never merged, so our simplistic 1788 component matcher always finds them to be different. 1789 We should use Type.equals on these, and use different 1790 TypeFunctions for non-static member functions, and non-static 1791 member functions of different classes. 1792 */ 1793 if (substitute(t)) 1794 return; 1795 buf.writeByte('F'); 1796 if (t.linkage == LINK.c) 1797 buf.writeByte('Y'); 1798 Type tn = t.next; 1799 if (t.isref) 1800 tn = tn.referenceTo(); 1801 tn.accept(this); 1802 mangleFunctionParameters(t.parameterList); 1803 buf.writeByte('E'); 1804 append(t); 1805 } 1806 1807 override void visit(TypeStruct t) 1808 { 1809 if (t.isImmutable() || t.isShared()) 1810 return error(t); 1811 //printf("TypeStruct %s\n", t.toChars()); 1812 doSymbol(t); 1813 } 1814 1815 override void visit(TypeEnum t) 1816 { 1817 if (t.isImmutable() || t.isShared()) 1818 return error(t); 1819 1820 /* __c_(u)long(long) get special mangling 1821 */ 1822 const id = t.sym.ident; 1823 //printf("enum id = '%s'\n", id.toChars()); 1824 if (id == Id.__c_long) 1825 return writeBasicType(t, 0, 'l'); 1826 else if (id == Id.__c_ulong) 1827 return writeBasicType(t, 0, 'm'); 1828 else if (id == Id.__c_wchar_t) 1829 return writeBasicType(t, 0, 'w'); 1830 else if (id == Id.__c_longlong) 1831 return writeBasicType(t, 0, 'x'); 1832 else if (id == Id.__c_ulonglong) 1833 return writeBasicType(t, 0, 'y'); 1834 1835 doSymbol(t); 1836 } 1837 1838 override void visit(TypeClass t) 1839 { 1840 mangleTypeClass(t, false); 1841 } 1842 1843 /** 1844 * Performs template parameter substitution 1845 * 1846 * Mangling is performed on a copy of the post-parsing AST before 1847 * any semantic pass is run. 1848 * There is no easy way to link a type to the template parameters 1849 * once semantic has run, because: 1850 * - the `TemplateInstance` installs aliases in its scope to its params 1851 * - `AliasDeclaration`s are resolved in many places 1852 * - semantic passes are destructive, so the `TypeIdentifier` gets lost 1853 * 1854 * As a result, the best approach with the current architecture is to: 1855 * - Run the visitor on the `originalType` of the function, 1856 * looking up any `TypeIdentifier` at the template scope when found. 1857 * - Fallback to the post-semantic `TypeFunction` when the identifier is 1858 * not a template parameter. 1859 */ 1860 override void visit(TypeIdentifier t) 1861 { 1862 auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; 1863 assert(decl.parameters !is null); 1864 auto idx = templateParamIndex(t.ident, decl.parameters); 1865 // If not found, default to the post-semantic type 1866 if (idx >= decl.parameters.length) 1867 return this.context.res.visitObject(this); 1868 1869 auto param = (*decl.parameters)[idx]; 1870 if (auto type = this.context.res.isType()) 1871 CV_qualifiers(type); 1872 // Otherwise, attempt substitution (`S_` takes precedence on `T_`) 1873 if (this.substitute(param)) 1874 return; 1875 1876 // If substitution failed, write `TX_` where `X` is the index 1877 this.writeTemplateArgIndex(idx, param); 1878 this.append(param); 1879 // Write the ABI tags, if any 1880 if (auto sym = this.context.res.isDsymbol()) 1881 this.abiTags.writeSymbol(sym, this); 1882 } 1883 1884 /// Ditto 1885 override void visit(TypeInstance t) 1886 { 1887 assert(t.tempinst !is null); 1888 t.tempinst.accept(this); 1889 } 1890 1891 /** 1892 * Mangles a `TemplateInstance` 1893 * 1894 * A `TemplateInstance` can be found either in the parameter, 1895 * or the return value. 1896 * Arguments to the template instance needs to be mangled but the template 1897 * can be partially substituted, so for example the following: 1898 * `Container!(T, Val) func16479_12 (alias Container, T, int Val) ()` 1899 * will mangle the return value part to "T_IT0_XT1_EE" 1900 */ 1901 override void visit(TemplateInstance t) 1902 { 1903 // Template names are substituted, but args still need to be written 1904 void writeArgs () 1905 { 1906 buf.writeByte('I'); 1907 // When visiting the arguments, the context will be set to the 1908 // resolved type 1909 auto analyzed_ti = this.context.res.asType().toDsymbol(null).isInstantiated(); 1910 auto prev = this.context; 1911 scope (exit) this.context.pop(prev); 1912 foreach (idx, RootObject o; *t.tiargs) 1913 { 1914 this.context.res = (*analyzed_ti.tiargs)[idx]; 1915 o.visitObject(this); 1916 } 1917 if (analyzed_ti.tiargs.dim > t.tiargs.dim) 1918 { 1919 // If the resolved AST has more args than the parse one, 1920 // we have default arguments 1921 auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters; 1922 foreach (idx, arg; (*oparams)[t.tiargs.dim .. $]) 1923 { 1924 this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.dim]; 1925 1926 if (auto ttp = arg.isTemplateTypeParameter()) 1927 ttp.defaultType.accept(this); 1928 else if (auto tvp = arg.isTemplateValueParameter()) 1929 tvp.defaultValue.accept(this); 1930 else if (auto tvp = arg.isTemplateThisParameter()) 1931 tvp.defaultType.accept(this); 1932 else if (auto tvp = arg.isTemplateAliasParameter()) 1933 tvp.defaultAlias.visitObject(this); 1934 else 1935 assert(0, arg.toString()); 1936 } 1937 } 1938 buf.writeByte('E'); 1939 } 1940 1941 // `name` is used, not `ident` 1942 assert(t.name !is null); 1943 assert(t.tiargs !is null); 1944 1945 bool needsTa; 1946 auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; 1947 // Attempt to substitute the template itself 1948 auto idx = templateParamIndex(t.name, decl.parameters); 1949 if (idx < decl.parameters.length) 1950 { 1951 auto param = (*decl.parameters)[idx]; 1952 if (auto type = t.getType()) 1953 CV_qualifiers(type); 1954 if (this.substitute(param)) 1955 return; 1956 this.writeTemplateArgIndex(idx, param); 1957 this.append(param); 1958 writeArgs(); 1959 } 1960 else if (this.writeStdSubstitution(t, needsTa)) 1961 { 1962 if (needsTa) 1963 writeArgs(); 1964 } 1965 else if (!this.substitute(t)) 1966 this.writeQualified(t, &writeArgs); 1967 } 1968 1969 /// Ditto 1970 override void visit(IntegerExp t) 1971 { 1972 this.buf.writeByte('L'); 1973 t.type.accept(this); 1974 this.buf.print(t.getInteger()); 1975 this.buf.writeByte('E'); 1976 } 1977 1978 override void visit(Nspace t) 1979 { 1980 if (auto p = getQualifier(t)) 1981 p.accept(this); 1982 1983 if (isStd(t)) 1984 buf.writestring("St"); 1985 else 1986 { 1987 this.writeIdentifier(t.ident); 1988 this.append(t); 1989 } 1990 } 1991 1992 override void visit(Type t) 1993 { 1994 error(t); 1995 } 1996 1997 void visit(Tuple t) 1998 { 1999 assert(0); 2000 } 2001 } 2002 2003 /// Helper code to visit `RootObject`, as it doesn't define `accept`, 2004 /// only its direct subtypes do. 2005 private void visitObject(V : Visitor)(RootObject o, V this_) 2006 { 2007 assert(o !is null); 2008 if (Type ta = isType(o)) 2009 ta.accept(this_); 2010 else if (Expression ea = isExpression(o)) 2011 ea.accept(this_); 2012 else if (Dsymbol sa = isDsymbol(o)) 2013 sa.accept(this_); 2014 else if (TemplateParameter t = isTemplateParameter(o)) 2015 t.accept(this_); 2016 else if (Tuple t = isTuple(o)) 2017 // `Tuple` inherits `RootObject` and does not define accept 2018 // For this reason, this uses static dispatch on the visitor 2019 this_.visit(t); 2020 else 2021 assert(0, o.toString()); 2022 } 2023 2024 /// Helper function to safely get a type out of a `RootObject` 2025 private Type asType(RootObject o) 2026 { 2027 Type ta = isType(o); 2028 // When called with context.res as argument, it can be `FuncDeclaration` 2029 if (!ta && o.asFuncDecl()) 2030 ta = (cast(FuncDeclaration)o).type; 2031 assert(ta !is null, o.toString()); 2032 return ta; 2033 } 2034 2035 /// Helper function to safely get a `FuncDeclaration` out of a `RootObject` 2036 private FuncDeclaration asFuncDecl(RootObject o) 2037 { 2038 Dsymbol d = isDsymbol(o); 2039 assert(d !is null); 2040 auto fd = d.isFuncDeclaration(); 2041 assert(fd !is null); 2042 return fd; 2043 } 2044 2045 /// Helper class to compare entries in components 2046 private extern(C++) final class ComponentVisitor : Visitor 2047 { 2048 /// Only one of the following is not `null`, it's always 2049 /// the most specialized type, set from the ctor 2050 private Nspace namespace; 2051 2052 /// Ditto 2053 private CPPNamespaceDeclaration namespace2; 2054 2055 /// Ditto 2056 private TypePointer tpointer; 2057 2058 /// Ditto 2059 private TypeReference tref; 2060 2061 /// Ditto 2062 private TypeIdentifier tident; 2063 2064 /// Least specialized type 2065 private RootObject object; 2066 2067 /// Set to the result of the comparison 2068 private bool result; 2069 2070 public this(RootObject base) 2071 { 2072 switch (base.dyncast()) 2073 { 2074 case DYNCAST.dsymbol: 2075 if (auto ns = (cast(Dsymbol)base).isNspace()) 2076 this.namespace = ns; 2077 else if (auto ns = (cast(Dsymbol)base).isCPPNamespaceDeclaration()) 2078 this.namespace2 = ns; 2079 else 2080 goto default; 2081 break; 2082 2083 case DYNCAST.type: 2084 auto t = cast(Type)base; 2085 if (t.ty == Tpointer) 2086 this.tpointer = cast(TypePointer)t; 2087 else if (t.ty == Treference) 2088 this.tref = cast(TypeReference)t; 2089 else if (t.ty == Tident) 2090 this.tident = cast(TypeIdentifier)t; 2091 else 2092 goto default; 2093 break; 2094 2095 // Note: ABI tags are also handled here (they are TupleExp of StringExp) 2096 default: 2097 this.object = base; 2098 } 2099 } 2100 2101 /// Introduce base class overloads 2102 alias visit = Visitor.visit; 2103 2104 /// Least specialized overload of each direct child of `RootObject` 2105 public override void visit(Dsymbol o) 2106 { 2107 this.result = this.object && this.object == o; 2108 } 2109 2110 /// Ditto 2111 public override void visit(Expression o) 2112 { 2113 this.result = this.object && this.object == o; 2114 } 2115 2116 /// Ditto 2117 public void visit(Tuple o) 2118 { 2119 this.result = this.object && this.object == o; 2120 } 2121 2122 /// Ditto 2123 public override void visit(Type o) 2124 { 2125 this.result = this.object && this.object == o; 2126 } 2127 2128 /// Ditto 2129 public override void visit(TemplateParameter o) 2130 { 2131 this.result = this.object && this.object == o; 2132 } 2133 2134 /** 2135 * This overload handles composed types including template parameters 2136 * 2137 * Components for substitutions include "next" type. 2138 * For example, if `ref T` is present, `ref T` and `T` will be present 2139 * in the substitution array. 2140 * But since we don't have the final/merged type, we cannot rely on 2141 * object comparison, and need to recurse instead. 2142 */ 2143 public override void visit(TypeReference o) 2144 { 2145 if (!this.tref) 2146 return; 2147 if (this.tref == o) 2148 this.result = true; 2149 else 2150 { 2151 // It might be a reference to a template parameter that we already 2152 // saw, so we need to recurse 2153 scope v = new ComponentVisitor(this.tref.next); 2154 o.next.visitObject(v); 2155 this.result = v.result; 2156 } 2157 } 2158 2159 /// Ditto 2160 public override void visit(TypePointer o) 2161 { 2162 if (!this.tpointer) 2163 return; 2164 if (this.tpointer == o) 2165 this.result = true; 2166 else 2167 { 2168 // It might be a pointer to a template parameter that we already 2169 // saw, so we need to recurse 2170 scope v = new ComponentVisitor(this.tpointer.next); 2171 o.next.visitObject(v); 2172 this.result = v.result; 2173 } 2174 } 2175 2176 /// Ditto 2177 public override void visit(TypeIdentifier o) 2178 { 2179 /// Since we know they are at the same level, scope resolution will 2180 /// give us the same symbol, thus we can just compare ident. 2181 this.result = (this.tident && (this.tident.ident == o.ident)); 2182 } 2183 2184 /** 2185 * Overload which accepts a Namespace 2186 * 2187 * It is very common for large C++ projects to have multiple files sharing 2188 * the same `namespace`. If any D project adopts the same approach 2189 * (e.g. separating data structures from functions), it will lead to two 2190 * `Nspace` objects being instantiated, with different addresses. 2191 * At the same time, we cannot compare just any Dsymbol via identifier, 2192 * because it messes with templates. 2193 * 2194 * See_Also: 2195 * https://issues.dlang.org/show_bug.cgi?id=18922 2196 * 2197 * Params: 2198 * ns = C++ namespace to do substitution for 2199 */ 2200 public override void visit(Nspace ns) 2201 { 2202 this.result = isNamespaceEqual(this.namespace, ns) 2203 || isNamespaceEqual(this.namespace2, ns); 2204 } 2205 2206 /// Ditto 2207 public override void visit(CPPNamespaceDeclaration ns) 2208 { 2209 this.result = isNamespaceEqual(this.namespace, ns) 2210 || isNamespaceEqual(this.namespace2, ns); 2211 } 2212 } 2213 2214 /// Transitional functions for `CPPNamespaceDeclaration` / `Nspace` 2215 /// Remove when `Nspace` is removed. 2216 private bool isNamespaceEqual (Nspace a, Nspace b) 2217 { 2218 if (a is null || b is null) 2219 return false; 2220 return a.equals(b); 2221 } 2222 2223 /// Ditto 2224 private bool isNamespaceEqual (Nspace a, CPPNamespaceDeclaration b) 2225 { 2226 return isNamespaceEqual(b, a); 2227 } 2228 2229 /// Ditto 2230 private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = 0) 2231 { 2232 if ((a is null) != (b is null)) 2233 return false; 2234 if (!a.ident.equals(b.ident)) 2235 return false; 2236 2237 // We need to see if there's more ident enclosing 2238 if (auto pb = b.toParent().isNspace()) 2239 return isNamespaceEqual(a.cppnamespace, pb); 2240 else 2241 return a.cppnamespace is null; 2242 } 2243 2244 /// Returns: 2245 /// Whether two `CPPNamespaceDeclaration` are equals 2246 private bool isNamespaceEqual (CPPNamespaceDeclaration a, CPPNamespaceDeclaration b) 2247 { 2248 if (a is null || b is null) 2249 return false; 2250 2251 if ((a.cppnamespace is null) != (b.cppnamespace is null)) 2252 return false; 2253 if (a.ident != b.ident) 2254 return false; 2255 return a.cppnamespace is null ? true : isNamespaceEqual(a.cppnamespace, b.cppnamespace); 2256 } 2257 2258 /** 2259 * A container for ABI tags 2260 * 2261 * At its hearth, there is a sorted array of ABI tags having been written 2262 * already. ABI tags can be present on parameters, template parameters, 2263 * return value, and varaible. ABI tags for a given type needs to be written 2264 * sorted. When a function returns a type that has ABI tags, only the tags that 2265 * haven't been printed as part of the mangling (e.g. arguments) are written 2266 * directly after the function name. 2267 * 2268 * This means that: 2269 * --- 2270 * /++ C++ type definitions: 2271 * struct [[gnu::abi_tag("tag1")]] Struct1 {}; 2272 * struct [[gnu::abi_tag("tag2")]] Struct2 {}; 2273 * // Can also be: "tag2", "tag1", since tags are sorted. 2274 * struct [[gnu::abi_tag("tag1", "tag2")]] Struct3 {}; 2275 * +/ 2276 * // Functions definitions: 2277 * Struct3 func1 (Struct1); 2278 * Struct3 func2 (Struct2); 2279 * Struct3 func3 (Struct2, Struct1); 2280 * --- 2281 * Will be respectively pseudo-mangled (part of interest between stars) as: 2282 * "_Z4 func1 *B4tag2* ParamsMangling" (ParamsMangling includes tag1), 2283 * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes tag2), 2284 * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes both). 2285 * 2286 * This is why why need to keep a list of tags that were written, 2287 * and insert the missing one after parameter mangling has been written. 2288 * Since there's a lot of operations that are not easily doable in DMD 2289 * (since we can't use Phobos), this special container is implemented. 2290 */ 2291 private struct ABITagContainer 2292 { 2293 private Array!StringExp written; 2294 2295 static ArrayLiteralExp forSymbol (Dsymbol s) 2296 { 2297 if (!s) 2298 return null; 2299 // If this is a template instance, we want the declaration, 2300 // as that's where the UDAs are 2301 if (auto ti = s.isTemplateInstance()) 2302 s = ti.tempdecl; 2303 if (!s.userAttribDecl || !s.userAttribDecl.atts) 2304 return null; 2305 2306 foreach (exp; *s.userAttribDecl.atts) 2307 { 2308 if (UserAttributeDeclaration.isGNUABITag(exp)) 2309 return (*exp.isStructLiteralExp().elements)[0] 2310 .isArrayLiteralExp(); 2311 } 2312 return null; 2313 } 2314 2315 void writeSymbol(Dsymbol s, CppMangleVisitor self) 2316 { 2317 auto tale = forSymbol(s); 2318 if (!tale) return; 2319 if (self.substitute(tale)) 2320 return; 2321 this.write(*self.buf, tale); 2322 } 2323 2324 /** 2325 * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer 2326 * 2327 * Params: 2328 * buf = Buffer to write mangling to 2329 * ale = GNU ABI tag array literal expression, semantically analyzed 2330 */ 2331 void write (ref OutBuffer buf, ArrayLiteralExp ale, bool skipKnown = false) 2332 { 2333 void writeElem (StringExp exp) 2334 { 2335 const tag = exp.peekString(); 2336 buf.writestring("B"); 2337 buf.print(tag.length); 2338 buf.writestring(tag); 2339 } 2340 2341 bool match; 2342 foreach (exp; *ale.elements) 2343 { 2344 auto elem = exp.toStringExp(); 2345 auto idx = closestIndex(this.written[], elem, match); 2346 if (!match) 2347 { 2348 writeElem(elem); 2349 this.written.insert(idx, elem); 2350 } 2351 else if (!skipKnown) 2352 writeElem(elem); 2353 } 2354 } 2355 } 2356 2357 /** 2358 * Returns the closest index to to `exp` in `slice` 2359 * 2360 * Performs a binary search on `slice` (assumes `slice` is sorted), 2361 * and returns either `exp`'s index in `slice` if `exact` is `true`, 2362 * or the index at which `exp` can be inserted in `slice` if `exact is `false`. 2363 * Inserting `exp` at the return value will keep the array sorted. 2364 * 2365 * Params: 2366 * slice = The sorted slice to search into 2367 * exp = The string expression to search for 2368 * exact = If `true` on return, `exp` was found in `slice` 2369 * 2370 * Returns: 2371 * Either the index to insert `exp` at (if `exact == false`), 2372 * or the index of `exp` in `slice`. 2373 */ 2374 private size_t closestIndex (const(StringExp)[] slice, StringExp exp, out bool exact) 2375 { 2376 if (!slice.length) return 0; 2377 2378 const StringExp* first = slice.ptr; 2379 while (true) 2380 { 2381 int res = dstrcmp(exp.peekString(), slice[$ / 2].peekString()); 2382 if (res == 0) 2383 { 2384 exact = true; 2385 return (&slice[$/2] - first); 2386 } 2387 2388 if (slice.length == 1) 2389 return (slice.ptr - first) + (res > 0); 2390 slice = slice[(res > 0 ? $ / 2 : 0) .. (res > 0 ? $ : $ / 2)]; 2391 } 2392 } 2393 2394 // 2395 unittest 2396 { 2397 bool match; 2398 auto s1 = new StringExp(Loc.initial, "Amande"); 2399 auto s2 = new StringExp(Loc.initial, "Baguette"); 2400 auto s3 = new StringExp(Loc.initial, "Croissant"); 2401 auto s4 = new StringExp(Loc.initial, "Framboises"); 2402 auto s5 = new StringExp(Loc.initial, "Proscuitto"); 2403 2404 // Found, odd size 2405 assert(closestIndex([s1, s2, s3, s4, s5], s1, match) == 0 && match); 2406 assert(closestIndex([s1, s2, s3, s4, s5], s2, match) == 1 && match); 2407 assert(closestIndex([s1, s2, s3, s4, s5], s3, match) == 2 && match); 2408 assert(closestIndex([s1, s2, s3, s4, s5], s4, match) == 3 && match); 2409 assert(closestIndex([s1, s2, s3, s4, s5], s5, match) == 4 && match); 2410 2411 // Not found, even size 2412 assert(closestIndex([s2, s3, s4, s5], s1, match) == 0 && !match); 2413 assert(closestIndex([s1, s3, s4, s5], s2, match) == 1 && !match); 2414 assert(closestIndex([s1, s2, s4, s5], s3, match) == 2 && !match); 2415 assert(closestIndex([s1, s2, s3, s5], s4, match) == 3 && !match); 2416 assert(closestIndex([s1, s2, s3, s4], s5, match) == 4 && !match); 2417 2418 // Found, even size 2419 assert(closestIndex([s1, s2, s3, s4], s1, match) == 0 && match); 2420 assert(closestIndex([s1, s2, s3, s4], s2, match) == 1 && match); 2421 assert(closestIndex([s1, s2, s3, s4], s3, match) == 2 && match); 2422 assert(closestIndex([s1, s2, s3, s4], s4, match) == 3 && match); 2423 assert(closestIndex([s1, s3, s4, s5], s5, match) == 3 && match); 2424 2425 // Not found, odd size 2426 assert(closestIndex([s2, s4, s5], s1, match) == 0 && !match); 2427 assert(closestIndex([s1, s4, s5], s2, match) == 1 && !match); 2428 assert(closestIndex([s1, s2, s4], s3, match) == 2 && !match); 2429 assert(closestIndex([s1, s3, s5], s4, match) == 2 && !match); 2430 assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match); 2431 } 2432 2433 /** 2434 * Visits the return type of a function and writes leftover ABI tags 2435 */ 2436 extern(C++) private final class LeftoverVisitor : Visitor 2437 { 2438 /// List of tags to write 2439 private Array!StringExp toWrite; 2440 /// List of tags to ignore 2441 private const(Array!StringExp)* ignore; 2442 2443 /// 2444 public this(const(Array!StringExp)* previous) 2445 { 2446 this.ignore = previous; 2447 } 2448 2449 /// Reintroduce base class overloads 2450 public alias visit = Visitor.visit; 2451 2452 /// Least specialized overload of each direct child of `RootObject` 2453 public override void visit(Dsymbol o) 2454 { 2455 auto ale = ABITagContainer.forSymbol(o); 2456 if (!ale) return; 2457 2458 bool match; 2459 foreach (elem; *ale.elements) 2460 { 2461 auto se = elem.toStringExp(); 2462 closestIndex((*this.ignore)[], se, match); 2463 if (match) continue; 2464 auto idx = closestIndex(this.toWrite[], se, match); 2465 if (!match) 2466 this.toWrite.insert(idx, se); 2467 } 2468 } 2469 2470 /// Ditto 2471 public override void visit(Type o) 2472 { 2473 if (auto sym = o.toDsymbol(null)) 2474 sym.accept(this); 2475 } 2476 2477 /// Composite type 2478 public override void visit(TypePointer o) 2479 { 2480 o.next.accept(this); 2481 } 2482 2483 public override void visit(TypeReference o) 2484 { 2485 o.next.accept(this); 2486 } 2487 }