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

Change const eval to just return the value #69181

Merged
merged 6 commits into from
Feb 18, 2020
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
3 changes: 2 additions & 1 deletion src/librustc/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};

use crate::hir::map::definitions::DefPathData;
use crate::mir;
use crate::mir::interpret::ConstValue;
use crate::ty::layout::{Align, LayoutError, Size};
use crate::ty::query::TyCtxtAt;
use crate::ty::{self, layout, Ty};
Expand Down Expand Up @@ -40,7 +41,7 @@ CloneTypeFoldableImpls! {
}

pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;

#[derive(Debug)]
pub struct ConstEvalErr<'tcx> {
Expand Down
38 changes: 37 additions & 1 deletion src/librustc/mir/interpret/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt;

use crate::ty::{
layout::{HasDataLayout, Size},
Ty,
ParamEnv, Ty, TyCtxt,
};

use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
Expand Down Expand Up @@ -66,6 +66,32 @@ impl<'tcx> ConstValue<'tcx> {
ConstValue::Scalar(val) => Some(val),
}
}

pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar()?.to_bits(size).ok()
}

pub fn try_to_bits_for_ty(
&self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<u128> {
let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size;
Copy link
Member

Choose a reason for hiding this comment

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

Are there no caveats here? I remember @eddyb saying that "reveal all" is often not the right choice.

Copy link
Member

Choose a reason for hiding this comment

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

Lately I've had to say almost the exact opposite (because some misunderstanding around Reveal::All's ability to cope with polymorphic situations: modulo bugs, it should always work), so I'm not sure what the context was for what you're thinking of.

Note that layout_of, IIRC, uses with_reveal_all internally, so if this does anything, it avoids extra queries.

Copy link
Author

Choose a reason for hiding this comment

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

I used the same logic as Const::try_eval_bits.

self.try_to_bits(size)
}

pub fn from_bool(b: bool) -> Self {
ConstValue::Scalar(Scalar::from_bool(b))
}

pub fn from_u64(i: u64) -> Self {
ConstValue::Scalar(Scalar::from_u64(i))
}

pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self {
ConstValue::Scalar(Scalar::from_machine_usize(i, cx))
}
}

/// A `Scalar` represents an immediate, primitive value existing outside of a
Expand Down Expand Up @@ -287,6 +313,11 @@ impl<'tcx, Tag> Scalar<Tag> {
Scalar::Raw { data: i as u128, size: 8 }
}

#[inline]
pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self {
Self::from_uint(i, cx.data_layout().pointer_size)
}

#[inline]
pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
let i = i.into();
Expand All @@ -306,6 +337,11 @@ impl<'tcx, Tag> Scalar<Tag> {
.unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits()))
}

#[inline]
pub fn from_machine_isize(i: i64, cx: &impl HasDataLayout) -> Self {
Self::from_int(i, cx.data_layout().pointer_size)
}

#[inline]
pub fn from_f32(f: Single) -> Self {
// We trust apfloat to give us properly truncated data.
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ rustc_queries! {
/// Extracts a field of a (variant of a) const.
query const_field(
key: ty::ParamEnvAnd<'tcx, (&'tcx ty::Const<'tcx>, mir::Field)>
) -> &'tcx ty::Const<'tcx> {
) -> ConstValue<'tcx> {
no_force
desc { "extract field of const" }
}
Expand All @@ -531,7 +531,7 @@ rustc_queries! {
desc { "destructure constant" }
}

query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> &'tcx ty::Const<'tcx> {
query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
no_force
desc { "get a &core::panic::Location referring to a span" }
}
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2380,10 +2380,10 @@ impl<'tcx> AdtDef {
let repr_type = self.repr.discr_type();
match tcx.const_eval_poly(expr_did) {
Ok(val) => {
// FIXME: Find the right type and use it instead of `val.ty` here
if let Some(b) = val.try_eval_bits(tcx, param_env, val.ty) {
let ty = repr_type.to_ty(tcx);
if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) {
trace!("discriminants: {} ({:?})", b, repr_type);
Some(Discr { val: b, ty: val.ty })
Some(Discr { val: b, ty })
} else {
info!("invalid enum discriminant: {:#?}", val);
crate::mir::interpret::struct_error(
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife
use crate::middle::stability::{self, DeprecationEntry};
use crate::mir;
use crate::mir::interpret::GlobalId;
use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult};
use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue};
use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit;
use crate::session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
Expand Down
11 changes: 9 additions & 2 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2415,9 +2415,14 @@ pub struct Const<'tcx> {
static_assert_size!(Const<'_>, 48);

impl<'tcx> Const<'tcx> {
#[inline]
pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
tcx.mk_const(Self { val: ConstKind::Value(val), ty })
}

#[inline]
pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self {
tcx.mk_const(Self { val: ConstKind::Value(ConstValue::Scalar(val)), ty })
Self::from_value(tcx, ConstValue::Scalar(val), ty)
}

#[inline]
Expand Down Expand Up @@ -2471,7 +2476,9 @@ impl<'tcx> Const<'tcx> {

// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
tcx.const_eval_resolve(param_env, did, substs, promoted, None).ok()
tcx.const_eval_resolve(param_env, did, substs, promoted, None)
.ok()
.map(|val| Const::from_value(tcx, val, self.ty))
};

match self.val {
Expand Down
8 changes: 3 additions & 5 deletions src/librustc_codegen_llvm/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,9 @@ pub fn codegen_static_initializer(
cx: &CodegenCx<'ll, 'tcx>,
def_id: DefId,
) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> {
let static_ = cx.tcx.const_eval_poly(def_id)?;

let alloc = match static_.val {
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) if offset.bytes() == 0 => alloc,
_ => bug!("static const eval returned {:#?}", static_),
let alloc = match cx.tcx.const_eval_poly(def_id)? {
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
val => bug!("static const eval returned {:#?}", val),
};
Ok((const_alloc_to_llvm(cx, alloc), alloc))
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_llvm/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
.tcx
.const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)
.unwrap();
OperandRef::from_const(self, ty_name).immediate_or_packed_pair(self)
OperandRef::from_const(self, ty_name, ret_ty).immediate_or_packed_pair(self)
}
"init" => {
let ty = substs.type_at(0);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
caller.line as u32,
caller.col_display as u32 + 1,
));
OperandRef::from_const(bx, const_loc)
OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
})
}

Expand Down
30 changes: 20 additions & 10 deletions src/librustc_codegen_ssa/mir/constant.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::mir::operand::OperandRef;
use crate::traits::*;
use rustc::mir;
use rustc::mir::interpret::ErrorHandled;
use rustc::mir::interpret::{ConstValue, ErrorHandled};
use rustc::ty::layout::{self, HasTyCtxt};
use rustc::ty::{self, Ty};
use rustc_index::vec::Idx;
Expand Down Expand Up @@ -30,15 +30,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
_ => {
let val = self.eval_mir_constant(constant)?;
Ok(OperandRef::from_const(bx, val))
let ty = self.monomorphize(&constant.literal.ty);
Ok(OperandRef::from_const(bx, val.clone(), ty))
}
}
}

pub fn eval_mir_constant(
&mut self,
constant: &mir::Constant<'tcx>,
) -> Result<&'tcx ty::Const<'tcx>, ErrorHandled> {
) -> Result<ConstValue<'tcx>, ErrorHandled> {
match constant.literal.val {
ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
let substs = self.monomorphize(&substs);
Expand All @@ -55,7 +56,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
err
})
}
_ => Ok(self.monomorphize(&constant.literal)),
ty::ConstKind::Value(value) => Ok(value),
_ => {
let const_ = self.monomorphize(&constant.literal);
if let ty::ConstKind::Value(value) = const_.val {
Ok(value)
} else {
span_bug!(constant.span, "encountered bad ConstKind in codegen: {:?}", const_);
}
}
}
}

Expand All @@ -65,21 +74,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx: &Bx,
span: Span,
ty: Ty<'tcx>,
constant: Result<&'tcx ty::Const<'tcx>, ErrorHandled>,
constant: Result<ConstValue<'tcx>, ErrorHandled>,
) -> (Bx::Value, Ty<'tcx>) {
constant
.map(|c| {
let field_ty = c.ty.builtin_index().unwrap();
let fields = match c.ty.kind {
.map(|val| {
let field_ty = ty.builtin_index().unwrap();
let fields = match ty.kind {
ty::Array(_, n) => n.eval_usize(bx.tcx(), ty::ParamEnv::reveal_all()),
_ => bug!("invalid simd shuffle type: {}", c.ty),
_ => bug!("invalid simd shuffle type: {}", ty),
};
let c = ty::Const::from_value(bx.tcx(), val, ty);
let values: Vec<_> = (0..fields)
.map(|field| {
let field = bx.tcx().const_field(
ty::ParamEnv::reveal_all().and((&c, mir::Field::new(field as usize))),
);
if let Some(prim) = field.val.try_to_scalar() {
if let Some(prim) = field.try_to_scalar() {
let layout = bx.layout_of(field_ty);
let scalar = match layout.abi {
layout::Abi::Scalar(ref x) => x,
Expand Down
14 changes: 5 additions & 9 deletions src/librustc_codegen_ssa/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::MemFlags;

use rustc::mir;
use rustc::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar};
use rustc::ty;
use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout};
use rustc::ty::Ty;

use std::fmt;

Expand Down Expand Up @@ -66,20 +66,16 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {

pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
val: &'tcx ty::Const<'tcx>,
val: ConstValue<'tcx>,
ty: Ty<'tcx>,
) -> Self {
let layout = bx.layout_of(val.ty);
let layout = bx.layout_of(ty);

if layout.is_zst() {
return OperandRef::new_zst(bx, layout);
}

let val_val = match val.val {
ty::ConstKind::Value(val_val) => val_val,
_ => bug!("encountered bad ConstKind in codegen"),
};

let val = match val_val {
let val = match val {
ConstValue::Scalar(x) => {
let scalar = match layout.abi {
layout::Abi::Scalar(ref x) => x,
Expand Down
15 changes: 5 additions & 10 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub(crate) fn const_field<'tcx>(
variant: Option<VariantIdx>,
field: mir::Field,
value: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
) -> ConstValue<'tcx> {
trace!("const_field: {:?}, {:?}", field, value);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
// get the operand again
Expand All @@ -46,19 +46,13 @@ pub(crate) fn const_field<'tcx>(
pub(crate) fn const_caller_location<'tcx>(
tcx: TyCtxt<'tcx>,
(file, line, col): (Symbol, u32, u32),
) -> &'tcx ty::Const<'tcx> {
) -> ConstValue<'tcx> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

let loc_ty = tcx.caller_location_ty();
let loc_place = ecx.alloc_caller_location(file, line, col);
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false).unwrap();
let loc_const = ty::Const {
ty: loc_ty,
val: ty::ConstKind::Value(ConstValue::Scalar(loc_place.ptr.into())),
};

tcx.mk_const(loc_const)
ConstValue::Scalar(loc_place.ptr.into())
}

// this function uses `unwrap` copiously, because an already validated constant
Expand All @@ -84,7 +78,8 @@ pub(crate) fn destructure_const<'tcx>(
let down = ecx.operand_downcast(op, variant).unwrap();
let fields_iter = (0..field_count).map(|i| {
let field_op = ecx.operand_field(down, i).unwrap();
op_to_const(&ecx, field_op)
let val = op_to_const(&ecx, field_op);
ty::Const::from_value(tcx, val, field_op.layout.ty)
});
let fields = tcx.arena.alloc_from_iter(fields_iter);

Expand Down
18 changes: 7 additions & 11 deletions src/librustc_mir/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
pub(super) fn op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, 'tcx>,
op: OpTy<'tcx>,
) -> &'tcx ty::Const<'tcx> {
) -> ConstValue<'tcx> {
// We do not have value optimizations for everything.
// Only scalars and slices, since they are very common.
// Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
Expand Down Expand Up @@ -144,7 +144,7 @@ pub(super) fn op_to_const<'tcx>(
ConstValue::Scalar(Scalar::zst())
}
};
let val = match immediate {
match immediate {
Ok(mplace) => to_const_value(mplace),
// see comment on `let try_as_immediate` above
Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
Expand All @@ -166,8 +166,7 @@ pub(super) fn op_to_const<'tcx>(
let len: usize = len.try_into().unwrap();
ConstValue::Slice { data, start, end: start + len }
}
};
ecx.tcx.mk_const(ty::Const { val: ty::ConstKind::Value(val), ty: op.layout.ty })
}
}

fn validate_and_turn_into_const<'tcx>(
Expand Down Expand Up @@ -195,13 +194,10 @@ fn validate_and_turn_into_const<'tcx>(
// whether they become immediates.
if is_static || cid.promoted.is_some() {
let ptr = mplace.ptr.assert_ptr();
Ok(tcx.mk_const(ty::Const {
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
offset: ptr.offset,
}),
ty: mplace.layout.ty,
}))
Ok(ConstValue::ByRef {
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
offset: ptr.offset,
})
} else {
Ok(op_to_const(&ecx, mplace.into()))
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn const_eval(
&self,
gid: GlobalId<'tcx>,
ty: Ty<'tcx>,
Copy link
Member

Choose a reason for hiding this comment

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

So with this, the gid is not sufficient to determine the type of the constant it identifies? That seems somewhat odd to me, given that it is enough to determine the value.

Copy link
Member

@eddyb eddyb Feb 15, 2020

Choose a reason for hiding this comment

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

This is pretty much down to lifetime parametricity/polymorphism.
Basically the entire type is determinable except lifetimes.

And because lifetimes shouldn't affect const-evaluation, and to maximize caching, we want to erase them, but we also don't want the lifetime-erased type to leak back into e.g. borrow-checking.

Copy link
Member

Choose a reason for hiding this comment

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

This makes me think it would be nice to provide a "lifetime substitution" instead of the full type (and compute the non-lifetime parts from GlobalId), but that might need a lot more infrastructure than this PR (in general we might want lifetimes slots to be placeholders for e.g. MIR borrowck to work with).

cc @nikomatsakis (who I'm sure has had this idea before)

Copy link
Member

Choose a reason for hiding this comment

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

Basically the entire type is determinable except lifetimes.

Miri doesn't care about lifetimes though.

I am just worried about the additional failure mode this introduces -- generally in Miri we track the type of values so when someone uses e.g. an i32 as i64 we get an ICE (and this has caught quite a few bugs). But here we just believe the type the caller tells us.

Copy link
Member

Choose a reason for hiding this comment

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

Miri doesn't care about lifetimes though.

The caller does. Or is const_eval an internal API? (It's sometimes hard to tell on GitHub).

But here we just believe the type the caller tells us.

If this is not internal, and this matters, we could assert that the type is identical after erasing the one that might have lifetimes.

If it is internal, then yeah, using whatever lifetime-erased type can be computed from GlobalId would be fine.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, that is exactly why I asked for the query impls to be moved out of rustc_mir::interp way back when.

Copy link
Author

Choose a reason for hiding this comment

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

The gid should be sufficient to determine the type, at least a type that is good enough for miri, though getting the type of a promoted globals seems like it would be a little bit difficult. I decided as this is an internal API and as both call sites has the type available, it would just be easier to pass it through.

Copy link
Member

Choose a reason for hiding this comment

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

One call site doesn't really have the type available... the one where you used dest.layout.ty, previously this was checking that the type of the constant was the same as the type of the place it was put into. Now there is no check any more, you are just assuming this to be the case.

Copy link
Author

Choose a reason for hiding this comment

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

I will change that call site to actually use the proper type, which as it's for intrinsics the type of them are known.

Copy link
Member

Choose a reason for hiding this comment

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

@Skinny121 did you ever get around to actually do this? Doesn't look like it, but I might have missed it.

) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
// and thus don't care about the parameter environment. While we could just use
Expand All @@ -777,7 +778,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not
// return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call
// `ecx.const_eval`.
self.eval_const_to_op(val, None)
let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
self.eval_const_to_op(&const_, None)
}

pub fn const_eval_raw(
Expand Down
Loading