Skip to content

Commit 125c58c

Browse files
committed
Auto merge of rust-lang#74202 - oli-obk:mir_const, r=RalfJung
Reduce the amount of interning and `layout_of` calls in const eval. r? @ghost If we just want to get at some bits of a constant, we don't need to intern it before extracting those bits. Also, if we want to read a `usize` or `bool`, we can fetch the size without invoking a query.
2 parents 4cd0ee9 + 1bf0993 commit 125c58c

File tree

6 files changed

+457
-385
lines changed

6 files changed

+457
-385
lines changed

src/librustc_middle/mir/interpret/value.rs

+12
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ impl<'tcx> ConstValue<'tcx> {
6060
self.try_to_scalar()?.to_bits(size).ok()
6161
}
6262

63+
pub fn try_to_bool(&self) -> Option<bool> {
64+
match self.try_to_bits(Size::from_bytes(1))? {
65+
0 => Some(false),
66+
1 => Some(true),
67+
_ => None,
68+
}
69+
}
70+
71+
pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
72+
Some(self.try_to_bits(tcx.data_layout.pointer_size)? as u64)
73+
}
74+
6375
pub fn try_to_bits_for_ty(
6476
&self,
6577
tcx: TyCtxt<'tcx>,

src/librustc_middle/ty/consts.rs

+192-100
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,203 @@
1-
use crate::mir::interpret::truncate;
2-
use rustc_target::abi::Size;
3-
4-
#[derive(Copy, Clone)]
5-
/// A type for representing any integer. Only used for printing.
6-
// FIXME: Use this for the integer-tree representation needed for type level ints and
7-
// const generics?
8-
pub struct ConstInt {
9-
/// Number of bytes of the integer. Only 1, 2, 4, 8, 16 are legal values.
10-
size: u8,
11-
/// Whether the value is of a signed integer type.
12-
signed: bool,
13-
/// Whether the value is a `usize` or `isize` type.
14-
is_ptr_sized_integral: bool,
15-
/// Raw memory of the integer. All bytes beyond the `size` are unused and must be zero.
16-
raw: u128,
1+
use crate::mir::interpret::ConstValue;
2+
use crate::mir::interpret::{LitToConstInput, Scalar};
3+
use crate::ty::subst::InternalSubsts;
4+
use crate::ty::{self, Ty, TyCtxt};
5+
use crate::ty::{ParamEnv, ParamEnvAnd};
6+
use rustc_errors::ErrorReported;
7+
use rustc_hir as hir;
8+
use rustc_hir::def_id::LocalDefId;
9+
use rustc_macros::HashStable;
10+
11+
mod int;
12+
mod kind;
13+
14+
pub use int::*;
15+
pub use kind::*;
16+
17+
/// Typed constant value.
18+
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)]
19+
#[derive(HashStable)]
20+
pub struct Const<'tcx> {
21+
pub ty: Ty<'tcx>,
22+
23+
pub val: ConstKind<'tcx>,
1724
}
1825

19-
impl ConstInt {
20-
pub fn new(raw: u128, size: Size, signed: bool, is_ptr_sized_integral: bool) -> Self {
21-
assert!(raw <= truncate(u128::MAX, size));
22-
Self { raw, size: size.bytes() as u8, signed, is_ptr_sized_integral }
26+
#[cfg(target_arch = "x86_64")]
27+
static_assert_size!(Const<'_>, 48);
28+
29+
impl<'tcx> Const<'tcx> {
30+
/// Literals and const generic parameters are eagerly converted to a constant, everything else
31+
/// becomes `Unevaluated`.
32+
pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self {
33+
Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id))
2334
}
24-
}
2535

26-
impl std::fmt::Debug for ConstInt {
27-
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28-
let Self { size, signed, raw, is_ptr_sized_integral } = *self;
29-
if signed {
30-
let bit_size = size * 8;
31-
let min = 1u128 << (bit_size - 1);
32-
let max = min - 1;
33-
if raw == min {
34-
match (size, is_ptr_sized_integral) {
35-
(_, true) => write!(fmt, "isize::MIN"),
36-
(1, _) => write!(fmt, "i8::MIN"),
37-
(2, _) => write!(fmt, "i16::MIN"),
38-
(4, _) => write!(fmt, "i32::MIN"),
39-
(8, _) => write!(fmt, "i64::MIN"),
40-
(16, _) => write!(fmt, "i128::MIN"),
41-
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
42-
}
43-
} else if raw == max {
44-
match (size, is_ptr_sized_integral) {
45-
(_, true) => write!(fmt, "isize::MAX"),
46-
(1, _) => write!(fmt, "i8::MAX"),
47-
(2, _) => write!(fmt, "i16::MAX"),
48-
(4, _) => write!(fmt, "i32::MAX"),
49-
(8, _) => write!(fmt, "i64::MAX"),
50-
(16, _) => write!(fmt, "i128::MAX"),
51-
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
36+
pub fn from_opt_const_arg_anon_const(
37+
tcx: TyCtxt<'tcx>,
38+
def: ty::WithOptConstParam<LocalDefId>,
39+
) -> &'tcx Self {
40+
debug!("Const::from_anon_const(def={:?})", def);
41+
42+
let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
43+
44+
let body_id = match tcx.hir().get(hir_id) {
45+
hir::Node::AnonConst(ac) => ac.body,
46+
_ => span_bug!(
47+
tcx.def_span(def.did.to_def_id()),
48+
"from_anon_const can only process anonymous constants"
49+
),
50+
};
51+
52+
let expr = &tcx.hir().body(body_id).value;
53+
54+
let ty = tcx.type_of(def.def_id_for_type_of());
55+
56+
let lit_input = match expr.kind {
57+
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
58+
hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind {
59+
hir::ExprKind::Lit(ref lit) => {
60+
Some(LitToConstInput { lit: &lit.node, ty, neg: true })
5261
}
62+
_ => None,
63+
},
64+
_ => None,
65+
};
66+
67+
if let Some(lit_input) = lit_input {
68+
// If an error occurred, ignore that it's a literal and leave reporting the error up to
69+
// mir.
70+
if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) {
71+
return c;
5372
} else {
54-
match size {
55-
1 => write!(fmt, "{}", raw as i8)?,
56-
2 => write!(fmt, "{}", raw as i16)?,
57-
4 => write!(fmt, "{}", raw as i32)?,
58-
8 => write!(fmt, "{}", raw as i64)?,
59-
16 => write!(fmt, "{}", raw as i128)?,
60-
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
61-
}
62-
if fmt.alternate() {
63-
match (size, is_ptr_sized_integral) {
64-
(_, true) => write!(fmt, "_isize")?,
65-
(1, _) => write!(fmt, "_i8")?,
66-
(2, _) => write!(fmt, "_i16")?,
67-
(4, _) => write!(fmt, "_i32")?,
68-
(8, _) => write!(fmt, "_i64")?,
69-
(16, _) => write!(fmt, "_i128")?,
70-
_ => bug!(),
71-
}
72-
}
73-
Ok(())
73+
tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const");
7474
}
75-
} else {
76-
let max = truncate(u128::MAX, Size::from_bytes(size));
77-
if raw == max {
78-
match (size, is_ptr_sized_integral) {
79-
(_, true) => write!(fmt, "usize::MAX"),
80-
(1, _) => write!(fmt, "u8::MAX"),
81-
(2, _) => write!(fmt, "u16::MAX"),
82-
(4, _) => write!(fmt, "u32::MAX"),
83-
(8, _) => write!(fmt, "u64::MAX"),
84-
(16, _) => write!(fmt, "u128::MAX"),
85-
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
86-
}
87-
} else {
88-
match size {
89-
1 => write!(fmt, "{}", raw as u8)?,
90-
2 => write!(fmt, "{}", raw as u16)?,
91-
4 => write!(fmt, "{}", raw as u32)?,
92-
8 => write!(fmt, "{}", raw as u64)?,
93-
16 => write!(fmt, "{}", raw as u128)?,
94-
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
95-
}
96-
if fmt.alternate() {
97-
match (size, is_ptr_sized_integral) {
98-
(_, true) => write!(fmt, "_usize")?,
99-
(1, _) => write!(fmt, "_u8")?,
100-
(2, _) => write!(fmt, "_u16")?,
101-
(4, _) => write!(fmt, "_u32")?,
102-
(8, _) => write!(fmt, "_u64")?,
103-
(16, _) => write!(fmt, "_u128")?,
104-
_ => bug!(),
105-
}
106-
}
107-
Ok(())
75+
}
76+
77+
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
78+
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
79+
let expr = match &expr.kind {
80+
hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
81+
block.expr.as_ref().unwrap()
82+
}
83+
_ => expr,
84+
};
85+
86+
use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
87+
let val = match expr.kind {
88+
ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
89+
// Find the name and index of the const parameter by indexing the generics of
90+
// the parent item and construct a `ParamConst`.
91+
let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local());
92+
let item_id = tcx.hir().get_parent_node(hir_id);
93+
let item_def_id = tcx.hir().local_def_id(item_id);
94+
let generics = tcx.generics_of(item_def_id.to_def_id());
95+
let index =
96+
generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()];
97+
let name = tcx.hir().name(hir_id);
98+
ty::ConstKind::Param(ty::ParamConst::new(index, name))
10899
}
100+
_ => ty::ConstKind::Unevaluated(
101+
def.to_global(),
102+
InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
103+
None,
104+
),
105+
};
106+
107+
tcx.mk_const(ty::Const { val, ty })
108+
}
109+
110+
#[inline]
111+
/// Interns the given value as a constant.
112+
pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
113+
tcx.mk_const(Self { val: ConstKind::Value(val), ty })
114+
}
115+
116+
#[inline]
117+
/// Interns the given scalar as a constant.
118+
pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self {
119+
Self::from_value(tcx, ConstValue::Scalar(val), ty)
120+
}
121+
122+
#[inline]
123+
/// Creates a constant with the given integer value and interns it.
124+
pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self {
125+
let size = tcx
126+
.layout_of(ty)
127+
.unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
128+
.size;
129+
Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value)
130+
}
131+
132+
#[inline]
133+
/// Creates an interned zst constant.
134+
pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
135+
Self::from_scalar(tcx, Scalar::zst(), ty)
136+
}
137+
138+
#[inline]
139+
/// Creates an interned bool constant.
140+
pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self {
141+
Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool))
142+
}
143+
144+
#[inline]
145+
/// Creates an interned usize constant.
146+
pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self {
147+
Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize))
148+
}
149+
150+
#[inline]
151+
/// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
152+
/// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
153+
/// contains const generic parameters or pointers).
154+
pub fn try_eval_bits(
155+
&self,
156+
tcx: TyCtxt<'tcx>,
157+
param_env: ParamEnv<'tcx>,
158+
ty: Ty<'tcx>,
159+
) -> Option<u128> {
160+
assert_eq!(self.ty, ty);
161+
let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size;
162+
// if `ty` does not depend on generic parameters, use an empty param_env
163+
self.val.eval(tcx, param_env).try_to_bits(size)
164+
}
165+
166+
#[inline]
167+
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
168+
self.val.eval(tcx, param_env).try_to_bool()
169+
}
170+
171+
#[inline]
172+
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> {
173+
self.val.eval(tcx, param_env).try_to_machine_usize(tcx)
174+
}
175+
176+
#[inline]
177+
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
178+
/// unevaluated constant.
179+
pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> {
180+
if let Some(val) = self.val.try_eval(tcx, param_env) {
181+
match val {
182+
Ok(val) => Const::from_value(tcx, val, self.ty),
183+
Err(ErrorReported) => tcx.const_error(self.ty),
184+
}
185+
} else {
186+
self
109187
}
110188
}
189+
190+
#[inline]
191+
/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
192+
pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
193+
self.try_eval_bits(tcx, param_env, ty)
194+
.unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
195+
}
196+
197+
#[inline]
198+
/// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
199+
pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 {
200+
self.try_eval_usize(tcx, param_env)
201+
.unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
202+
}
111203
}

0 commit comments

Comments
 (0)