Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make the eval() functions on our const types return the resulting value #115803

Merged
merged 3 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,11 +723,8 @@ fn codegen_stmt<'tcx>(
}
Rvalue::Repeat(ref operand, times) => {
let operand = codegen_operand(fx, operand);
let times = fx
.monomorphize(times)
.eval(fx.tcx, ParamEnv::reveal_all())
.try_to_bits(fx.tcx.data_layout.pointer_size)
.unwrap();
let times =
fx.monomorphize(times).eval_target_usize(fx.tcx, ParamEnv::reveal_all());
if operand.layout().size.bytes() == 0 {
// Do nothing for ZST's
} else if fx.clif_type(operand.layout().ty) == Some(types::I8) {
Expand Down
30 changes: 4 additions & 26 deletions compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,31 +77,9 @@ pub(crate) fn eval_mir_constant<'tcx>(
fx: &FunctionCx<'_, '_, 'tcx>,
constant: &Constant<'tcx>,
) -> Option<(ConstValue<'tcx>, Ty<'tcx>)> {
let constant_kind = fx.monomorphize(constant.literal);
let uv = match constant_kind {
ConstantKind::Ty(const_) => match const_.kind() {
ty::ConstKind::Unevaluated(uv) => uv.expand(),
ty::ConstKind::Value(val) => {
return Some((fx.tcx.valtree_to_const_val((const_.ty(), val)), const_.ty()));
}
err => span_bug!(
constant.span,
"encountered bad ConstKind after monomorphizing: {:?}",
err
),
},
ConstantKind::Unevaluated(mir::UnevaluatedConst { def, .. }, _)
if fx.tcx.is_static(def) =>
{
span_bug!(constant.span, "MIR constant refers to static");
}
ConstantKind::Unevaluated(uv, _) => uv,
ConstantKind::Val(val, _) => return Some((val, constant_kind.ty())),
};

let val = fx
.tcx
.const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None)
let cv = fx.monomorphize(constant.literal);
let val = cv
.eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span))
.map_err(|err| match err {
ErrorHandled::Reported(_) => {
fx.tcx.sess.span_err(constant.span, "erroneous constant encountered");
Expand All @@ -111,7 +89,7 @@ pub(crate) fn eval_mir_constant<'tcx>(
}
})
.ok();
val.map(|val| (val, constant_kind.ty()))
val.map(|val| (val, cv.ty()))
}

pub(crate) fn codegen_constant_operand<'tcx>(
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,8 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
// avoiding collisions and will make the emitted type names shorter.
let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
let mut hasher = StableHasher::new();
let ct = ct.eval(tcx, ty::ParamEnv::reveal_all());
hcx.while_hashing_spans(false, |hcx| {
ct.to_valtree().hash_stable(hcx, &mut hasher)
});
let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), None).unwrap();
hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher));
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
hasher.finish::<Hash64>()
});

Expand Down
52 changes: 20 additions & 32 deletions compiler/rustc_codegen_ssa/src/mir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,43 +24,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&self,
constant: &mir::Constant<'tcx>,
) -> Result<ConstValue<'tcx>, ErrorHandled> {
let ct = self.monomorphize(constant.literal);
let uv = match ct {
mir::ConstantKind::Ty(ct) => match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv.expand(),
ty::ConstKind::Value(val) => {
return Ok(self.cx.tcx().valtree_to_const_val((ct.ty(), val)));
self.monomorphize(constant.literal)
.eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span))
.map_err(|err| {
match err {
ErrorHandled::Reported(_) => {
self.cx
.tcx()
.sess
.emit_err(errors::ErroneousConstant { span: constant.span });
}
ErrorHandled::TooGeneric => {
self.cx.tcx().sess.diagnostic().emit_bug(
errors::PolymorphicConstantTooGeneric { span: constant.span },
);
}
}
err => span_bug!(
constant.span,
"encountered bad ConstKind after monomorphizing: {:?}",
err
),
},
mir::ConstantKind::Unevaluated(uv, _) => uv,
mir::ConstantKind::Val(val, _) => return Ok(val),
};

self.cx.tcx().const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None).map_err(|err| {
match err {
ErrorHandled::Reported(_) => {
self.cx.tcx().sess.emit_err(errors::ErroneousConstant { span: constant.span });
}
ErrorHandled::TooGeneric => {
self.cx
.tcx()
.sess
.diagnostic()
.emit_bug(errors::PolymorphicConstantTooGeneric { span: constant.span });
}
}
err
})
err
})
}

/// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
/// that the given `constant` is an `ConstantKind::Unevaluated` and must be convertible to
/// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
///
/// Note that this function is cursed, since usually MIR consts should not be evaluated to valtrees!
pub fn eval_unevaluated_mir_constant_to_valtree(
&self,
constant: &mir::Constant<'tcx>,
Expand All @@ -80,7 +68,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but
// the user pass through arbitrary expressions.
// FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real
// const generic.
// const generic, and get rid of this entire function.
other => span_bug!(constant.span, "{other:#?}"),
};
let uv = self.monomorphize(uv);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ TrivialTypeTraversalAndLiftImpls! { ErrorHandled }

pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed.
/// This is needed in `thir::pattern::lower_inline_const`.
pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;

pub fn struct_error<'tcx>(
Expand Down
154 changes: 74 additions & 80 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2299,18 +2299,6 @@ impl<'tcx> ConstantKind<'tcx> {
}
}

#[inline]
pub fn try_to_value(self, tcx: TyCtxt<'tcx>) -> Option<interpret::ConstValue<'tcx>> {
match self {
ConstantKind::Ty(c) => match c.kind() {
ty::ConstKind::Value(valtree) => Some(tcx.valtree_to_const_val((c.ty(), valtree))),
_ => None,
},
ConstantKind::Val(val, _) => Some(val),
ConstantKind::Unevaluated(..) => None,
}
}

#[inline]
pub fn try_to_scalar(self) -> Option<Scalar> {
match self {
Expand Down Expand Up @@ -2342,37 +2330,59 @@ impl<'tcx> ConstantKind<'tcx> {
}

#[inline]
pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
match self {
Self::Ty(c) => {
if let Some(val) = c.try_eval_for_mir(tcx, param_env) {
match val {
Ok(val) => Self::Val(val, c.ty()),
Err(guar) => Self::Ty(ty::Const::new_error(tcx, guar, self.ty())),
}
pub fn eval(
self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Option<Span>,
) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
let (uneval, param_env) = match self {
ConstantKind::Ty(c) => {
if let ty::ConstKind::Unevaluated(uneval) = c.kind() {
// Avoid the round-trip via valtree, evaluate directly to ConstValue.
let (param_env, uneval) = uneval.prepare_for_eval(tcx, param_env);
(uneval.expand(), param_env)
} else {
self
// It's already a valtree, or an error.
let val = c.eval(tcx, param_env, span)?;
return Ok(tcx.valtree_to_const_val((self.ty(), val)));
}
}
Self::Val(_, _) => self,
Self::Unevaluated(uneval, ty) => {
// FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
match tcx.const_eval_resolve(param_env, uneval, None) {
Ok(val) => Self::Val(val, ty),
Err(ErrorHandled::TooGeneric) => self,
Err(ErrorHandled::Reported(guar)) => {
Self::Ty(ty::Const::new_error(tcx, guar.into(), ty))
}
}
ConstantKind::Unevaluated(uneval, _) => (uneval, param_env),
ConstantKind::Val(val, _) => return Ok(val),
};
// FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
tcx.const_eval_resolve(param_env, uneval, span)
}

/// Normalizes the constant to a value or an error if possible.
#[inline]
pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want this method to translate a ValTree into a ConstValue? For normalization, it may be more interesting to keep the ValTree unchanged, as it is already a legal value.

Copy link
Member Author

@RalfJung RalfJung Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a MIR constant, its normal form is a ConstValue. (Also this matches prior behavior.)

If we leave this as a valtree, then every future call to eval has to do the conversion again. That seems like a lot of wasted effort.

match self.eval(tcx, param_env, None) {
Ok(val) => Self::Val(val, self.ty()),
Err(ErrorHandled::Reported(guar)) => {
Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
}
Err(ErrorHandled::TooGeneric) => self,
}
}

/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
#[inline]
pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
self.try_eval_bits(tcx, param_env, ty)
.unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
pub fn try_eval_scalar(
self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<Scalar> {
self.eval(tcx, param_env, None).ok()?.try_to_scalar()
}

#[inline]
pub fn try_eval_scalar_int(
self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<ScalarInt> {
self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
}

#[inline]
Expand All @@ -2382,59 +2392,38 @@ impl<'tcx> ConstantKind<'tcx> {
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<u128> {
match self {
Self::Ty(ct) => ct.try_eval_bits(tcx, param_env, ty),
Self::Val(val, t) => {
assert_eq!(*t, ty);
let size =
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
val.try_to_bits(size)
}
Self::Unevaluated(uneval, ty) => {
match tcx.const_eval_resolve(param_env, *uneval, None) {
Ok(val) => {
let size = tcx
.layout_of(param_env.with_reveal_all_normalized(tcx).and(*ty))
.ok()?
.size;
val.try_to_bits(size)
}
Err(_) => None,
}
}
}
let int = self.try_eval_scalar_int(tcx, param_env)?;
assert_eq!(self.ty(), ty);
let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
int.to_bits(size).ok()
}

/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
#[inline]
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
match self {
Self::Ty(ct) => ct.try_eval_bool(tcx, param_env),
Self::Val(val, _) => val.try_to_bool(),
Self::Unevaluated(uneval, _) => {
match tcx.const_eval_resolve(param_env, *uneval, None) {
Ok(val) => val.try_to_bool(),
Err(_) => None,
}
}
}
pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
self.try_eval_bits(tcx, param_env, ty)
.unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
}

#[inline]
pub fn try_eval_target_usize(
&self,
self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<u64> {
match self {
Self::Ty(ct) => ct.try_eval_target_usize(tcx, param_env),
Self::Val(val, _) => val.try_to_target_usize(tcx),
Self::Unevaluated(uneval, _) => {
match tcx.const_eval_resolve(param_env, *uneval, None) {
Ok(val) => val.try_to_target_usize(tcx),
Err(_) => None,
}
}
}
self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
}

#[inline]
/// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
self.try_eval_target_usize(tcx, param_env)
.unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
}

#[inline]
pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
}

#[inline]
Expand Down Expand Up @@ -2576,7 +2565,7 @@ impl<'tcx> ConstantKind<'tcx> {
}
}

pub fn from_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
match c.kind() {
ty::ConstKind::Value(valtree) => {
let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
Expand Down Expand Up @@ -2610,6 +2599,11 @@ impl<'tcx> UnevaluatedConst<'tcx> {
pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
UnevaluatedConst { def, args, promoted: Default::default() }
}

#[inline]
pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
UnevaluatedConst::new(instance.def_id(), instance.args)
}
}

/// A collection of projections into user types.
Expand Down Expand Up @@ -2884,7 +2878,7 @@ fn pretty_print_const_value<'tcx>(
}
}
(ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
let n = n.try_to_bits(tcx.data_layout.pointer_size).unwrap();
let n = n.try_to_target_usize(tcx).unwrap();
// cast is ok because we already checked for pointer size (32 or 64 bit) above
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
Expand Down
Loading