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:
bors 2019-05-31 10:42:31 +00:00
commit db4c783319
10 changed files with 307 additions and 28 deletions

View File

@ -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)
}

View File

@ -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)))

View File

@ -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

View File

@ -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, "+")?;

View File

@ -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,
),
};

View File

@ -14,6 +14,9 @@ use super::{
Machine, PlaceTy, OpTy, InterpretCx,
};
mod type_name;
pub use type_name::*;
fn numeric_intrinsic<'tcx, Tag>(
name: &str,

View File

@ -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(),
})
}

View File

@ -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;

View File

@ -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
}

View File

@ -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>");
}
}