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_macros::HashStable;
|
||||
use rustc_target::abi::HasDataLayout;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds
|
||||
/// or also inbounds of a *live* allocation.
|
||||
|
@ -112,10 +113,11 @@ impl AllocationExtra<()> for () { }
|
|||
|
||||
impl<Tag, Extra> Allocation<Tag, Extra> {
|
||||
/// Creates a read-only allocation initialized by the given bytes
|
||||
pub fn from_bytes(slice: &[u8], align: Align, extra: Extra) -> Self {
|
||||
let undef_mask = UndefMask::new(Size::from_bytes(slice.len() as u64), true);
|
||||
pub fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align, extra: Extra) -> Self {
|
||||
let bytes = slice.into().into_owned();
|
||||
let undef_mask = UndefMask::new(Size::from_bytes(bytes.len() as u64), true);
|
||||
Self {
|
||||
bytes: slice.to_owned(),
|
||||
bytes,
|
||||
relocations: Relocations::new(),
|
||||
undef_mask,
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Primitive};
|
|||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
use rustc::hir;
|
||||
use syntax::ast::{self, FloatTy};
|
||||
use syntax::symbol::LocalInternedString;
|
||||
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
|
||||
|
@ -213,8 +212,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
}
|
||||
"type_name" => {
|
||||
let tp_ty = substs.type_at(0);
|
||||
let ty_name = LocalInternedString::intern(&tp_ty.to_string());
|
||||
self.const_str_slice(ty_name)
|
||||
let ty_name = rustc_mir::interpret::type_name(self.tcx, tp_ty);
|
||||
OperandRef::from_const(self, ty_name).immediate_or_packed_pair(self)
|
||||
}
|
||||
"type_id" => {
|
||||
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>>(
|
||||
bx: &mut Bx,
|
||||
val: &'tcx ty::Const<'tcx>
|
||||
) -> Result<Self, ErrorHandled> {
|
||||
) -> Self {
|
||||
let layout = bx.layout_of(val.ty);
|
||||
|
||||
if layout.is_zst() {
|
||||
return Ok(OperandRef::new_zst(bx, layout));
|
||||
return OperandRef::new_zst(bx, layout);
|
||||
}
|
||||
|
||||
let val = match val.val {
|
||||
|
@ -110,14 +110,14 @@ impl<'a, 'tcx: 'a, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
OperandValue::Pair(a_llval, b_llval)
|
||||
},
|
||||
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,
|
||||
layout
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) => {
|
||||
let ty = self.monomorphize(&constant.ty);
|
||||
self.eval_mir_constant(constant)
|
||||
.and_then(|c| OperandRef::from_const(bx, c))
|
||||
.map(|c| OperandRef::from_const(bx, c))
|
||||
.unwrap_or_else(|err| {
|
||||
match err {
|
||||
// errored or at least linted
|
||||
|
|
|
@ -427,7 +427,7 @@ impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> {
|
|||
mut self,
|
||||
predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
let mut first = false;
|
||||
let mut first = true;
|
||||
for p in predicates {
|
||||
if !first {
|
||||
write!(self, "+")?;
|
||||
|
|
|
@ -116,7 +116,9 @@ fn op_to_const<'tcx>(
|
|||
ptr.offset.bytes(),
|
||||
),
|
||||
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,
|
||||
),
|
||||
};
|
||||
|
|
|
@ -14,6 +14,9 @@ use super::{
|
|||
Machine, PlaceTy, OpTy, InterpretCx,
|
||||
};
|
||||
|
||||
mod type_name;
|
||||
|
||||
pub use type_name::*;
|
||||
|
||||
fn numeric_intrinsic<'tcx, Tag>(
|
||||
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::validity::RefTracking;
|
||||
|
||||
pub use self::intrinsics::type_name;
|
||||
|
|
|
@ -2,21 +2,64 @@
|
|||
#![allow(dead_code)]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
struct NT(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() {
|
||||
// type_name should support unsized types
|
||||
assert_eq!(unsafe {(
|
||||
// Slice
|
||||
std::intrinsics::type_name::<[u8]>(),
|
||||
// str
|
||||
std::intrinsics::type_name::<str>(),
|
||||
// Trait
|
||||
std::intrinsics::type_name::<dyn Send>(),
|
||||
// Newtype
|
||||
std::intrinsics::type_name::<NT>(),
|
||||
// DST
|
||||
std::intrinsics::type_name::<DST>()
|
||||
)}, ("[u8]", "str", "dyn std::marker::Send", "NT", "DST"));
|
||||
check!([u8], "[u8]");
|
||||
check!(str, "str");
|
||||
check!(dyn Send, "dyn core::marker::Send");
|
||||
check!(NT, "issue_21058::NT");
|
||||
check!(DST, "issue_21058::DST");
|
||||
check!(&i32, "&i32");
|
||||
check!(&'static i32, "&i32");
|
||||
check!((i32, u32), "(i32, u32)");
|
||||
check!(val: foo(), "issue_21058::Foo");
|
||||
check!(val: Foo::new, "issue_21058::Foo::new");
|
||||
check!(val:
|
||||
<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() {
|
||||
unsafe {
|
||||
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