Skip to content

Commit

Permalink
Insert satisfying op_to_const bbqing sound here.
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Feb 15, 2019
1 parent 92e4471 commit 0075868
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 48 deletions.
80 changes: 34 additions & 46 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use syntax::ast::Mutability;
use syntax::source_map::{Span, DUMMY_SP};

use crate::interpret::{self,
PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Operand, Immediate, Scalar, Pointer,
PlaceTy, MPlaceTy, OpTy, ImmTy, Scalar, Pointer,
RawConst, ConstValue,
EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
Allocation, AllocId, MemoryKind,
Expand Down Expand Up @@ -62,43 +62,6 @@ pub(crate) fn eval_promoted<'a, 'mir, 'tcx>(
eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env)
}

// FIXME: These two conversion functions are bad hacks. We should just always use allocations.
fn op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
op: OpTy<'tcx>,
) -> EvalResult<'tcx, ty::Const<'tcx>> {
// We do not normalize just any data. Only scalar layout and slices.
let normalized_op = match op.layout.abi {
layout::Abi::Scalar(..) => ecx.try_read_immediate(op)?,
layout::Abi::ScalarPair(..) if op.layout.ty.is_slice() => ecx.try_read_immediate(op)?,
_ => match *op {
Operand::Indirect(mplace) => Err(mplace),
Operand::Immediate(val) => Ok(val)
},
};
let (val, alloc) = match normalized_op {
Err(MemPlace { ptr, align, meta }) => {
// extract alloc-offset pair
assert!(meta.is_none());
let ptr = ptr.to_ptr()?;
let alloc = ecx.memory.get(ptr.alloc_id)?;
assert!(alloc.align >= align);
assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes());
let mut alloc = alloc.clone();
alloc.align = align;
// FIXME shouldn't it be the case that `mark_static_initialized` has already
// interned this? I thought that is the entire point of that `FinishStatic` stuff?
let alloc = ecx.tcx.intern_const_alloc(alloc);
(ConstValue::ByRef, Some((alloc, ptr)))
},
Ok(Immediate::Scalar(x)) =>
(ConstValue::Scalar(x.not_undef()?), None),
Ok(Immediate::ScalarPair(a, b)) =>
(ConstValue::Slice(a.not_undef()?, b.to_usize(ecx)?), None),
};
Ok(ty::Const { val, ty: op.layout.ty, alloc })
}

fn eval_body_and_ecx<'a, 'mir, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
cid: GlobalId<'tcx>,
Expand Down Expand Up @@ -471,18 +434,43 @@ pub fn const_field<'a, 'tcx>(
trace!("const_field: {:?}, {:?}", field, value);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env);
let result = (|| {
// get the operand again
let op = ecx.lazy_const_to_op(ty::LazyConst::Evaluated(value), value.ty)?;
let (alloc, ptr) = value.alloc.expect(
"const_field can only be called on aggregates, which should never be created without
a corresponding allocation",
);
let mplace = MPlaceTy::from_aligned_ptr(ptr, ecx.layout_of(value.ty)?);
// downcast
let down = match variant {
None => op,
Some(variant) => ecx.operand_downcast(op, variant)?
None => mplace,
Some(variant) => ecx.mplace_downcast(mplace, variant)?,
};
// then project
let field = ecx.operand_field(down, field.index() as u64)?;
// and finally move back to the const world, always normalizing because
// this is not called for statics.
op_to_const(&ecx, field)
let field = ecx.mplace_field(down, field.index() as u64)?;
let val = match field.layout.abi {
layout::Abi::Scalar(..) => {
let scalar = ecx.try_read_immediate_from_mplace(field)?.unwrap().to_scalar()?;
ConstValue::Scalar(scalar)
}
layout::Abi::ScalarPair(..) if field.layout.ty.is_slice() => {
let (a, b) = ecx.try_read_immediate_from_mplace(field)?.unwrap().to_scalar_pair()?;
ConstValue::Slice(a, b.to_usize(&ecx)?)
},
_ => ConstValue::ByRef,
};
let field_ptr = field.to_ptr().unwrap();
assert_eq!(
ptr.alloc_id,
field_ptr.alloc_id,
"field access of aggregate moved to different allocation",
);
Ok(ty::Const {
val,
ty: field.layout.ty,
alloc: Some((
alloc,
field_ptr,
)),
})
})();
result.map_err(|error| {
let err = error_to_const_error(&ecx, error);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ pub(super) fn from_known_layout<'tcx>(
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Try reading an immediate in memory; this is interesting particularly for ScalarPair.
/// Returns `None` if the layout does not permit loading this as a value.
pub(super) fn try_read_immediate_from_mplace(
crate fn try_read_immediate_from_mplace(
&self,
mplace: MPlaceTy<'tcx, M::PointerTag>,
) -> EvalResult<'tcx, Option<Immediate<M::PointerTag>>> {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
}

#[inline]
fn from_aligned_ptr(ptr: Pointer<Tag>, layout: TyLayout<'tcx>) -> Self {
crate fn from_aligned_ptr(ptr: Pointer<Tag>, layout: TyLayout<'tcx>) -> Self {

This comment has been minimized.

Copy link
@RalfJung

RalfJung Feb 15, 2019

Member

This breaks privacy barriers I was hoping to uphold :( Basically, there should be no way from outside the place module to create an MPlaceTy with a "bad" layout, at least assuming the input data we get is somewhat consistent.

I'd prefer a const_to_mplace in this module, or so.

MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout }
}

Expand Down

0 comments on commit 0075868

Please sign in to comment.