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