diff options
Diffstat (limited to 'libiberty')
-rw-r--r-- | libiberty/ChangeLog | 13 | ||||
-rw-r--r-- | libiberty/Makefile.in | 13 | ||||
-rw-r--r-- | libiberty/cplus-dem.c | 47 | ||||
-rw-r--r-- | libiberty/rust-demangle.c | 348 | ||||
-rw-r--r-- | libiberty/testsuite/Makefile.in | 7 | ||||
-rw-r--r-- | libiberty/testsuite/rust-demangle-expected | 161 |
6 files changed, 585 insertions, 4 deletions
diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index 1082431..7ae91ad 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,16 @@ +2016-11-03 David Tolnay <dtolnay@gmail.com> + Mark Wielaard <mark@klomp.org> + + * Makefile.in (CFILES): Add rust-demangle.c. + (REQUIRED_OFILES): Add rust-demangle.o. + * cplus-dem.c (libiberty_demanglers): Add rust_demangling case. + (cplus_demangle): Handle RUST_DEMANGLING. + (rust_demangle): New function. + * rust-demangle.c: New file. + * testsuite/Makefile.in (really-check): Add check-rust-demangle. + (check-rust-demangle): New rule. + * testsuite/rust-demangle-expected: New file. + 2016-11-15 Mark Wielaard <mark@klomp.org> * cp-demangle.c (d_expression_1): Make sure third expression diff --git a/libiberty/Makefile.in b/libiberty/Makefile.in index c7a4568..0ff9e45 100644 --- a/libiberty/Makefile.in +++ b/libiberty/Makefile.in @@ -146,6 +146,7 @@ CFILES = alloca.c argv.c asprintf.c atexit.c \ pex-unix.c pex-win32.c \ physmem.c putenv.c \ random.c regex.c rename.c rindex.c \ + rust-demangle.c \ safe-ctype.c setenv.c setproctitle.c sha1.c sigsetmask.c \ simple-object.c simple-object-coff.c simple-object-elf.c \ simple-object-mach-o.c simple-object-xcoff.c \ @@ -183,6 +184,7 @@ REQUIRED_OFILES = \ ./partition.$(objext) ./pexecute.$(objext) ./physmem.$(objext) \ ./pex-common.$(objext) ./pex-one.$(objext) \ ./@pexecute@.$(objext) ./vprintf-support.$(objext) \ + ./rust-demangle.$(objext) \ ./safe-ctype.$(objext) \ ./simple-object.$(objext) ./simple-object-coff.$(objext) \ ./simple-object-elf.$(objext) ./simple-object-mach-o.$(objext) \ @@ -1188,6 +1190,17 @@ $(CONFIGURED_OFILES): stamp-picdir stamp-noasandir else true; fi $(COMPILE.c) $(srcdir)/rindex.c $(OUTPUT_OPTION) +./rust-demangle.$(objext): $(srcdir)/rust-demangle.c config.h \ + $(INCDIR)/ansidecl.h $(INCDIR)/demangle.h $(INCDIR)/libiberty.h \ + $(INCDIR)/safe-ctype.h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/rust-demangle.c -o pic/$@; \ + else true; fi + if [ x"$(NOASANFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(NOASANFLAG) $(srcdir)/rust-demangle.c -o noasan/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/rust-demangle.c $(OUTPUT_OPTION) + ./safe-ctype.$(objext): $(srcdir)/safe-ctype.c $(INCDIR)/ansidecl.h \ $(INCDIR)/safe-ctype.h if [ x"$(PICFLAG)" != x ]; then \ diff --git a/libiberty/cplus-dem.c b/libiberty/cplus-dem.c index 3125a45..0386da5 100644 --- a/libiberty/cplus-dem.c +++ b/libiberty/cplus-dem.c @@ -323,6 +323,12 @@ const struct demangler_engine libiberty_demanglers[] = } , { + RUST_DEMANGLING_STYLE_STRING, + rust_demangling, + "Rust style demangling" + } + , + { NULL, unknown_demangling, NULL } }; @@ -874,10 +880,26 @@ cplus_demangle (const char *mangled, int options) work->options |= (int) current_demangling_style & DMGL_STYLE_MASK; /* The V3 ABI demangling is implemented elsewhere. */ - if (GNU_V3_DEMANGLING || AUTO_DEMANGLING) + if (GNU_V3_DEMANGLING || RUST_DEMANGLING || AUTO_DEMANGLING) { ret = cplus_demangle_v3 (mangled, work->options); - if (ret || GNU_V3_DEMANGLING) + if (GNU_V3_DEMANGLING) + return ret; + + if (ret) + { + /* Rust symbols are GNU_V3 mangled plus some extra subtitutions. + The subtitutions are always smaller, so do in place changes. */ + if (rust_is_mangled (ret)) + rust_demangle_sym (ret); + else if (RUST_DEMANGLING) + { + free (ret); + ret = NULL; + } + } + + if (ret || RUST_DEMANGLING) return ret; } @@ -903,6 +925,27 @@ cplus_demangle (const char *mangled, int options) return (ret); } +char * +rust_demangle (const char *mangled, int options) +{ + /* Rust symbols are GNU_V3 mangled plus some extra subtitutions. */ + char *ret = cplus_demangle_v3 (mangled, options); + + /* The Rust subtitutions are always smaller, so do in place changes. */ + if (ret != NULL) + { + if (rust_is_mangled (ret)) + rust_demangle_sym (ret); + else + { + free (ret); + ret = NULL; + } + } + + return ret; +} + /* Demangle ada names. The encoding is documented in gcc/ada/exp_dbug.ads. */ char * diff --git a/libiberty/rust-demangle.c b/libiberty/rust-demangle.c new file mode 100644 index 0000000..326881e --- /dev/null +++ b/libiberty/rust-demangle.c @@ -0,0 +1,348 @@ +/* Demangler for the Rust programming language + Copyright 2016 Free Software Foundation, Inc. + Written by David Tolnay (dtolnay@gmail.com). + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +In addition to the permissions in the GNU Library General Public +License, the Free Software Foundation gives you unlimited permission +to link the compiled version of this file into combinations with other +programs, and to distribute those combinations without any restriction +coming from the use of this file. (The Library Public License +restrictions do apply in other respects; for example, they cover +modification of the file, and distribution when not linked into a +combined executable.) + +Libiberty is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. +If not, see <http://www.gnu.org/licenses/>. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "safe-ctype.h" + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#else +extern size_t strlen(const char *s); +extern int strncmp(const char *s1, const char *s2, size_t n); +extern void *memset(void *s, int c, size_t n); +#endif + +#include <demangle.h> +#include "libiberty.h" + + +/* Mangled Rust symbols look like this: + _$LT$std..sys..fd..FileDesc$u20$as$u20$core..ops..Drop$GT$::drop::hc68340e1baa4987a + + The original symbol is: + <std::sys::fd::FileDesc as core::ops::Drop>::drop + + The last component of the path is a 64-bit hash in lowercase hex, + prefixed with "h". Rust does not have a global namespace between + crates, an illusion which Rust maintains by using the hash to + distinguish things that would otherwise have the same symbol. + + Any path component not starting with a XID_Start character is + prefixed with "_". + + The following escape sequences are used: + + "," => $C$ + "@" => $SP$ + "*" => $BP$ + "&" => $RF$ + "<" => $LT$ + ">" => $GT$ + "(" => $LP$ + ")" => $RP$ + " " => $u20$ + "\"" => $u22$ + "'" => $u27$ + "+" => $u2b$ + ";" => $u3b$ + "[" => $u5b$ + "]" => $u5d$ + "{" => $u7b$ + "}" => $u7d$ + "~" => $u7e$ + + A double ".." means "::" and a single "." means "-". + + The only characters allowed in the mangled symbol are a-zA-Z0-9 and _.:$ */ + +static const char *hash_prefix = "::h"; +static const size_t hash_prefix_len = 3; +static const size_t hash_len = 16; + +static int is_prefixed_hash (const char *start); +static int looks_like_rust (const char *sym, size_t len); +static int unescape (const char **in, char **out, const char *seq, char value); + +/* INPUT: sym: symbol that has been through C++ (gnu v3) demangling + + This function looks for the following indicators: + + 1. The hash must consist of "h" followed by 16 lowercase hex digits. + + 2. As a sanity check, the hash must use between 5 and 15 of the 16 + possible hex digits. This is true of 99.9998% of hashes so once + in your life you may see a false negative. The point is to + notice path components that could be Rust hashes but are + probably not, like "haaaaaaaaaaaaaaaa". In this case a false + positive (non-Rust symbol has an important path component + removed because it looks like a Rust hash) is worse than a false + negative (the rare Rust symbol is not demangled) so this sets + the balance in favor of false negatives. + + 3. There must be no characters other than a-zA-Z0-9 and _.:$ + + 4. There must be no unrecognized $-sign sequences. + + 5. There must be no sequence of three or more dots in a row ("..."). */ + +int +rust_is_mangled (const char *sym) +{ + size_t len, len_without_hash; + + if (!sym) + return 0; + + len = strlen (sym); + if (len <= hash_prefix_len + hash_len) + /* Not long enough to contain "::h" + hash + something else */ + return 0; + + len_without_hash = len - (hash_prefix_len + hash_len); + if (!is_prefixed_hash (sym + len_without_hash)) + return 0; + + return looks_like_rust (sym, len_without_hash); +} + +/* A hash is the prefix "::h" followed by 16 lowercase hex digits. The + hex digits must comprise between 5 and 15 (inclusive) distinct + digits. */ + +static int +is_prefixed_hash (const char *str) +{ + const char *end; + char seen[16]; + size_t i; + int count; + + if (strncmp (str, hash_prefix, hash_prefix_len)) + return 0; + str += hash_prefix_len; + + memset (seen, 0, sizeof(seen)); + for (end = str + hash_len; str < end; str++) + if (*str >= '0' && *str <= '9') + seen[*str - '0'] = 1; + else if (*str >= 'a' && *str <= 'f') + seen[*str - 'a' + 10] = 1; + else + return 0; + + /* Count how many distinct digits seen */ + count = 0; + for (i = 0; i < 16; i++) + if (seen[i]) + count++; + + return count >= 5 && count <= 15; +} + +static int +looks_like_rust (const char *str, size_t len) +{ + const char *end = str + len; + + while (str < end) + switch (*str) + { + case '$': + if (!strncmp (str, "$C$", 3)) + str += 3; + else if (!strncmp (str, "$SP$", 4) + || !strncmp (str, "$BP$", 4) + || !strncmp (str, "$RF$", 4) + || !strncmp (str, "$LT$", 4) + || !strncmp (str, "$GT$", 4) + || !strncmp (str, "$LP$", 4) + || !strncmp (str, "$RP$", 4)) + str += 4; + else if (!strncmp (str, "$u20$", 5) + || !strncmp (str, "$u22$", 5) + || !strncmp (str, "$u27$", 5) + || !strncmp (str, "$u2b$", 5) + || !strncmp (str, "$u3b$", 5) + || !strncmp (str, "$u5b$", 5) + || !strncmp (str, "$u5d$", 5) + || !strncmp (str, "$u7b$", 5) + || !strncmp (str, "$u7d$", 5) + || !strncmp (str, "$u7e$", 5)) + str += 5; + else + return 0; + break; + case '.': + /* Do not allow three or more consecutive dots */ + if (!strncmp (str, "...", 3)) + return 0; + /* Fall through */ + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': + case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + case '_': + case ':': + str++; + break; + default: + return 0; + } + + return 1; +} + +/* + INPUT: sym: symbol for which rust_is_mangled(sym) returned 1. + + The input is demangled in-place because the mangled name is always + longer than the demangled one. */ + +void +rust_demangle_sym (char *sym) +{ + const char *in; + char *out; + const char *end; + + if (!sym) + return; + + in = sym; + out = sym; + end = sym + strlen (sym) - (hash_prefix_len + hash_len); + + while (in < end) + switch (*in) + { + case '$': + if (!(unescape (&in, &out, "$C$", ',') + || unescape (&in, &out, "$SP$", '@') + || unescape (&in, &out, "$BP$", '*') + || unescape (&in, &out, "$RF$", '&') + || unescape (&in, &out, "$LT$", '<') + || unescape (&in, &out, "$GT$", '>') + || unescape (&in, &out, "$LP$", '(') + || unescape (&in, &out, "$RP$", ')') + || unescape (&in, &out, "$u20$", ' ') + || unescape (&in, &out, "$u22$", '\"') + || unescape (&in, &out, "$u27$", '\'') + || unescape (&in, &out, "$u2b$", '+') + || unescape (&in, &out, "$u3b$", ';') + || unescape (&in, &out, "$u5b$", '[') + || unescape (&in, &out, "$u5d$", ']') + || unescape (&in, &out, "$u7b$", '{') + || unescape (&in, &out, "$u7d$", '}') + || unescape (&in, &out, "$u7e$", '~'))) { + /* unexpected escape sequence, not looks_like_rust. */ + goto fail; + } + break; + case '_': + /* If this is the start of a path component and the next + character is an escape sequence, ignore the underscore. The + mangler inserts an underscore to make sure the path + component begins with a XID_Start character. */ + if ((in == sym || in[-1] == ':') && in[1] == '$') + in++; + else + *out++ = *in++; + break; + case '.': + if (in[1] == '.') + { + /* ".." becomes "::" */ + *out++ = ':'; + *out++ = ':'; + in += 2; + } + else + { + /* "." becomes "-" */ + *out++ = '-'; + in++; + } + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': + case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + case ':': + *out++ = *in++; + break; + default: + /* unexpected character in symbol, not looks_like_rust. */ + goto fail; + } + goto done; + +fail: + *out++ = '?'; /* This is pretty lame, but it's hard to do better. */ +done: + *out = '\0'; +} + +static int +unescape (const char **in, char **out, const char *seq, char value) +{ + size_t len = strlen (seq); + + if (strncmp (*in, seq, len)) + return 0; + + **out = value; + + *in += len; + *out += 1; + + return 1; +} diff --git a/libiberty/testsuite/Makefile.in b/libiberty/testsuite/Makefile.in index 8f5f7b5..da0b2f4 100644 --- a/libiberty/testsuite/Makefile.in +++ b/libiberty/testsuite/Makefile.in @@ -45,8 +45,8 @@ all: # CHECK is set to "really_check" or the empty string by configure. check: @CHECK@ -really-check: check-cplus-dem check-d-demangle check-pexecute check-expandargv \ - check-strtol +really-check: check-cplus-dem check-d-demangle check-rust-demangle \ + check-pexecute check-expandargv check-strtol # Run some tests of the demangler. check-cplus-dem: test-demangle $(srcdir)/demangle-expected @@ -55,6 +55,9 @@ check-cplus-dem: test-demangle $(srcdir)/demangle-expected check-d-demangle: test-demangle $(srcdir)/d-demangle-expected ./test-demangle < $(srcdir)/d-demangle-expected +check-rust-demangle: test-demangle $(srcdir)/rust-demangle-expected + ./test-demangle < $(srcdir)/rust-demangle-expected + # Check the pexecute code. check-pexecute: test-pexecute ./test-pexecute diff --git a/libiberty/testsuite/rust-demangle-expected b/libiberty/testsuite/rust-demangle-expected new file mode 100644 index 0000000..0b4288f --- /dev/null +++ b/libiberty/testsuite/rust-demangle-expected @@ -0,0 +1,161 @@ +# This file holds test cases for the Rust demangler. +# Each test case looks like this: +# options +# input to be demangled +# expected output +# +# See demangle-expected for documentation of supported options. +# +# A line starting with `#' is ignored. +# However, blank lines in this file are NOT ignored. +# +############ +# +# Coverage Tests +# +# +# Demangles as rust symbol. +--format=rust +_ZN4main4main17he714a2e23ed7db23E +main::main +# Also demangles as c++ gnu v3 mangled symbol. But with extra Rust hash. +--format=gnu-v3 +_ZN4main4main17he714a2e23ed7db23E +main::main::he714a2e23ed7db23 +# But auto should demangle fully gnu-v3 -> rust -> demangled, not partially. +--format=auto +_ZN4main4main17he714a2e23ed7db23E +main::main +# Hash is exactly 16 hex chars. Not more. +--format=auto +_ZN4main4main18h1e714a2e23ed7db23E +main::main::h1e714a2e23ed7db23 +# Not less. +--format=auto +_ZN4main4main16h714a2e23ed7db23E +main::main::h714a2e23ed7db23 +# And not non-hex. +--format=auto +_ZN4main4main17he714a2e23ed7db2gE +main::main::he714a2e23ed7db2g +# $XX$ substitutions should not contain just numbers. +--format=auto +_ZN4main4$99$17he714a2e23ed7db23E +main::$99$::he714a2e23ed7db23 +# _ at start of path should be removed. +# ".." translates to "::" "$GT$" to ">" and "$LT$" to "<". +--format=rust +_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3bar17h930b740aa94f1d3aE +<Test + 'static as foo::Bar<Test>>::bar +# +--format=rust +_ZN54_$LT$I$u20$as$u20$core..iter..traits..IntoIterator$GT$9into_iter17h8581507801fb8615E +<I as core::iter::traits::IntoIterator>::into_iter +# +--format=rust +_ZN10parse_tsan4main17hdbbfdf1c6a7e27d9E +parse_tsan::main +# +--format=rust +_ZN65_$LT$std..env..Args$u20$as$u20$core..iter..iterator..Iterator$GT$4next17h420a7c8d0c7eef40E +<std::env::Args as core::iter::iterator::Iterator>::next +# +--format=rust +_ZN4core3str9from_utf817hdcea28871313776dE +core::str::from_utf8 +# +--format=rust +_ZN4core3mem7size_of17h18bde9bb8c22e2cfE +core::mem::size_of +# +--format=rust +_ZN5alloc4heap8allocate17hd55c03e6cb81d924E +alloc::heap::allocate +# +--format=rust +_ZN4core3ptr8null_mut17h736cce09ca0ac11aE +core::ptr::null_mut +# +--format=rust +_ZN4core3ptr31_$LT$impl$u20$$BP$mut$u20$T$GT$7is_null17h7f9de798bc3f0879E +core::ptr::<impl *mut T>::is_null +# +--format=rust +_ZN40_$LT$alloc..raw_vec..RawVec$LT$T$GT$$GT$6double17h4166e2b47539e1ffE +<alloc::raw_vec::RawVec<T>>::double +# +--format=rust +_ZN39_$LT$collections..vec..Vec$LT$T$GT$$GT$4push17hd4b6b23c1b88141aE +<collections::vec::Vec<T>>::push +# +--format=rust +_ZN70_$LT$collections..vec..Vec$LT$T$GT$$u20$as$u20$core..ops..DerefMut$GT$9deref_mut17hf299b860dc5a831cE +<collections::vec::Vec<T> as core::ops::DerefMut>::deref_mut +# +--format=rust +_ZN63_$LT$core..ptr..Unique$LT$T$GT$$u20$as$u20$core..ops..Deref$GT$5deref17hc784b4a166cb5e5cE +<core::ptr::Unique<T> as core::ops::Deref>::deref +# +--format=rust +_ZN40_$LT$alloc..raw_vec..RawVec$LT$T$GT$$GT$3ptr17h7570b6e9070b693bE +<alloc::raw_vec::RawVec<T>>::ptr +# +--format=rust +_ZN4core3ptr31_$LT$impl$u20$$BP$mut$u20$T$GT$7is_null17h0f3228f343444ac8E +core::ptr::<impl *mut T>::is_null +# +--format=rust +_ZN53_$LT$$u5b$T$u5d$$u20$as$u20$core..slice..SliceExt$GT$10as_mut_ptr17h153241df1c7d1666E +<[T] as core::slice::SliceExt>::as_mut_ptr +# +--format=rust +_ZN11collections5slice29_$LT$impl$u20$$u5b$T$u5d$$GT$10as_mut_ptr17hf12a6d0409938c96E +collections::slice::<impl [T]>::as_mut_ptr +# +--format=rust +_ZN4core3ptr5write17h651fe53ec860e780E +core::ptr::write +# +--format=rust +_ZN65_$LT$std..env..Args$u20$as$u20$core..iter..iterator..Iterator$GT$4next17h420a7c8d0c7eef40E +<std::env::Args as core::iter::iterator::Iterator>::next +# +--format=rust +_ZN54_$LT$I$u20$as$u20$core..iter..traits..IntoIterator$GT$9into_iter17he06cb713aae5b465E +<I as core::iter::traits::IntoIterator>::into_iter +# +--format=rust +_ZN71_$LT$collections..vec..IntoIter$LT$T$GT$$u20$as$u20$core..ops..Drop$GT$4drop17hf7f23304ebe62eedE +<collections::vec::IntoIter<T> as core::ops::Drop>::drop +# +--format=rust +_ZN86_$LT$collections..vec..IntoIter$LT$T$GT$$u20$as$u20$core..iter..iterator..Iterator$GT$4next17h04b3fbf148c39713E +<collections::vec::IntoIter<T> as core::iter::iterator::Iterator>::next +# +--format=rust +_ZN75_$LT$$RF$$u27$a$u20$mut$u20$I$u20$as$u20$core..iter..iterator..Iterator$GT$4next17ha050492063e0fd20E +<&'a mut I as core::iter::iterator::Iterator>::next +# Different hashes are OK, they are just stripped. +--format=rust +_ZN13drop_contents17hfe3c0a68c8ad1c74E +drop_contents +# +--format=rust +_ZN13drop_contents17h48cb59bef15bb555E +drop_contents +# +--format=rust +_ZN4core3mem7size_of17h900b33157bf58f26E +core::mem::size_of +# +--format=rust +_ZN67_$LT$alloc..raw_vec..RawVec$LT$T$GT$$u20$as$u20$core..ops..Drop$GT$4drop17h96a5cf6e94807905E +<alloc::raw_vec::RawVec<T> as core::ops::Drop>::drop +# +--format=rust +_ZN68_$LT$core..nonzero..NonZero$LT$T$GT$$u20$as$u20$core..ops..Deref$GT$5deref17hc49056f882aa46dbE +<core::nonzero::NonZero<T> as core::ops::Deref>::deref +# +--format=rust +_ZN63_$LT$core..ptr..Unique$LT$T$GT$$u20$as$u20$core..ops..Deref$GT$5deref17h19f2ad4920655e85E +<core::ptr::Unique<T> as core::ops::Deref>::deref |