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