diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 9549a5af5af5c..856b47c7e1b6f 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -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 Allocation { /// 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>, 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 Allocation { } } - pub fn from_byte_aligned_bytes(slice: &[u8], extra: Extra) -> Self { + pub fn from_byte_aligned_bytes<'a>(slice: impl Into>, extra: Extra) -> Self { Allocation::from_bytes(slice, Align::from_bytes(1).unwrap(), extra) } diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 9ae0e26196d94..060c295eb7af1 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -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))) diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs index ec471a1323eab..0ae2fbe8071ad 100644 --- a/src/librustc_codegen_ssa/mir/operand.rs +++ b/src/librustc_codegen_ssa/mir/operand.rs @@ -68,11 +68,11 @@ impl<'a, 'tcx: 'a, V: CodegenObject> OperandRef<'tcx, V> { pub fn from_const>( bx: &mut Bx, val: &'tcx ty::Const<'tcx> - ) -> Result { + ) -> 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 diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 0637c7b058882..2c68ed58d50d4 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -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, ), }; diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index d9721a8cadff9..34e9b4972a16d 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -14,6 +14,9 @@ use super::{ Machine, PlaceTy, OpTy, InterpretCx, }; +mod type_name; + +pub use type_name::*; fn numeric_intrinsic<'tcx, Tag>( name: &str, diff --git a/src/librustc_mir/interpret/intrinsics/type_name.rs b/src/librustc_mir/interpret/intrinsics/type_name.rs new file mode 100644 index 0000000000000..48b02d8e11b60 --- /dev/null +++ b/src/librustc_mir/interpret/intrinsics/type_name.rs @@ -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 { + Ok(self) + } + + fn print_type(self, ty: Ty<'tcx>) -> Result { + 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 { + // don't print constants to the user + Ok(self) + } + + fn print_dyn_existential( + mut self, + predicates: &'tcx ty::List>, + ) -> Result { + 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.push_str(&self.tcx.original_crate_name(cnum).as_str()); + Ok(self) + } + + fn path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self.pretty_path_qualified(self_ty, trait_ref) + } + + fn path_append_impl( + self, + print_prefix: impl FnOnce(Self) -> Result, + _disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + 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, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result { + 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, + args: &[Kind<'tcx>], + ) -> Result { + 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( + mut self, + mut elems: impl Iterator, + ) -> Result + 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, + ) -> Result { + 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(), + }) +} diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index ea358389ddb76..ed389a8df94ab 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -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; diff --git a/src/test/run-pass/issues/issue-21058.rs b/src/test/run-pass/issues/issue-21058.rs index 0483e62fd2121..3efa94ce6835c 100644 --- a/src/test/run-pass/issues/issue-21058.rs +++ b/src/test/run-pass/issues/issue-21058.rs @@ -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::(), - // Trait - std::intrinsics::type_name::(), - // Newtype - std::intrinsics::type_name::(), - // DST - std::intrinsics::type_name::() - )}, ("[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: + ::fmt, + "::fmt" + ); + check!(val: || {}, "issue_21058::main::{{closure}}"); + bar::(); +} + +trait Trait { + type Assoc; +} + +impl Trait for i32 { + type Assoc = String; +} + +fn bar() { + check!(T::Assoc, "alloc::string::String"); + check!(T, "i32"); +} + +fn type_name_of_val(_: T) -> &'static str { + unsafe { std::intrinsics::type_name::() } +} + +#[derive(Debug)] +struct Foo; + +impl Foo { + fn new() -> Self { Foo } +} + +fn foo() -> impl Debug { + Foo } diff --git a/src/test/run-pass/tydesc-name.rs b/src/test/run-pass/tydesc-name.rs index 94de30e88e1fb..91578b71d04ae 100644 --- a/src/test/run-pass/tydesc-name.rs +++ b/src/test/run-pass/tydesc-name.rs @@ -11,6 +11,6 @@ struct Foo { pub fn main() { unsafe { assert_eq!(type_name::(), "isize"); - assert_eq!(type_name::>(), "Foo"); + assert_eq!(type_name::>(), "tydesc_name::Foo"); } }