Auto merge of #60166 - oli-obk:type_name, r=eddyb
Make the `type_name` intrinsic deterministic cc @eddyb for the printing infrastructure cc @Centril for the deterministic (coherent?) output r? @sfackler
This commit is contained in:
commit
db4c783319
|
@ -12,6 +12,7 @@ use std::ops::{Deref, DerefMut};
|
||||||
use rustc_data_structures::sorted_map::SortedMap;
|
use rustc_data_structures::sorted_map::SortedMap;
|
||||||
use rustc_macros::HashStable;
|
use rustc_macros::HashStable;
|
||||||
use rustc_target::abi::HasDataLayout;
|
use rustc_target::abi::HasDataLayout;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds
|
/// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds
|
||||||
/// or also inbounds of a *live* allocation.
|
/// or also inbounds of a *live* allocation.
|
||||||
|
@ -112,10 +113,11 @@ impl AllocationExtra<()> for () { }
|
||||||
|
|
||||||
impl<Tag, Extra> Allocation<Tag, Extra> {
|
impl<Tag, Extra> Allocation<Tag, Extra> {
|
||||||
/// Creates a read-only allocation initialized by the given bytes
|
/// Creates a read-only allocation initialized by the given bytes
|
||||||
pub fn from_bytes(slice: &[u8], align: Align, extra: Extra) -> Self {
|
pub fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align, extra: Extra) -> Self {
|
||||||
let undef_mask = UndefMask::new(Size::from_bytes(slice.len() as u64), true);
|
let bytes = slice.into().into_owned();
|
||||||
|
let undef_mask = UndefMask::new(Size::from_bytes(bytes.len() as u64), true);
|
||||||
Self {
|
Self {
|
||||||
bytes: slice.to_owned(),
|
bytes,
|
||||||
relocations: Relocations::new(),
|
relocations: Relocations::new(),
|
||||||
undef_mask,
|
undef_mask,
|
||||||
align,
|
align,
|
||||||
|
@ -124,7 +126,7 @@ impl<Tag, Extra> Allocation<Tag, Extra> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_byte_aligned_bytes(slice: &[u8], extra: Extra) -> Self {
|
pub fn from_byte_aligned_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, extra: Extra) -> Self {
|
||||||
Allocation::from_bytes(slice, Align::from_bytes(1).unwrap(), extra)
|
Allocation::from_bytes(slice, Align::from_bytes(1).unwrap(), extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Primitive};
|
||||||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
use syntax::ast::{self, FloatTy};
|
use syntax::ast::{self, FloatTy};
|
||||||
use syntax::symbol::LocalInternedString;
|
|
||||||
|
|
||||||
use rustc_codegen_ssa::traits::*;
|
use rustc_codegen_ssa::traits::*;
|
||||||
|
|
||||||
|
@ -213,8 +212,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
||||||
}
|
}
|
||||||
"type_name" => {
|
"type_name" => {
|
||||||
let tp_ty = substs.type_at(0);
|
let tp_ty = substs.type_at(0);
|
||||||
let ty_name = LocalInternedString::intern(&tp_ty.to_string());
|
let ty_name = rustc_mir::interpret::type_name(self.tcx, tp_ty);
|
||||||
self.const_str_slice(ty_name)
|
OperandRef::from_const(self, ty_name).immediate_or_packed_pair(self)
|
||||||
}
|
}
|
||||||
"type_id" => {
|
"type_id" => {
|
||||||
self.const_u64(self.tcx.type_id_hash(substs.type_at(0)))
|
self.const_u64(self.tcx.type_id_hash(substs.type_at(0)))
|
||||||
|
|
|
@ -68,11 +68,11 @@ impl<'a, 'tcx: 'a, V: CodegenObject> OperandRef<'tcx, V> {
|
||||||
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||||
bx: &mut Bx,
|
bx: &mut Bx,
|
||||||
val: &'tcx ty::Const<'tcx>
|
val: &'tcx ty::Const<'tcx>
|
||||||
) -> Result<Self, ErrorHandled> {
|
) -> Self {
|
||||||
let layout = bx.layout_of(val.ty);
|
let layout = bx.layout_of(val.ty);
|
||||||
|
|
||||||
if layout.is_zst() {
|
if layout.is_zst() {
|
||||||
return Ok(OperandRef::new_zst(bx, layout));
|
return OperandRef::new_zst(bx, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = match val.val {
|
let val = match val.val {
|
||||||
|
@ -110,14 +110,14 @@ impl<'a, 'tcx: 'a, V: CodegenObject> OperandRef<'tcx, V> {
|
||||||
OperandValue::Pair(a_llval, b_llval)
|
OperandValue::Pair(a_llval, b_llval)
|
||||||
},
|
},
|
||||||
ConstValue::ByRef(ptr, alloc) => {
|
ConstValue::ByRef(ptr, alloc) => {
|
||||||
return Ok(bx.load_operand(bx.from_const_alloc(layout, alloc, ptr.offset)));
|
return bx.load_operand(bx.from_const_alloc(layout, alloc, ptr.offset));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(OperandRef {
|
OperandRef {
|
||||||
val,
|
val,
|
||||||
layout
|
layout
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that this operand refers to a scalar and returns
|
/// Asserts that this operand refers to a scalar and returns
|
||||||
|
@ -468,7 +468,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
mir::Operand::Constant(ref constant) => {
|
mir::Operand::Constant(ref constant) => {
|
||||||
let ty = self.monomorphize(&constant.ty);
|
let ty = self.monomorphize(&constant.ty);
|
||||||
self.eval_mir_constant(constant)
|
self.eval_mir_constant(constant)
|
||||||
.and_then(|c| OperandRef::from_const(bx, c))
|
.map(|c| OperandRef::from_const(bx, c))
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
match err {
|
match err {
|
||||||
// errored or at least linted
|
// errored or at least linted
|
||||||
|
|
|
@ -427,7 +427,7 @@ impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> {
|
||||||
mut self,
|
mut self,
|
||||||
predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
|
predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
|
||||||
) -> Result<Self::DynExistential, Self::Error> {
|
) -> Result<Self::DynExistential, Self::Error> {
|
||||||
let mut first = false;
|
let mut first = true;
|
||||||
for p in predicates {
|
for p in predicates {
|
||||||
if !first {
|
if !first {
|
||||||
write!(self, "+")?;
|
write!(self, "+")?;
|
||||||
|
|
|
@ -116,7 +116,9 @@ fn op_to_const<'tcx>(
|
||||||
ptr.offset.bytes(),
|
ptr.offset.bytes(),
|
||||||
),
|
),
|
||||||
Scalar::Raw { .. } => (
|
Scalar::Raw { .. } => (
|
||||||
ecx.tcx.intern_const_alloc(Allocation::from_byte_aligned_bytes(b"", ())),
|
ecx.tcx.intern_const_alloc(Allocation::from_byte_aligned_bytes(
|
||||||
|
b"" as &[u8], (),
|
||||||
|
)),
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,9 @@ use super::{
|
||||||
Machine, PlaceTy, OpTy, InterpretCx,
|
Machine, PlaceTy, OpTy, InterpretCx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod type_name;
|
||||||
|
|
||||||
|
pub use type_name::*;
|
||||||
|
|
||||||
fn numeric_intrinsic<'tcx, Tag>(
|
fn numeric_intrinsic<'tcx, Tag>(
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
use rustc::ty::{
|
||||||
|
TyCtxt, Ty,
|
||||||
|
subst::{UnpackedKind, Kind},
|
||||||
|
print::{Printer, PrettyPrinter, Print},
|
||||||
|
self,
|
||||||
|
};
|
||||||
|
use rustc::hir::map::{DefPathData, DisambiguatedDefPathData};
|
||||||
|
use rustc::hir::def_id::CrateNum;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use rustc::mir::interpret::{Allocation, ConstValue};
|
||||||
|
|
||||||
|
struct AbsolutePathPrinter<'a, 'tcx> {
|
||||||
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Printer<'tcx, 'tcx> for AbsolutePathPrinter<'_, 'tcx> {
|
||||||
|
type Error = std::fmt::Error;
|
||||||
|
|
||||||
|
type Path = Self;
|
||||||
|
type Region = Self;
|
||||||
|
type Type = Self;
|
||||||
|
type DynExistential = Self;
|
||||||
|
type Const = Self;
|
||||||
|
|
||||||
|
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
|
||||||
|
self.tcx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||||
|
match ty.sty {
|
||||||
|
// types without identity
|
||||||
|
| ty::Bool
|
||||||
|
| ty::Char
|
||||||
|
| ty::Int(_)
|
||||||
|
| ty::Uint(_)
|
||||||
|
| ty::Float(_)
|
||||||
|
| ty::Str
|
||||||
|
| ty::Array(_, _)
|
||||||
|
| ty::Slice(_)
|
||||||
|
| ty::RawPtr(_)
|
||||||
|
| ty::Ref(_, _, _)
|
||||||
|
| ty::FnPtr(_)
|
||||||
|
| ty::Never
|
||||||
|
| ty::Tuple(_)
|
||||||
|
| ty::Dynamic(_, _)
|
||||||
|
| ty::Adt(..)
|
||||||
|
| ty::Foreign(_)
|
||||||
|
// should be unreachable, but there's no hurt in printing it (and better than ICEing)
|
||||||
|
| ty::Error
|
||||||
|
=> self.pretty_print_type(ty),
|
||||||
|
| ty::Infer(_)
|
||||||
|
| ty::Bound(_, _)
|
||||||
|
| ty::Param(_)
|
||||||
|
| ty::Placeholder(_)
|
||||||
|
| ty::Projection(_)
|
||||||
|
| ty::UnnormalizedProjection(_)
|
||||||
|
| ty::GeneratorWitness(_)
|
||||||
|
=> bug!(
|
||||||
|
"{:#?} in `type_name` should not happen because we are always monomorphized",
|
||||||
|
ty,
|
||||||
|
),
|
||||||
|
// types with identity (print the module path instead)
|
||||||
|
| ty::FnDef(did, substs)
|
||||||
|
| ty::Opaque(did, substs)
|
||||||
|
=> self.print_def_path(did, substs),
|
||||||
|
ty::Closure(did, substs) => self.print_def_path(did, substs.substs),
|
||||||
|
ty::Generator(did, substs, _) => self.print_def_path(did, substs.substs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_const(
|
||||||
|
self,
|
||||||
|
_: &'tcx ty::Const<'tcx>,
|
||||||
|
) -> Result<Self::Const, Self::Error> {
|
||||||
|
// don't print constants to the user
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_dyn_existential(
|
||||||
|
mut self,
|
||||||
|
predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
|
||||||
|
) -> Result<Self::DynExistential, Self::Error> {
|
||||||
|
let mut first = true;
|
||||||
|
for p in predicates {
|
||||||
|
if !first {
|
||||||
|
write!(self, "+")?;
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
self = p.print(self)?;
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||||
|
self.path.push_str(&self.tcx.original_crate_name(cnum).as_str());
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_qualified(
|
||||||
|
self,
|
||||||
|
self_ty: Ty<'tcx>,
|
||||||
|
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||||
|
) -> Result<Self::Path, Self::Error> {
|
||||||
|
self.pretty_path_qualified(self_ty, trait_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_append_impl(
|
||||||
|
self,
|
||||||
|
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||||
|
_disambiguated_data: &DisambiguatedDefPathData,
|
||||||
|
self_ty: Ty<'tcx>,
|
||||||
|
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||||
|
) -> Result<Self::Path, Self::Error> {
|
||||||
|
self.pretty_path_append_impl(
|
||||||
|
|mut cx| {
|
||||||
|
cx = print_prefix(cx)?;
|
||||||
|
|
||||||
|
cx.path.push_str("::");
|
||||||
|
|
||||||
|
Ok(cx)
|
||||||
|
},
|
||||||
|
self_ty,
|
||||||
|
trait_ref,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_append(
|
||||||
|
mut self,
|
||||||
|
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||||
|
disambiguated_data: &DisambiguatedDefPathData,
|
||||||
|
) -> Result<Self::Path, Self::Error> {
|
||||||
|
self = print_prefix(self)?;
|
||||||
|
|
||||||
|
// Skip `::{{constructor}}` on tuple/unit structs.
|
||||||
|
match disambiguated_data.data {
|
||||||
|
DefPathData::Ctor => return Ok(self),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.path.push_str("::");
|
||||||
|
|
||||||
|
self.path.push_str(&disambiguated_data.data.as_interned_str().as_str());
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_generic_args(
|
||||||
|
mut self,
|
||||||
|
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||||
|
args: &[Kind<'tcx>],
|
||||||
|
) -> Result<Self::Path, Self::Error> {
|
||||||
|
self = print_prefix(self)?;
|
||||||
|
let args = args.iter().cloned().filter(|arg| {
|
||||||
|
match arg.unpack() {
|
||||||
|
UnpackedKind::Lifetime(_) => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if args.clone().next().is_some() {
|
||||||
|
self.generic_delimiters(|cx| cx.comma_sep(args))
|
||||||
|
} else {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PrettyPrinter<'tcx, 'tcx> for AbsolutePathPrinter<'_, 'tcx> {
|
||||||
|
fn region_should_not_be_omitted(
|
||||||
|
&self,
|
||||||
|
_region: ty::Region<'_>,
|
||||||
|
) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn comma_sep<T>(
|
||||||
|
mut self,
|
||||||
|
mut elems: impl Iterator<Item = T>,
|
||||||
|
) -> Result<Self, Self::Error>
|
||||||
|
where T: Print<'tcx, 'tcx, Self, Output = Self, Error = Self::Error>
|
||||||
|
{
|
||||||
|
if let Some(first) = elems.next() {
|
||||||
|
self = first.print(self)?;
|
||||||
|
for elem in elems {
|
||||||
|
self.path.push_str(", ");
|
||||||
|
self = elem.print(self)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_delimiters(
|
||||||
|
mut self,
|
||||||
|
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
write!(self, "<")?;
|
||||||
|
|
||||||
|
self = f(self)?;
|
||||||
|
|
||||||
|
write!(self, ">")?;
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for AbsolutePathPrinter<'_, '_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
|
Ok(self.path.push_str(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces an absolute path representation of the given type. See also the documentation on
|
||||||
|
/// `std::any::type_name`
|
||||||
|
pub fn type_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||||
|
let path = AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path;
|
||||||
|
let len = path.len();
|
||||||
|
let alloc = Allocation::from_byte_aligned_bytes(path.into_bytes(), ());
|
||||||
|
let alloc = tcx.intern_const_alloc(alloc);
|
||||||
|
tcx.mk_const(ty::Const {
|
||||||
|
val: ConstValue::Slice {
|
||||||
|
data: alloc,
|
||||||
|
start: 0,
|
||||||
|
end: len,
|
||||||
|
},
|
||||||
|
ty: tcx.mk_static_str(),
|
||||||
|
})
|
||||||
|
}
|
|
@ -32,3 +32,5 @@ pub use self::operand::{ScalarMaybeUndef, Immediate, ImmTy, Operand, OpTy};
|
||||||
pub use self::visitor::{ValueVisitor, MutValueVisitor};
|
pub use self::visitor::{ValueVisitor, MutValueVisitor};
|
||||||
|
|
||||||
pub use self::validity::RefTracking;
|
pub use self::validity::RefTracking;
|
||||||
|
|
||||||
|
pub use self::intrinsics::type_name;
|
||||||
|
|
|
@ -2,21 +2,64 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
struct NT(str);
|
struct NT(str);
|
||||||
struct DST { a: u32, b: str }
|
struct DST { a: u32, b: str }
|
||||||
|
|
||||||
|
macro_rules! check {
|
||||||
|
(val: $ty_of:expr, $expected:expr) => {
|
||||||
|
assert_eq!(type_name_of_val($ty_of), $expected);
|
||||||
|
};
|
||||||
|
($ty:ty, $expected:expr) => {
|
||||||
|
assert_eq!(unsafe { std::intrinsics::type_name::<$ty>()}, $expected);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// type_name should support unsized types
|
// type_name should support unsized types
|
||||||
assert_eq!(unsafe {(
|
check!([u8], "[u8]");
|
||||||
// Slice
|
check!(str, "str");
|
||||||
std::intrinsics::type_name::<[u8]>(),
|
check!(dyn Send, "dyn core::marker::Send");
|
||||||
// str
|
check!(NT, "issue_21058::NT");
|
||||||
std::intrinsics::type_name::<str>(),
|
check!(DST, "issue_21058::DST");
|
||||||
// Trait
|
check!(&i32, "&i32");
|
||||||
std::intrinsics::type_name::<dyn Send>(),
|
check!(&'static i32, "&i32");
|
||||||
// Newtype
|
check!((i32, u32), "(i32, u32)");
|
||||||
std::intrinsics::type_name::<NT>(),
|
check!(val: foo(), "issue_21058::Foo");
|
||||||
// DST
|
check!(val: Foo::new, "issue_21058::Foo::new");
|
||||||
std::intrinsics::type_name::<DST>()
|
check!(val:
|
||||||
)}, ("[u8]", "str", "dyn std::marker::Send", "NT", "DST"));
|
<Foo as Debug>::fmt,
|
||||||
|
"<issue_21058::Foo as core::fmt::Debug>::fmt"
|
||||||
|
);
|
||||||
|
check!(val: || {}, "issue_21058::main::{{closure}}");
|
||||||
|
bar::<i32>();
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Trait {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for i32 {
|
||||||
|
type Assoc = String;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar<T: Trait>() {
|
||||||
|
check!(T::Assoc, "alloc::string::String");
|
||||||
|
check!(T, "i32");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name_of_val<T>(_: T) -> &'static str {
|
||||||
|
unsafe { std::intrinsics::type_name::<T>() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self { Foo }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() -> impl Debug {
|
||||||
|
Foo
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,6 @@ struct Foo<T> {
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_eq!(type_name::<isize>(), "isize");
|
assert_eq!(type_name::<isize>(), "isize");
|
||||||
assert_eq!(type_name::<Foo<usize>>(), "Foo<usize>");
|
assert_eq!(type_name::<Foo<usize>>(), "tydesc_name::Foo<usize>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue