diff options
Diffstat (limited to 'src/librustc_mir/const_eval.rs')
| -rw-r--r-- | src/librustc_mir/const_eval.rs | 73 |
1 files changed, 49 insertions, 24 deletions
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index b9432997b7e..105856fecc7 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -35,6 +35,16 @@ const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000; /// Should be a power of two for performance reasons. const DETECTOR_SNAPSHOT_PERIOD: isize = 256; +/// Warning: do not use this function if you expect to start interpreting the given `Mir`. +/// The `EvalContext` is only meant to be used to query values from constants and statics. +/// +/// This function is used during const propagation. We cannot use `mk_eval_cx`, because copy +/// propagation happens *during* the computation of the MIR of the current function. So if we +/// tried to call the `optimized_mir` query, we'd get a cycle error because we are (transitively) +/// inside the `optimized_mir` query of the `Instance` given. +/// +/// Since we are looking at the MIR of the function in an abstract manner, we don't have a +/// `ParamEnv` available to us. This function creates a `ParamEnv` for the given instance. pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, @@ -43,12 +53,26 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> { debug!("mk_borrowck_eval_cx: {:?}", instance); let param_env = tcx.param_env(instance.def_id()); + mk_eval_cx_inner(tcx, instance, mir, span, param_env) +} + +/// This is just a helper function to reduce code duplication between `mk_borrowck_eval_cx` and +/// `mk_eval_cx`. Do not call this function directly. +fn mk_eval_cx_inner<'a, 'mir, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: Instance<'tcx>, + mir: &'mir mir::Mir<'tcx>, + span: Span, + param_env: ty::ParamEnv<'tcx>, +) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> { let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new()); - // insert a stack frame so any queries have the correct substs - // cannot use `push_stack_frame`; if we do `const_prop` explodes + // Insert a stack frame so any queries have the correct substs. + // We also avoid all the extra work performed by push_stack_frame, + // like initializing local variables ecx.stack.push(interpret::Frame { block: mir::START_BLOCK, locals: IndexVec::new(), + local_layouts: IndexVec::new(), instance, span, mir, @@ -60,24 +84,23 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>( Ok(ecx) } -pub fn mk_eval_cx<'a, 'tcx>( +/// Warning: do not use this function if you expect to start interpreting the given `Mir`. +/// The `EvalContext` is only meant to be used to do field and index projections into constants for +/// `simd_shuffle` and const patterns in match arms. +/// +/// The function containing the `match` that is currently being analyzed may have generic bounds +/// that inform us about the generic bounds of the constant. E.g. using an associated constant +/// of a function's generic parameter will require knowledge about the bounds on the generic +/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. +fn mk_eval_cx<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> { debug!("mk_eval_cx: {:?}, {:?}", instance, param_env); let span = tcx.def_span(instance.def_id()); - let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new()); - let mir = ecx.load_mir(instance.def)?; - // insert a stack frame so any queries have the correct substs - ecx.push_stack_frame( - instance, - mir.span, - mir, - None, - StackPopCleanup::Goto(None), // never pop - )?; - Ok(ecx) + let mir = tcx.optimized_mir(instance.def.def_id()); + mk_eval_cx_inner(tcx, instance, mir, span, param_env) } pub(crate) fn eval_promoted<'a, 'mir, 'tcx>( @@ -95,7 +118,7 @@ pub fn op_to_const<'tcx>( ecx: &CompileTimeEvalContext<'_, '_, 'tcx>, op: OpTy<'tcx>, may_normalize: bool, -) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { +) -> EvalResult<'tcx, ty::Const<'tcx>> { // We do not normalize just any data. Only scalar layout and fat pointers. let normalize = may_normalize && match op.layout.abi { @@ -134,14 +157,16 @@ pub fn op_to_const<'tcx>( Ok(Immediate::ScalarPair(a, b)) => ConstValue::ScalarPair(a.not_undef()?, b.not_undef()?), }; - Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty)) + Ok(ty::Const { val, ty: op.layout.ty }) } -pub fn const_to_op<'tcx>( + +pub fn lazy_const_to_op<'tcx>( ecx: &CompileTimeEvalContext<'_, '_, 'tcx>, - cnst: &ty::Const<'tcx>, + cnst: ty::LazyConst<'tcx>, + ty: ty::Ty<'tcx>, ) -> EvalResult<'tcx, OpTy<'tcx>> { - let op = ecx.const_value_to_op(cnst.val)?; - Ok(OpTy { op, layout: ecx.layout_of(cnst.ty)? }) + let op = ecx.const_value_to_op(cnst)?; + Ok(OpTy { op, layout: ecx.layout_of(ty)? }) } fn eval_body_and_ecx<'a, 'mir, 'tcx>( @@ -508,13 +533,13 @@ pub fn const_field<'a, 'tcx>( instance: ty::Instance<'tcx>, variant: Option<VariantIdx>, field: mir::Field, - value: &'tcx ty::Const<'tcx>, + value: ty::Const<'tcx>, ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> { trace!("const_field: {:?}, {:?}, {:?}", instance, field, value); let ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let result = (|| { // get the operand again - let op = const_to_op(&ecx, value)?; + let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(value), value.ty)?; // downcast let down = match variant { None => op, @@ -537,11 +562,11 @@ pub fn const_variant_index<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, - val: &'tcx ty::Const<'tcx>, + val: ty::Const<'tcx>, ) -> EvalResult<'tcx, VariantIdx> { trace!("const_variant_index: {:?}, {:?}", instance, val); let ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); - let op = const_to_op(&ecx, val)?; + let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(val), val.ty)?; Ok(ecx.read_discriminant(op)?.1) } |
