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