Skip to content

Commit

Permalink
Make the type_name intrinsic's output deterministic
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed May 30, 2019
1 parent 14178ef commit 5b98489
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 27 deletions.
10 changes: 6 additions & 4 deletions src/librustc/mir/interpret/allocation.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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)
}

Expand Down
5 changes: 2 additions & 3 deletions src/librustc_codegen_llvm/intrinsic.rs
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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)))
Expand Down
12 changes: 6 additions & 6 deletions src/librustc_codegen_ssa/mir/operand.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir/const_eval.rs
Expand Up @@ -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,
),
};
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_mir/interpret/intrinsics.rs
Expand Up @@ -14,6 +14,9 @@ use super::{
Machine, PlaceTy, OpTy, InterpretCx,
};

mod type_name;

pub use type_name::*;

fn numeric_intrinsic<'tcx, Tag>(
name: &str,
Expand Down
228 changes: 228 additions & 0 deletions 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<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(),
})
}
2 changes: 2 additions & 0 deletions src/librustc_mir/interpret/mod.rs
Expand Up @@ -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;

0 comments on commit 5b98489

Please sign in to comment.