1 //===--- RustDemangle.cpp ---------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a demangler for Rust v0 mangled symbols as specified in 10 // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Demangle/RustDemangle.h" 15 #include "llvm/Demangle/Demangle.h" 16 17 #include <algorithm> 18 #include <cassert> 19 #include <cstring> 20 #include <limits> 21 22 using namespace llvm; 23 using namespace rust_demangle; 24 25 char *llvm::rustDemangle(const char *MangledName, char *Buf, size_t *N, 26 int *Status) { 27 if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { 28 if (Status != nullptr) 29 *Status = demangle_invalid_args; 30 return nullptr; 31 } 32 33 // Return early if mangled name doesn't look like a Rust symbol. 34 StringView Mangled(MangledName); 35 if (!Mangled.startsWith("_R")) { 36 if (Status != nullptr) 37 *Status = demangle_invalid_mangled_name; 38 return nullptr; 39 } 40 41 Demangler D; 42 if (!initializeOutputStream(nullptr, nullptr, D.Output, 1024)) { 43 if (Status != nullptr) 44 *Status = demangle_memory_alloc_failure; 45 return nullptr; 46 } 47 48 if (!D.demangle(Mangled)) { 49 if (Status != nullptr) 50 *Status = demangle_invalid_mangled_name; 51 std::free(D.Output.getBuffer()); 52 return nullptr; 53 } 54 55 D.Output += '\0'; 56 char *Demangled = D.Output.getBuffer(); 57 size_t DemangledLen = D.Output.getCurrentPosition(); 58 59 if (Buf != nullptr) { 60 if (DemangledLen <= *N) { 61 std::memcpy(Buf, Demangled, DemangledLen); 62 std::free(Demangled); 63 Demangled = Buf; 64 } else { 65 std::free(Buf); 66 } 67 } 68 69 if (N != nullptr) 70 *N = DemangledLen; 71 72 if (Status != nullptr) 73 *Status = demangle_success; 74 75 return Demangled; 76 } 77 78 Demangler::Demangler(size_t MaxRecursionLevel) 79 : MaxRecursionLevel(MaxRecursionLevel) {} 80 81 static inline bool isDigit(const char C) { return '0' <= C && C <= '9'; } 82 83 static inline bool isHexDigit(const char C) { 84 return ('0' <= C && C <= '9') || ('a' <= C && C <= 'f'); 85 } 86 87 static inline bool isLower(const char C) { return 'a' <= C && C <= 'z'; } 88 89 static inline bool isUpper(const char C) { return 'A' <= C && C <= 'Z'; } 90 91 /// Returns true if C is a valid mangled character: <0-9a-zA-Z_>. 92 static inline bool isValid(const char C) { 93 return isDigit(C) || isLower(C) || isUpper(C) || C == '_'; 94 } 95 96 // Demangles Rust v0 mangled symbol. Returns true when successful, and false 97 // otherwise. The demangled symbol is stored in Output field. It is 98 // responsibility of the caller to free the memory behind the output stream. 99 // 100 // <symbol-name> = "_R" <path> [<instantiating-crate>] 101 bool Demangler::demangle(StringView Mangled) { 102 Position = 0; 103 Error = false; 104 Print = true; 105 RecursionLevel = 0; 106 BoundLifetimes = 0; 107 108 if (!Mangled.consumeFront("_R")) { 109 Error = true; 110 return false; 111 } 112 Input = Mangled; 113 114 demanglePath(rust_demangle::InType::No); 115 116 if (Position != Input.size()) { 117 SwapAndRestore<bool> SavePrint(Print, false); 118 demanglePath(InType::No); 119 } 120 121 if (Position != Input.size()) 122 Error = true; 123 124 return !Error; 125 } 126 127 // Demangles a path. InType indicates whether a path is inside a type. When 128 // LeaveOpen is true, a closing `>` after generic arguments is omitted from the 129 // output. Return value indicates whether generics arguments have been left 130 // open. 131 // 132 // <path> = "C" <identifier> // crate root 133 // | "M" <impl-path> <type> // <T> (inherent impl) 134 // | "X" <impl-path> <type> <path> // <T as Trait> (trait impl) 135 // | "Y" <type> <path> // <T as Trait> (trait definition) 136 // | "N" <ns> <path> <identifier> // ...::ident (nested path) 137 // | "I" <path> {<generic-arg>} "E" // ...<T, U> (generic args) 138 // | <backref> 139 // <identifier> = [<disambiguator>] <undisambiguated-identifier> 140 // <ns> = "C" // closure 141 // | "S" // shim 142 // | <A-Z> // other special namespaces 143 // | <a-z> // internal namespaces 144 bool Demangler::demanglePath(InType InType, LeaveOpen LeaveOpen) { 145 if (Error || RecursionLevel >= MaxRecursionLevel) { 146 Error = true; 147 return false; 148 } 149 SwapAndRestore<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1); 150 151 switch (consume()) { 152 case 'C': { 153 parseOptionalBase62Number('s'); 154 Identifier Ident = parseIdentifier(); 155 print(Ident.Name); 156 break; 157 } 158 case 'M': { 159 demangleImplPath(InType); 160 print("<"); 161 demangleType(); 162 print(">"); 163 break; 164 } 165 case 'X': { 166 demangleImplPath(InType); 167 print("<"); 168 demangleType(); 169 print(" as "); 170 demanglePath(rust_demangle::InType::Yes); 171 print(">"); 172 break; 173 } 174 case 'Y': { 175 print("<"); 176 demangleType(); 177 print(" as "); 178 demanglePath(rust_demangle::InType::Yes); 179 print(">"); 180 break; 181 } 182 case 'N': { 183 char NS = consume(); 184 if (!isLower(NS) && !isUpper(NS)) { 185 Error = true; 186 break; 187 } 188 demanglePath(InType); 189 190 uint64_t Disambiguator = parseOptionalBase62Number('s'); 191 Identifier Ident = parseIdentifier(); 192 193 if (isUpper(NS)) { 194 // Special namespaces 195 print("::{"); 196 if (NS == 'C') 197 print("closure"); 198 else if (NS == 'S') 199 print("shim"); 200 else 201 print(NS); 202 if (!Ident.empty()) { 203 print(":"); 204 print(Ident.Name); 205 } 206 print('#'); 207 printDecimalNumber(Disambiguator); 208 print('}'); 209 } else { 210 // Implementation internal namespaces. 211 if (!Ident.empty()) { 212 print("::"); 213 print(Ident.Name); 214 } 215 } 216 break; 217 } 218 case 'I': { 219 demanglePath(InType); 220 // Omit "::" when in a type, where it is optional. 221 if (InType == rust_demangle::InType::No) 222 print("::"); 223 print("<"); 224 for (size_t I = 0; !Error && !consumeIf('E'); ++I) { 225 if (I > 0) 226 print(", "); 227 demangleGenericArg(); 228 } 229 if (LeaveOpen == rust_demangle::LeaveOpen::Yes) 230 return true; 231 else 232 print(">"); 233 break; 234 } 235 default: 236 // FIXME parse remaining productions. 237 Error = true; 238 break; 239 } 240 241 return false; 242 } 243 244 // <impl-path> = [<disambiguator>] <path> 245 // <disambiguator> = "s" <base-62-number> 246 void Demangler::demangleImplPath(InType InType) { 247 SwapAndRestore<bool> SavePrint(Print, false); 248 parseOptionalBase62Number('s'); 249 demanglePath(InType); 250 } 251 252 // <generic-arg> = <lifetime> 253 // | <type> 254 // | "K" <const> 255 // <lifetime> = "L" <base-62-number> 256 void Demangler::demangleGenericArg() { 257 if (consumeIf('L')) 258 printLifetime(parseBase62Number()); 259 else if (consumeIf('K')) 260 demangleConst(); 261 else 262 demangleType(); 263 } 264 265 // <basic-type> = "a" // i8 266 // | "b" // bool 267 // | "c" // char 268 // | "d" // f64 269 // | "e" // str 270 // | "f" // f32 271 // | "h" // u8 272 // | "i" // isize 273 // | "j" // usize 274 // | "l" // i32 275 // | "m" // u32 276 // | "n" // i128 277 // | "o" // u128 278 // | "s" // i16 279 // | "t" // u16 280 // | "u" // () 281 // | "v" // ... 282 // | "x" // i64 283 // | "y" // u64 284 // | "z" // ! 285 // | "p" // placeholder (e.g. for generic params), shown as _ 286 static bool parseBasicType(char C, BasicType &Type) { 287 switch (C) { 288 case 'a': 289 Type = BasicType::I8; 290 return true; 291 case 'b': 292 Type = BasicType::Bool; 293 return true; 294 case 'c': 295 Type = BasicType::Char; 296 return true; 297 case 'd': 298 Type = BasicType::F64; 299 return true; 300 case 'e': 301 Type = BasicType::Str; 302 return true; 303 case 'f': 304 Type = BasicType::F32; 305 return true; 306 case 'h': 307 Type = BasicType::U8; 308 return true; 309 case 'i': 310 Type = BasicType::ISize; 311 return true; 312 case 'j': 313 Type = BasicType::USize; 314 return true; 315 case 'l': 316 Type = BasicType::I32; 317 return true; 318 case 'm': 319 Type = BasicType::U32; 320 return true; 321 case 'n': 322 Type = BasicType::I128; 323 return true; 324 case 'o': 325 Type = BasicType::U128; 326 return true; 327 case 'p': 328 Type = BasicType::Placeholder; 329 return true; 330 case 's': 331 Type = BasicType::I16; 332 return true; 333 case 't': 334 Type = BasicType::U16; 335 return true; 336 case 'u': 337 Type = BasicType::Unit; 338 return true; 339 case 'v': 340 Type = BasicType::Variadic; 341 return true; 342 case 'x': 343 Type = BasicType::I64; 344 return true; 345 case 'y': 346 Type = BasicType::U64; 347 return true; 348 case 'z': 349 Type = BasicType::Never; 350 return true; 351 default: 352 return false; 353 } 354 } 355 356 void Demangler::printBasicType(BasicType Type) { 357 switch (Type) { 358 case BasicType::Bool: 359 print("bool"); 360 break; 361 case BasicType::Char: 362 print("char"); 363 break; 364 case BasicType::I8: 365 print("i8"); 366 break; 367 case BasicType::I16: 368 print("i16"); 369 break; 370 case BasicType::I32: 371 print("i32"); 372 break; 373 case BasicType::I64: 374 print("i64"); 375 break; 376 case BasicType::I128: 377 print("i128"); 378 break; 379 case BasicType::ISize: 380 print("isize"); 381 break; 382 case BasicType::U8: 383 print("u8"); 384 break; 385 case BasicType::U16: 386 print("u16"); 387 break; 388 case BasicType::U32: 389 print("u32"); 390 break; 391 case BasicType::U64: 392 print("u64"); 393 break; 394 case BasicType::U128: 395 print("u128"); 396 break; 397 case BasicType::USize: 398 print("usize"); 399 break; 400 case BasicType::F32: 401 print("f32"); 402 break; 403 case BasicType::F64: 404 print("f64"); 405 break; 406 case BasicType::Str: 407 print("str"); 408 break; 409 case BasicType::Placeholder: 410 print("_"); 411 break; 412 case BasicType::Unit: 413 print("()"); 414 break; 415 case BasicType::Variadic: 416 print("..."); 417 break; 418 case BasicType::Never: 419 print("!"); 420 break; 421 } 422 } 423 424 // <type> = | <basic-type> 425 // | <path> // named type 426 // | "A" <type> <const> // [T; N] 427 // | "S" <type> // [T] 428 // | "T" {<type>} "E" // (T1, T2, T3, ...) 429 // | "R" [<lifetime>] <type> // &T 430 // | "Q" [<lifetime>] <type> // &mut T 431 // | "P" <type> // *const T 432 // | "O" <type> // *mut T 433 // | "F" <fn-sig> // fn(...) -> ... 434 // | "D" <dyn-bounds> <lifetime> // dyn Trait<Assoc = X> + Send + 'a 435 // | <backref> // backref 436 void Demangler::demangleType() { 437 size_t Start = Position; 438 439 char C = consume(); 440 BasicType Type; 441 if (parseBasicType(C, Type)) 442 return printBasicType(Type); 443 444 switch (C) { 445 case 'A': 446 print("["); 447 demangleType(); 448 print("; "); 449 demangleConst(); 450 print("]"); 451 break; 452 case 'S': 453 print("["); 454 demangleType(); 455 print("]"); 456 break; 457 case 'T': { 458 print("("); 459 size_t I = 0; 460 for (; !Error && !consumeIf('E'); ++I) { 461 if (I > 0) 462 print(", "); 463 demangleType(); 464 } 465 if (I == 1) 466 print(","); 467 print(")"); 468 break; 469 } 470 case 'R': 471 case 'Q': 472 print('&'); 473 if (consumeIf('L')) { 474 if (auto Lifetime = parseBase62Number()) { 475 printLifetime(Lifetime); 476 print(' '); 477 } 478 } 479 if (C == 'Q') 480 print("mut "); 481 demangleType(); 482 break; 483 case 'P': 484 print("*const "); 485 demangleType(); 486 break; 487 case 'O': 488 print("*mut "); 489 demangleType(); 490 break; 491 case 'F': 492 demangleFnSig(); 493 break; 494 case 'D': 495 demangleDynBounds(); 496 if (consumeIf('L')) { 497 if (auto Lifetime = parseBase62Number()) { 498 print(" + "); 499 printLifetime(Lifetime); 500 } 501 } else { 502 Error = true; 503 } 504 break; 505 default: 506 Position = Start; 507 demanglePath(rust_demangle::InType::Yes); 508 break; 509 } 510 } 511 512 // <fn-sig> := [<binder>] ["U"] ["K" <abi>] {<type>} "E" <type> 513 // <abi> = "C" 514 // | <undisambiguated-identifier> 515 void Demangler::demangleFnSig() { 516 SwapAndRestore<size_t> SaveBoundLifetimes(BoundLifetimes, BoundLifetimes); 517 demangleOptionalBinder(); 518 519 if (consumeIf('U')) 520 print("unsafe "); 521 522 if (consumeIf('K')) { 523 print("extern \""); 524 if (consumeIf('C')) { 525 print("C"); 526 } else { 527 Identifier Ident = parseIdentifier(); 528 for (char C : Ident.Name) { 529 // When mangling ABI string, the "-" is replaced with "_". 530 if (C == '_') 531 C = '-'; 532 print(C); 533 } 534 } 535 print("\" "); 536 } 537 538 print("fn("); 539 for (size_t I = 0; !Error && !consumeIf('E'); ++I) { 540 if (I > 0) 541 print(", "); 542 demangleType(); 543 } 544 print(")"); 545 546 if (consumeIf('u')) { 547 // Skip the unit type from the output. 548 } else { 549 print(" -> "); 550 demangleType(); 551 } 552 } 553 554 // <dyn-bounds> = [<binder>] {<dyn-trait>} "E" 555 void Demangler::demangleDynBounds() { 556 SwapAndRestore<size_t> SaveBoundLifetimes(BoundLifetimes, BoundLifetimes); 557 print("dyn "); 558 demangleOptionalBinder(); 559 for (size_t I = 0; !Error && !consumeIf('E'); ++I) { 560 if (I > 0) 561 print(" + "); 562 demangleDynTrait(); 563 } 564 } 565 566 // <dyn-trait> = <path> {<dyn-trait-assoc-binding>} 567 // <dyn-trait-assoc-binding> = "p" <undisambiguated-identifier> <type> 568 void Demangler::demangleDynTrait() { 569 bool IsOpen = demanglePath(InType::Yes, LeaveOpen::Yes); 570 while (!Error && consumeIf('p')) { 571 if (!IsOpen) { 572 IsOpen = true; 573 print('<'); 574 } else { 575 print(", "); 576 } 577 print(parseIdentifier().Name); 578 print(" = "); 579 demangleType(); 580 } 581 if (IsOpen) 582 print(">"); 583 } 584 585 // Demangles optional binder and updates the number of bound lifetimes. 586 // 587 // <binder> = "G" <base-62-number> 588 void Demangler::demangleOptionalBinder() { 589 uint64_t Binder = parseOptionalBase62Number('G'); 590 if (Error || Binder == 0) 591 return; 592 593 // In valid inputs each bound lifetime is referenced later. Referencing a 594 // lifetime requires at least one byte of input. Reject inputs that are too 595 // short to reference all bound lifetimes. Otherwise demangling of invalid 596 // binders could generate excessive amounts of output. 597 if (Binder >= Input.size() - BoundLifetimes) { 598 Error = true; 599 return; 600 } 601 602 print("for<"); 603 for (size_t I = 0; I != Binder; ++I) { 604 BoundLifetimes += 1; 605 if (I > 0) 606 print(", "); 607 printLifetime(1); 608 } 609 print("> "); 610 } 611 612 // <const> = <basic-type> <const-data> 613 // | "p" // placeholder 614 // | <backref> 615 void Demangler::demangleConst() { 616 BasicType Type; 617 if (parseBasicType(consume(), Type)) { 618 switch (Type) { 619 case BasicType::I8: 620 case BasicType::I16: 621 case BasicType::I32: 622 case BasicType::I64: 623 case BasicType::I128: 624 case BasicType::ISize: 625 case BasicType::U8: 626 case BasicType::U16: 627 case BasicType::U32: 628 case BasicType::U64: 629 case BasicType::U128: 630 case BasicType::USize: 631 demangleConstInt(); 632 break; 633 case BasicType::Bool: 634 demangleConstBool(); 635 break; 636 case BasicType::Char: 637 demangleConstChar(); 638 break; 639 case BasicType::Placeholder: 640 print('_'); 641 break; 642 default: 643 // FIXME demangle backreferences. 644 Error = true; 645 break; 646 } 647 } else { 648 Error = true; 649 } 650 } 651 652 // <const-data> = ["n"] <hex-number> 653 void Demangler::demangleConstInt() { 654 if (consumeIf('n')) 655 print('-'); 656 657 StringView HexDigits; 658 uint64_t Value = parseHexNumber(HexDigits); 659 if (HexDigits.size() <= 16) { 660 printDecimalNumber(Value); 661 } else { 662 print("0x"); 663 print(HexDigits); 664 } 665 } 666 667 // <const-data> = "0_" // false 668 // | "1_" // true 669 void Demangler::demangleConstBool() { 670 StringView HexDigits; 671 parseHexNumber(HexDigits); 672 if (HexDigits == "0") 673 print("false"); 674 else if (HexDigits == "1") 675 print("true"); 676 else 677 Error = true; 678 } 679 680 /// Returns true if CodePoint represents a printable ASCII character. 681 static bool isAsciiPrintable(uint64_t CodePoint) { 682 return 0x20 <= CodePoint && CodePoint <= 0x7e; 683 } 684 685 // <const-data> = <hex-number> 686 void Demangler::demangleConstChar() { 687 StringView HexDigits; 688 uint64_t CodePoint = parseHexNumber(HexDigits); 689 if (Error || HexDigits.size() > 6) { 690 Error = true; 691 return; 692 } 693 694 print("'"); 695 switch (CodePoint) { 696 case '\t': 697 print(R"(\t)"); 698 break; 699 case '\r': 700 print(R"(\r)"); 701 break; 702 case '\n': 703 print(R"(\n)"); 704 break; 705 case '\\': 706 print(R"(\\)"); 707 break; 708 case '"': 709 print(R"(")"); 710 break; 711 case '\'': 712 print(R"(\')"); 713 break; 714 default: 715 if (isAsciiPrintable(CodePoint)) { 716 char C = CodePoint; 717 print(C); 718 } else { 719 print(R"(\u{)"); 720 print(HexDigits); 721 print('}'); 722 } 723 break; 724 } 725 print('\''); 726 } 727 728 // <undisambiguated-identifier> = ["u"] <decimal-number> ["_"] <bytes> 729 Identifier Demangler::parseIdentifier() { 730 bool Punycode = consumeIf('u'); 731 uint64_t Bytes = parseDecimalNumber(); 732 733 // Underscore resolves the ambiguity when identifier starts with a decimal 734 // digit or another underscore. 735 consumeIf('_'); 736 737 if (Error || Bytes > Input.size() - Position) { 738 Error = true; 739 return {}; 740 } 741 StringView S = Input.substr(Position, Bytes); 742 Position += Bytes; 743 744 if (!std::all_of(S.begin(), S.end(), isValid)) { 745 Error = true; 746 return {}; 747 } 748 749 return {S, Punycode}; 750 } 751 752 // Parses optional base 62 number. The presence of a number is determined using 753 // Tag. Returns 0 when tag is absent and parsed value + 1 otherwise 754 // 755 // This function is indended for parsing disambiguators and binders which when 756 // not present have their value interpreted as 0, and otherwise as decoded 757 // value + 1. For example for binders, value for "G_" is 1, for "G0_" value is 758 // 2. When "G" is absent value is 0. 759 uint64_t Demangler::parseOptionalBase62Number(char Tag) { 760 if (!consumeIf(Tag)) 761 return 0; 762 763 uint64_t N = parseBase62Number(); 764 if (Error || !addAssign(N, 1)) 765 return 0; 766 767 return N; 768 } 769 770 // Parses base 62 number with <0-9a-zA-Z> as digits. Number is terminated by 771 // "_". All values are offset by 1, so that "_" encodes 0, "0_" encodes 1, 772 // "1_" encodes 2, etc. 773 // 774 // <base-62-number> = {<0-9a-zA-Z>} "_" 775 uint64_t Demangler::parseBase62Number() { 776 if (consumeIf('_')) 777 return 0; 778 779 uint64_t Value = 0; 780 781 while (true) { 782 uint64_t Digit; 783 char C = consume(); 784 785 if (C == '_') { 786 break; 787 } else if (isDigit(C)) { 788 Digit = C - '0'; 789 } else if (isLower(C)) { 790 Digit = 10 + (C - 'a'); 791 } else if (isUpper(C)) { 792 Digit = 10 + 26 + (C - 'A'); 793 } else { 794 Error = true; 795 return 0; 796 } 797 798 if (!mulAssign(Value, 62)) 799 return 0; 800 801 if (!addAssign(Value, Digit)) 802 return 0; 803 } 804 805 if (!addAssign(Value, 1)) 806 return 0; 807 808 return Value; 809 } 810 811 // Parses a decimal number that had been encoded without any leading zeros. 812 // 813 // <decimal-number> = "0" 814 // | <1-9> {<0-9>} 815 uint64_t Demangler::parseDecimalNumber() { 816 char C = look(); 817 if (!isDigit(C)) { 818 Error = true; 819 return 0; 820 } 821 822 if (C == '0') { 823 consume(); 824 return 0; 825 } 826 827 uint64_t Value = 0; 828 829 while (isDigit(look())) { 830 if (!mulAssign(Value, 10)) { 831 Error = true; 832 return 0; 833 } 834 835 uint64_t D = consume() - '0'; 836 if (!addAssign(Value, D)) 837 return 0; 838 } 839 840 return Value; 841 } 842 843 // Parses a hexadecimal number with <0-9a-f> as a digits. Returns the parsed 844 // value and stores hex digits in HexDigits. The return value is unspecified if 845 // HexDigits.size() > 16. 846 // 847 // <hex-number> = "0_" 848 // | <1-9a-f> {<0-9a-f>} "_" 849 uint64_t Demangler::parseHexNumber(StringView &HexDigits) { 850 size_t Start = Position; 851 uint64_t Value = 0; 852 853 if (!isHexDigit(look())) 854 Error = true; 855 856 if (consumeIf('0')) { 857 if (!consumeIf('_')) 858 Error = true; 859 } else { 860 while (!Error && !consumeIf('_')) { 861 char C = consume(); 862 Value *= 16; 863 if (isDigit(C)) 864 Value += C - '0'; 865 else if ('a' <= C && C <= 'f') 866 Value += 10 + (C - 'a'); 867 else 868 Error = true; 869 } 870 } 871 872 if (Error) { 873 HexDigits = StringView(); 874 return 0; 875 } 876 877 size_t End = Position - 1; 878 assert(Start < End); 879 HexDigits = Input.substr(Start, End - Start); 880 return Value; 881 } 882 883 // Prints a lifetime. An index 0 always represents an erased lifetime. Indices 884 // starting from 1, are De Bruijn indices, referring to higher-ranked lifetimes 885 // bound by one of the enclosing binders. 886 void Demangler::printLifetime(uint64_t Index) { 887 if (Index == 0) { 888 print("'_"); 889 return; 890 } 891 892 if (Index - 1 >= BoundLifetimes) { 893 Error = true; 894 return; 895 } 896 897 uint64_t Depth = BoundLifetimes - Index; 898 print('\''); 899 if (Depth < 26) { 900 char C = 'a' + Depth; 901 print(C); 902 } else { 903 print('z'); 904 printDecimalNumber(Depth - 26 + 1); 905 } 906 } 907