Auto merge of #87194 - eddyb:const-value-mangling, r=michaelwoerister,oli-obk
rustc_symbol_mangling: support structural constants and &str in v0. This PR should unblock #85530 (except for float `const` generics, which AFAIK should've never worked). (cc `@tmiasko` could the https://github.com/rust-lang/rust/pull/85530#issuecomment-857855379 failures be retried with a quick crater "subset" run of this PR + changing the default to `v0`? Just to make sure I didn't miss anything other than the floats) The encoding is the one suggested before in e.g. https://github.com/rust-lang/rust/issues/61486#issuecomment-878932102, tho this PR won't by itself finish #61486, before closing that we'd likely want to move to `@oli-obk's` "valtrees" (i.e. #83234 and other associated work). <hr> **EDITs**: 1. switched unit/tuple/braced-with-named-fields `<const-fields>` prefixes from `"u"`/`"T"`/`""` to `"U"`/`"T"`/`"S"` to avoid the ambiguity reported by `@tmiasko` in https://github.com/rust-lang/rust/pull/87194#issuecomment-884279921. 2. `rustc-demangle` PR: https://github.com/alexcrichton/rustc-demangle/pull/55 3. RFC amendment PR: https://github.com/rust-lang/rfcs/pull/3161 * also removed the grammar changes included in that PR, from this description 4. added tests (temporarily using my fork of `rustc-demangle`) <hr> r? `@michaelwoerister`
This commit is contained in:
commit
ad02dc46ba
21 changed files with 609 additions and 63 deletions
|
@ -15,7 +15,7 @@ measureme = "9.1.0"
|
|||
snap = "1"
|
||||
tracing = "0.1"
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc-demangle = "0.1.18"
|
||||
rustc-demangle = "0.1.21"
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
|
@ -1220,13 +1220,20 @@ pub trait PrettyPrinter<'tcx>:
|
|||
}
|
||||
p!(")");
|
||||
}
|
||||
ty::Adt(def, substs) if def.variants.is_empty() => {
|
||||
p!(print_value_path(def.did, substs));
|
||||
ty::Adt(def, _) if def.variants.is_empty() => {
|
||||
self = self.typed_value(
|
||||
|mut this| {
|
||||
write!(this, "unreachable()")?;
|
||||
Ok(this)
|
||||
},
|
||||
|this| this.print_type(ty),
|
||||
": ",
|
||||
)?;
|
||||
}
|
||||
ty::Adt(def, substs) => {
|
||||
let variant_id =
|
||||
contents.variant.expect("destructed const of adt without variant id");
|
||||
let variant_def = &def.variants[variant_id];
|
||||
let variant_idx =
|
||||
contents.variant.expect("destructed const of adt without variant idx");
|
||||
let variant_def = &def.variants[variant_idx];
|
||||
p!(print_value_path(variant_def.def_id, substs));
|
||||
|
||||
match variant_def.ctor_kind {
|
||||
|
|
|
@ -9,7 +9,7 @@ doctest = false
|
|||
[dependencies]
|
||||
tracing = "0.1"
|
||||
punycode = "0.4.0"
|
||||
rustc-demangle = "0.1.18"
|
||||
rustc-demangle = "0.1.21"
|
||||
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
#![feature(never_type)]
|
||||
#![feature(nll)]
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(iter_zip)]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use rustc_data_structures::base_n;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::def_id::{CrateNum, DefId};
|
||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::print::{Print, Printer};
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
||||
|
@ -11,6 +13,7 @@ use rustc_target::abi::Integer;
|
|||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::iter;
|
||||
use std::ops::Range;
|
||||
|
||||
pub(super) fn mangle(
|
||||
|
@ -534,39 +537,153 @@ impl Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
}
|
||||
|
||||
fn print_const(mut self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
// We only mangle a typed value if the const can be evaluated.
|
||||
let ct = ct.eval(self.tcx, ty::ParamEnv::reveal_all());
|
||||
match ct.val {
|
||||
ty::ConstKind::Value(_) => {}
|
||||
|
||||
// Placeholders (should be demangled as `_`).
|
||||
// NOTE(eddyb) despite `Unevaluated` having a `DefId` (and therefore
|
||||
// a path), even for it we still need to encode a placeholder, as
|
||||
// the path could refer back to e.g. an `impl` using the constant.
|
||||
ty::ConstKind::Unevaluated(_)
|
||||
| ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Infer(_)
|
||||
| ty::ConstKind::Bound(..)
|
||||
| ty::ConstKind::Placeholder(_)
|
||||
| ty::ConstKind::Error(_) => {
|
||||
// Never cached (single-character).
|
||||
self.push("p");
|
||||
return Ok(self);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(&i) = self.consts.get(&ct) {
|
||||
return self.print_backref(i);
|
||||
}
|
||||
let start = self.out.len();
|
||||
|
||||
let mut neg = false;
|
||||
let val = match ct.ty.kind() {
|
||||
ty::Uint(_) | ty::Bool | ty::Char => {
|
||||
ct.try_eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty)
|
||||
}
|
||||
ty::Int(ity) => {
|
||||
ct.try_eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty).and_then(|b| {
|
||||
let val = Integer::from_int_ty(&self.tcx, *ity).size().sign_extend(b) as i128;
|
||||
match ct.ty.kind() {
|
||||
ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
|
||||
self = ct.ty.print(self)?;
|
||||
|
||||
let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty);
|
||||
|
||||
// Negative integer values are mangled using `n` as a "sign prefix".
|
||||
if let ty::Int(ity) = ct.ty.kind() {
|
||||
let val =
|
||||
Integer::from_int_ty(&self.tcx, *ity).size().sign_extend(bits) as i128;
|
||||
if val < 0 {
|
||||
neg = true;
|
||||
self.push("n");
|
||||
}
|
||||
Some(val.unsigned_abs())
|
||||
})
|
||||
bits = val.unsigned_abs();
|
||||
}
|
||||
|
||||
let _ = write!(self.out, "{:x}_", bits);
|
||||
}
|
||||
|
||||
// HACK(eddyb) because `ty::Const` only supports sized values (for now),
|
||||
// we can't use `deref_const` + supporting `str`, we have to specially
|
||||
// handle `&str` and include both `&` ("R") and `str` ("e") prefixes.
|
||||
ty::Ref(_, ty, hir::Mutability::Not) if *ty == self.tcx.types.str_ => {
|
||||
self.push("R");
|
||||
match ct.val {
|
||||
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
|
||||
// NOTE(eddyb) the following comment was kept from `ty::print::pretty`:
|
||||
// The `inspect` here is okay since we checked the bounds, and there are no
|
||||
// relocations (we have an active `str` reference here). We don't use this
|
||||
// result to affect interpreter execution.
|
||||
let slice =
|
||||
data.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
|
||||
let s = std::str::from_utf8(slice).expect("non utf8 str from miri");
|
||||
|
||||
self.push("e");
|
||||
// FIXME(eddyb) use a specialized hex-encoding loop.
|
||||
for byte in s.bytes() {
|
||||
let _ = write!(self.out, "{:02x}", byte);
|
||||
}
|
||||
self.push("_");
|
||||
}
|
||||
|
||||
_ => {
|
||||
bug!("symbol_names: unsupported `&str` constant: {:?}", ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Ref(_, _, mutbl) => {
|
||||
self.push(match mutbl {
|
||||
hir::Mutability::Not => "R",
|
||||
hir::Mutability::Mut => "Q",
|
||||
});
|
||||
self = self.tcx.deref_const(ty::ParamEnv::reveal_all().and(ct)).print(self)?;
|
||||
}
|
||||
|
||||
ty::Array(..) | ty::Tuple(..) | ty::Adt(..) => {
|
||||
let contents = self.tcx.destructure_const(ty::ParamEnv::reveal_all().and(ct));
|
||||
let fields = contents.fields.iter().copied();
|
||||
|
||||
let print_field_list = |mut this: Self| {
|
||||
for field in fields.clone() {
|
||||
this = field.print(this)?;
|
||||
}
|
||||
this.push("E");
|
||||
Ok(this)
|
||||
};
|
||||
|
||||
match *ct.ty.kind() {
|
||||
ty::Array(..) => {
|
||||
self.push("A");
|
||||
self = print_field_list(self)?;
|
||||
}
|
||||
ty::Tuple(..) => {
|
||||
self.push("T");
|
||||
self = print_field_list(self)?;
|
||||
}
|
||||
ty::Adt(def, substs) => {
|
||||
let variant_idx =
|
||||
contents.variant.expect("destructed const of adt without variant idx");
|
||||
let variant_def = &def.variants[variant_idx];
|
||||
|
||||
self.push("V");
|
||||
self = self.print_def_path(variant_def.def_id, substs)?;
|
||||
|
||||
match variant_def.ctor_kind {
|
||||
CtorKind::Const => {
|
||||
self.push("U");
|
||||
}
|
||||
CtorKind::Fn => {
|
||||
self.push("T");
|
||||
self = print_field_list(self)?;
|
||||
}
|
||||
CtorKind::Fictive => {
|
||||
self.push("S");
|
||||
for (field_def, field) in iter::zip(&variant_def.fields, fields) {
|
||||
// HACK(eddyb) this mimics `path_append`,
|
||||
// instead of simply using `field_def.ident`,
|
||||
// just to be able to handle disambiguators.
|
||||
let disambiguated_field =
|
||||
self.tcx.def_key(field_def.did).disambiguated_data;
|
||||
let field_name =
|
||||
disambiguated_field.data.get_opt_name().map(|s| s.as_str());
|
||||
self.push_disambiguator(
|
||||
disambiguated_field.disambiguator as u64,
|
||||
);
|
||||
self.push_ident(&field_name.as_ref().map_or("", |s| &s[..]));
|
||||
|
||||
self = field.print(self)?;
|
||||
}
|
||||
self.push("E");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct);
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(bits) = val {
|
||||
// We only print the type if the const can be evaluated.
|
||||
self = ct.ty.print(self)?;
|
||||
let _ = write!(self.out, "{}{:x}_", if neg { "n" } else { "" }, bits);
|
||||
} else {
|
||||
// NOTE(eddyb) despite having the path, we need to
|
||||
// encode a placeholder, as the path could refer
|
||||
// back to e.g. an `impl` using the constant.
|
||||
self.push("p");
|
||||
}
|
||||
|
||||
// Only cache consts that do not refer to an enclosing
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue