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