interpret: use Either over Result when it is not representing an error condition

This commit is contained in:
Ralf Jung 2022-11-18 10:18:32 +01:00
parent 83356b78c4
commit 4101889786
17 changed files with 130 additions and 98 deletions

View File

@ -3330,6 +3330,7 @@ dependencies = [
name = "rustc_const_eval" name = "rustc_const_eval"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"either",
"rustc_apfloat", "rustc_apfloat",
"rustc_ast", "rustc_ast",
"rustc_attr", "rustc_attr",
@ -3820,6 +3821,7 @@ dependencies = [
name = "rustc_mir_build" name = "rustc_mir_build"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"either",
"rustc_apfloat", "rustc_apfloat",
"rustc_arena", "rustc_arena",
"rustc_ast", "rustc_ast",
@ -3866,6 +3868,7 @@ name = "rustc_mir_transform"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"coverage_test_macros", "coverage_test_macros",
"either",
"itertools", "itertools",
"rustc_ast", "rustc_ast",
"rustc_attr", "rustc_attr",

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
tracing = "0.1" tracing = "0.1"
either = "1"
rustc_apfloat = { path = "../rustc_apfloat" } rustc_apfloat = { path = "../rustc_apfloat" }
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" } rustc_attr = { path = "../rustc_attr" }

View File

@ -1,10 +1,7 @@
use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr}; use std::borrow::Cow;
use crate::interpret::eval_nullary_intrinsic; use std::convert::TryInto;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, use either::{Left, Right};
Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy,
RefTracking, StackPopCleanup,
};
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_middle::mir; use rustc_middle::mir;
@ -16,8 +13,14 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_target::abi::{self, Abi}; use rustc_target::abi::{self, Abi};
use std::borrow::Cow;
use std::convert::TryInto; use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr};
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy,
RefTracking, StackPopCleanup,
};
const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \ const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \
so this check might be overzealous. Please open an issue on the rustc \ so this check might be overzealous. Please open an issue on the rustc \
@ -135,14 +138,14 @@ pub(super) fn op_to_const<'tcx>(
_ => false, _ => false,
}; };
let immediate = if try_as_immediate { let immediate = if try_as_immediate {
Err(ecx.read_immediate(op).expect("normalization works on validated constants")) Right(ecx.read_immediate(op).expect("normalization works on validated constants"))
} else { } else {
// It is guaranteed that any non-slice scalar pair is actually ByRef here. // It is guaranteed that any non-slice scalar pair is actually ByRef here.
// When we come back from raw const eval, we are always by-ref. The only way our op here is // When we come back from raw const eval, we are always by-ref. The only way our op here is
// by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we // by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we
// "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
// structs containing such. // structs containing such.
op.try_as_mplace() op.as_mplace_or_imm()
}; };
debug!(?immediate); debug!(?immediate);
@ -168,9 +171,9 @@ pub(super) fn op_to_const<'tcx>(
} }
}; };
match immediate { match immediate {
Ok(ref mplace) => to_const_value(mplace), Left(ref mplace) => to_const_value(mplace),
// see comment on `let try_as_immediate` above // see comment on `let try_as_immediate` above
Err(imm) => match *imm { Right(imm) => match *imm {
_ if imm.layout.is_zst() => ConstValue::ZeroSized, _ if imm.layout.is_zst() => ConstValue::ZeroSized,
Immediate::Scalar(x) => ConstValue::Scalar(x), Immediate::Scalar(x) => ConstValue::Scalar(x),
Immediate::ScalarPair(a, b) => { Immediate::ScalarPair(a, b) => {

View File

@ -2,6 +2,8 @@ use std::cell::Cell;
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use either::{Either, Left, Right};
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_middle::mir; use rustc_middle::mir;
@ -121,13 +123,12 @@ pub struct Frame<'mir, 'tcx, Prov: Provenance = AllocId, Extra = ()> {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Current position within the function // Current position within the function
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// If this is `Err`, we are not currently executing any particular statement in /// If this is `Right`, we are not currently executing any particular statement in
/// this frame (can happen e.g. during frame initialization, and during unwinding on /// this frame (can happen e.g. during frame initialization, and during unwinding on
/// frames without cleanup code). /// frames without cleanup code).
/// We basically abuse `Result` as `Either`.
/// ///
/// Needs to be public because ConstProp does unspeakable things to it. /// Needs to be public because ConstProp does unspeakable things to it.
pub loc: Result<mir::Location, Span>, pub loc: Either<mir::Location, Span>,
} }
/// What we store about a frame in an interpreter backtrace. /// What we store about a frame in an interpreter backtrace.
@ -227,25 +228,24 @@ impl<'mir, 'tcx, Prov: Provenance> Frame<'mir, 'tcx, Prov> {
impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> {
/// Get the current location within the Frame. /// Get the current location within the Frame.
/// ///
/// If this is `Err`, we are not currently executing any particular statement in /// If this is `Left`, we are not currently executing any particular statement in
/// this frame (can happen e.g. during frame initialization, and during unwinding on /// this frame (can happen e.g. during frame initialization, and during unwinding on
/// frames without cleanup code). /// frames without cleanup code).
/// We basically abuse `Result` as `Either`.
/// ///
/// Used by priroda. /// Used by priroda.
pub fn current_loc(&self) -> Result<mir::Location, Span> { pub fn current_loc(&self) -> Either<mir::Location, Span> {
self.loc self.loc
} }
/// Return the `SourceInfo` of the current instruction. /// Return the `SourceInfo` of the current instruction.
pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { pub fn current_source_info(&self) -> Option<&mir::SourceInfo> {
self.loc.ok().map(|loc| self.body.source_info(loc)) self.loc.left().map(|loc| self.body.source_info(loc))
} }
pub fn current_span(&self) -> Span { pub fn current_span(&self) -> Span {
match self.loc { match self.loc {
Ok(loc) => self.body.source_info(loc).span, Left(loc) => self.body.source_info(loc).span,
Err(span) => span, Right(span) => span,
} }
} }
} }
@ -679,7 +679,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// first push a stack frame so we have access to the local substs // first push a stack frame so we have access to the local substs
let pre_frame = Frame { let pre_frame = Frame {
body, body,
loc: Err(body.span), // Span used for errors caused during preamble. loc: Right(body.span), // Span used for errors caused during preamble.
return_to_block, return_to_block,
return_place: return_place.clone(), return_place: return_place.clone(),
// empty local array, we fill it in below, after we are inside the stack frame and // empty local array, we fill it in below, after we are inside the stack frame and
@ -713,7 +713,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// done // done
self.frame_mut().locals = locals; self.frame_mut().locals = locals;
M::after_stack_push(self)?; M::after_stack_push(self)?;
self.frame_mut().loc = Ok(mir::Location::START); self.frame_mut().loc = Left(mir::Location::START);
let span = info_span!("frame", "{}", instance); let span = info_span!("frame", "{}", instance);
self.frame_mut().tracing_span.enter(span); self.frame_mut().tracing_span.enter(span);
@ -724,7 +724,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Jump to the given block. /// Jump to the given block.
#[inline] #[inline]
pub fn go_to_block(&mut self, target: mir::BasicBlock) { pub fn go_to_block(&mut self, target: mir::BasicBlock) {
self.frame_mut().loc = Ok(mir::Location { block: target, statement_index: 0 }); self.frame_mut().loc = Left(mir::Location { block: target, statement_index: 0 });
} }
/// *Return* to the given `target` basic block. /// *Return* to the given `target` basic block.
@ -750,8 +750,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// unwinding, and doing so is UB. /// unwinding, and doing so is UB.
pub fn unwind_to_block(&mut self, target: StackPopUnwind) -> InterpResult<'tcx> { pub fn unwind_to_block(&mut self, target: StackPopUnwind) -> InterpResult<'tcx> {
self.frame_mut().loc = match target { self.frame_mut().loc = match target {
StackPopUnwind::Cleanup(block) => Ok(mir::Location { block, statement_index: 0 }), StackPopUnwind::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
StackPopUnwind::Skip => Err(self.frame_mut().body.span), StackPopUnwind::Skip => Right(self.frame_mut().body.span),
StackPopUnwind::NotAllowed => { StackPopUnwind::NotAllowed => {
throw_ub_format!("unwinding past a stack frame that does not allow unwinding") throw_ub_format!("unwinding past a stack frame that does not allow unwinding")
} }
@ -783,8 +783,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
assert_eq!( assert_eq!(
unwinding, unwinding,
match self.frame().loc { match self.frame().loc {
Ok(loc) => self.body().basic_blocks[loc.block].is_cleanup, Left(loc) => self.body().basic_blocks[loc.block].is_cleanup,
Err(_) => true, Right(_) => true,
} }
); );
if unwinding && self.frame_idx() == 0 { if unwinding && self.frame_idx() == 0 {

View File

@ -19,8 +19,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
// Assert that the frame we look at is actually executing code currently // Assert that the frame we look at is actually executing code currently
// (`loc` is `Err` when we are unwinding and the frame does not require cleanup). // (`loc` is `Right` when we are unwinding and the frame does not require cleanup).
let loc = frame.loc.unwrap(); let loc = frame.loc.left().unwrap();
// This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all
// (such as `box`). Use the normal span by default. // (such as `box`). Use the normal span by default.

View File

@ -1,6 +1,8 @@
//! Functions concerning immediate values and operands, and reading from operands. //! Functions concerning immediate values and operands, and reading from operands.
//! All high-level functions to read from memory work on operands as sources. //! All high-level functions to read from memory work on operands as sources.
use either::{Either, Left, Right};
use rustc_hir::def::Namespace; use rustc_hir::def::Namespace;
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
@ -261,9 +263,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
layout: TyAndLayout<'tcx>, layout: TyAndLayout<'tcx>,
cx: &impl HasDataLayout, cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> { ) -> InterpResult<'tcx, Self> {
match self.try_as_mplace() { match self.as_mplace_or_imm() {
Ok(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()), Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
Err(imm) => { Right(imm) => {
assert!( assert!(
matches!(*imm, Immediate::Uninit), matches!(*imm, Immediate::Uninit),
"Scalar/ScalarPair cannot be offset into" "Scalar/ScalarPair cannot be offset into"
@ -353,8 +355,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Try returning an immediate for the operand. If the layout does not permit loading this as an /// Try returning an immediate for the operand. If the layout does not permit loading this as an
/// immediate, return where in memory we can find the data. /// immediate, return where in memory we can find the data.
/// Note that for a given layout, this operation will either always fail or always /// Note that for a given layout, this operation will either always return Left or Right!
/// succeed! Whether it succeeds depends on whether the layout can be represented /// succeed! Whether it returns Left depends on whether the layout can be represented
/// in an `Immediate`, not on which data is stored there currently. /// in an `Immediate`, not on which data is stored there currently.
/// ///
/// This is an internal function that should not usually be used; call `read_immediate` instead. /// This is an internal function that should not usually be used; call `read_immediate` instead.
@ -362,16 +364,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn read_immediate_raw( pub fn read_immediate_raw(
&self, &self,
src: &OpTy<'tcx, M::Provenance>, src: &OpTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::Provenance>, MPlaceTy<'tcx, M::Provenance>>> { ) -> InterpResult<'tcx, Either<ImmTy<'tcx, M::Provenance>, MPlaceTy<'tcx, M::Provenance>>> {
Ok(match src.try_as_mplace() { Ok(match src.as_mplace_or_imm() {
Ok(ref mplace) => { Left(ref mplace) => {
if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? { if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? {
Ok(val) Left(val)
} else { } else {
Err(*mplace) Right(*mplace)
} }
} }
Err(val) => Ok(val), Right(val) => Left(val),
}) })
} }
@ -390,7 +392,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) { ) {
span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty); span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty);
} }
let imm = self.read_immediate_raw(op)?.unwrap(); let imm = self.read_immediate_raw(op)?.left().unwrap();
if matches!(*imm, Immediate::Uninit) { if matches!(*imm, Immediate::Uninit) {
throw_ub!(InvalidUninitBytes(None)); throw_ub!(InvalidUninitBytes(None));
} }
@ -432,9 +434,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Basically we just transmute this place into an array following simd_size_and_type. // Basically we just transmute this place into an array following simd_size_and_type.
// This only works in memory, but repr(simd) types should never be immediates anyway. // This only works in memory, but repr(simd) types should never be immediates anyway.
assert!(op.layout.ty.is_simd()); assert!(op.layout.ty.is_simd());
match op.try_as_mplace() { match op.as_mplace_or_imm() {
Ok(mplace) => self.mplace_to_simd(&mplace), Left(mplace) => self.mplace_to_simd(&mplace),
Err(imm) => match *imm { Right(imm) => match *imm {
Immediate::Uninit => { Immediate::Uninit => {
throw_ub!(InvalidUninitBytes(None)) throw_ub!(InvalidUninitBytes(None))
} }

View File

@ -2,6 +2,8 @@
//! into a place. //! into a place.
//! All high-level functions to write to memory work on places as destinations. //! All high-level functions to write to memory work on places as destinations.
use either::{Either, Left, Right};
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::ty; use rustc_middle::ty;
@ -252,36 +254,36 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
// These are defined here because they produce a place. // These are defined here because they produce a place.
impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
#[inline(always)] #[inline(always)]
pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> { pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
match **self { match **self {
Operand::Indirect(mplace) => { Operand::Indirect(mplace) => {
Ok(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() }) Left(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() })
} }
Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), Operand::Immediate(imm) => Right(ImmTy::from_immediate(imm, self.layout)),
} }
} }
#[inline(always)] #[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
self.try_as_mplace().unwrap() self.as_mplace_or_imm().left().unwrap()
} }
} }
impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
/// A place is either an mplace or some local. /// A place is either an mplace or some local.
#[inline] #[inline]
pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> { pub fn as_mplace_or_local(&self) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> {
match **self { match **self {
Place::Ptr(mplace) => Ok(MPlaceTy { mplace, layout: self.layout, align: self.align }), Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
Place::Local { frame, local } => Err((frame, local)), Place::Local { frame, local } => Right((frame, local)),
} }
} }
#[inline(always)] #[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
self.try_as_mplace().unwrap() self.as_mplace_or_local().left().unwrap()
} }
} }
@ -569,9 +571,9 @@ where
} }
pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
let mplace = match dest.try_as_mplace() { let mplace = match dest.as_mplace_or_local() {
Ok(mplace) => mplace, Left(mplace) => mplace,
Err((frame, local)) => { Right((frame, local)) => {
match M::access_local_mut(self, frame, local)? { match M::access_local_mut(self, frame, local)? {
Operand::Immediate(local) => { Operand::Immediate(local) => {
*local = Immediate::Uninit; *local = Immediate::Uninit;
@ -639,7 +641,7 @@ where
// Let us see if the layout is simple so we take a shortcut, // Let us see if the layout is simple so we take a shortcut,
// avoid force_allocation. // avoid force_allocation.
let src = match self.read_immediate_raw(src)? { let src = match self.read_immediate_raw(src)? {
Ok(src_val) => { Left(src_val) => {
// FIXME(const_prop): Const-prop can possibly evaluate an // FIXME(const_prop): Const-prop can possibly evaluate an
// unsized copy operation when it thinks that the type is // unsized copy operation when it thinks that the type is
// actually sized, due to a trivially false where-clause // actually sized, due to a trivially false where-clause
@ -669,7 +671,7 @@ where
) )
}; };
} }
Err(mplace) => mplace, Right(mplace) => mplace,
}; };
// Slow path, this does not fit into an immediate. Just memcpy. // Slow path, this does not fit into an immediate. Just memcpy.
trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);

View File

@ -7,6 +7,8 @@
//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually //! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
//! implement the logic on OpTy, and MPlaceTy calls that. //! implement the logic on OpTy, and MPlaceTy calls that.
use either::{Left, Right};
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
@ -84,13 +86,13 @@ where
base: &OpTy<'tcx, M::Provenance>, base: &OpTy<'tcx, M::Provenance>,
field: usize, field: usize,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
let base = match base.try_as_mplace() { let base = match base.as_mplace_or_imm() {
Ok(ref mplace) => { Left(ref mplace) => {
// We can reuse the mplace field computation logic for indirect operands. // We can reuse the mplace field computation logic for indirect operands.
let field = self.mplace_field(mplace, field)?; let field = self.mplace_field(mplace, field)?;
return Ok(field.into()); return Ok(field.into());
} }
Err(value) => value, Right(value) => value,
}; };
let field_layout = base.layout.field(self, field); let field_layout = base.layout.field(self, field);

View File

@ -2,6 +2,8 @@
//! //!
//! The main entry point is the `step` method. //! The main entry point is the `step` method.
use either::Either;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
@ -46,7 +48,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(false); return Ok(false);
} }
let Ok(loc) = self.frame().loc else { let Either::Left(loc) = self.frame().loc else {
// We are unwinding and this fn has no cleanup code. // We are unwinding and this fn has no cleanup code.
// Just go on unwinding. // Just go on unwinding.
trace!("unwinding: skipping frame"); trace!("unwinding: skipping frame");
@ -61,7 +63,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Make sure we are not updating `statement_index` of the wrong frame. // Make sure we are not updating `statement_index` of the wrong frame.
assert_eq!(old_frames, self.frame_idx()); assert_eq!(old_frames, self.frame_idx());
// Advance the program counter. // Advance the program counter.
self.frame_mut().loc.as_mut().unwrap().statement_index += 1; self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1;
return Ok(true); return Ok(true);
} }
@ -305,7 +307,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.eval_terminator(terminator)?; self.eval_terminator(terminator)?;
if !self.stack().is_empty() { if !self.stack().is_empty() {
if let Ok(loc) = self.frame().loc { if let Either::Left(loc) = self.frame().loc {
info!("// executing {:?}", loc.block); info!("// executing {:?}", loc.block);
} }
} }

View File

@ -8,6 +8,8 @@ use std::convert::TryFrom;
use std::fmt::{Display, Write}; use std::fmt::{Display, Write};
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use either::{Left, Right};
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
@ -852,9 +854,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
return Ok(()); return Ok(());
} }
// Now that we definitely have a non-ZST array, we know it lives in memory. // Now that we definitely have a non-ZST array, we know it lives in memory.
let mplace = match op.try_as_mplace() { let mplace = match op.as_mplace_or_imm() {
Ok(mplace) => mplace, Left(mplace) => mplace,
Err(imm) => match *imm { Right(imm) => match *imm {
Immediate::Uninit => Immediate::Uninit =>
throw_validation_failure!(self.path, { "uninitialized bytes" }), throw_validation_failure!(self.path, { "uninitialized bytes" }),
Immediate::Scalar(..) | Immediate::ScalarPair(..) => Immediate::Scalar(..) | Immediate::ScalarPair(..) =>

View File

@ -11,6 +11,8 @@ use std::hash;
use std::ops::Range; use std::ops::Range;
use std::ptr; use std::ptr;
use either::{Left, Right};
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::intern::Interned; use rustc_data_structures::intern::Interned;
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
@ -503,11 +505,11 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
// `to_bits_or_ptr_internal` is the right method because we just want to store this data // `to_bits_or_ptr_internal` is the right method because we just want to store this data
// as-is into memory. // as-is into memory.
let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? { let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? {
Err(val) => { Right(ptr) => {
let (provenance, offset) = val.into_parts(); let (provenance, offset) = ptr.into_parts();
(u128::from(offset.bytes()), Some(provenance)) (u128::from(offset.bytes()), Some(provenance))
} }
Ok(data) => (data, None), Left(data) => (data, None),
}; };
let endian = cx.data_layout().endian; let endian = cx.data_layout().endian;

View File

@ -1,6 +1,8 @@
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use either::{Either, Left, Right};
use rustc_apfloat::{ use rustc_apfloat::{
ieee::{Double, Single}, ieee::{Double, Single},
Float, Float,
@ -293,10 +295,10 @@ impl<Prov> Scalar<Prov> {
pub fn to_bits_or_ptr_internal( pub fn to_bits_or_ptr_internal(
self, self,
target_size: Size, target_size: Size,
) -> Result<Result<u128, Pointer<Prov>>, ScalarSizeMismatch> { ) -> Result<Either<u128, Pointer<Prov>>, ScalarSizeMismatch> {
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
Ok(match self { Ok(match self {
Scalar::Int(int) => Ok(int.to_bits(target_size).map_err(|size| { Scalar::Int(int) => Left(int.to_bits(target_size).map_err(|size| {
ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() } ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() }
})?), })?),
Scalar::Ptr(ptr, sz) => { Scalar::Ptr(ptr, sz) => {
@ -306,7 +308,7 @@ impl<Prov> Scalar<Prov> {
data_size: sz.into(), data_size: sz.into(),
}); });
} }
Err(ptr) Right(ptr)
} }
}) })
} }
@ -318,8 +320,8 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
.to_bits_or_ptr_internal(cx.pointer_size()) .to_bits_or_ptr_internal(cx.pointer_size())
.map_err(|s| err_ub!(ScalarSizeMismatch(s)))? .map_err(|s| err_ub!(ScalarSizeMismatch(s)))?
{ {
Err(ptr) => Ok(ptr.into()), Right(ptr) => Ok(ptr.into()),
Ok(bits) => { Left(bits) => {
let addr = u64::try_from(bits).unwrap(); let addr = u64::try_from(bits).unwrap();
Ok(Pointer::from_addr(addr)) Ok(Pointer::from_addr(addr))
} }

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
rustc_arena = { path = "../rustc_arena" } rustc_arena = { path = "../rustc_arena" }
tracing = "0.1" tracing = "0.1"
either = "1"
rustc_middle = { path = "../rustc_middle" } rustc_middle = { path = "../rustc_middle" }
rustc_apfloat = { path = "../rustc_apfloat" } rustc_apfloat = { path = "../rustc_apfloat" }
rustc_attr = { path = "../rustc_attr" } rustc_attr = { path = "../rustc_attr" }

View File

@ -42,16 +42,17 @@
//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see //! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see
//! [`SplitVarLenSlice`]. //! [`SplitVarLenSlice`].
use self::Constructor::*; use std::cell::Cell;
use self::SliceKind::*; use std::cmp::{self, max, min, Ordering};
use std::fmt;
use std::iter::{once, IntoIterator};
use std::ops::RangeInclusive;
use super::compare_const_vals; use smallvec::{smallvec, SmallVec};
use super::usefulness::{MatchCheckCtxt, PatCtxt};
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_index::vec::Idx;
use rustc_hir::{HirId, RangeEnd}; use rustc_hir::{HirId, RangeEnd};
use rustc_index::vec::Idx;
use rustc_middle::mir::{self, Field}; use rustc_middle::mir::{self, Field};
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
@ -61,12 +62,11 @@ use rustc_session::lint;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{Integer, Size, VariantIdx}; use rustc_target::abi::{Integer, Size, VariantIdx};
use smallvec::{smallvec, SmallVec}; use self::Constructor::*;
use std::cell::Cell; use self::SliceKind::*;
use std::cmp::{self, max, min, Ordering};
use std::fmt; use super::compare_const_vals;
use std::iter::{once, IntoIterator}; use super::usefulness::{MatchCheckCtxt, PatCtxt};
use std::ops::RangeInclusive;
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
@ -147,7 +147,9 @@ impl IntRange {
// straight to the result, after doing a bit of checking. (We // straight to the result, after doing a bit of checking. (We
// could remove this branch and just fall through, which // could remove this branch and just fall through, which
// is more general but much slower.) // is more general but much slower.)
if let Ok(Ok(bits)) = scalar.to_bits_or_ptr_internal(target_size) { if let either::Left(bits) =
scalar.to_bits_or_ptr_internal(target_size).unwrap()
{
return Some(bits); return Some(bits);
} else { } else {
return None; return None;

View File

@ -9,6 +9,7 @@ edition = "2021"
itertools = "0.10.1" itertools = "0.10.1"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1" tracing = "0.1"
either = "1"
rustc_ast = { path = "../rustc_ast" } rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" } rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }

View File

@ -3,6 +3,8 @@
use std::cell::Cell; use std::cell::Cell;
use either::Left;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
@ -429,7 +431,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// Try to read the local as an immediate so that if it is representable as a scalar, we can // Try to read the local as an immediate so that if it is representable as a scalar, we can
// handle it as such, but otherwise, just return the value as is. // handle it as such, but otherwise, just return the value as is.
Some(match self.ecx.read_immediate_raw(&op) { Some(match self.ecx.read_immediate_raw(&op) {
Ok(Ok(imm)) => imm.into(), Ok(Left(imm)) => imm.into(),
_ => op, _ => op,
}) })
} }
@ -743,7 +745,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// FIXME> figure out what to do when read_immediate_raw fails // FIXME> figure out what to do when read_immediate_raw fails
let imm = self.use_ecx(|this| this.ecx.read_immediate_raw(value)); let imm = self.use_ecx(|this| this.ecx.read_immediate_raw(value));
if let Some(Ok(imm)) = imm { if let Some(Left(imm)) = imm {
match *imm { match *imm {
interpret::Immediate::Scalar(scalar) => { interpret::Immediate::Scalar(scalar) => {
*rval = Rvalue::Use(self.operand_from_scalar( *rval = Rvalue::Use(self.operand_from_scalar(

View File

@ -1,10 +1,10 @@
//! Propagates constants for early reporting of statically known //! Propagates constants for early reporting of statically known
//! assertion failures //! assertion failures
use crate::const_prop::CanConstProp; use std::cell::Cell;
use crate::const_prop::ConstPropMachine;
use crate::const_prop::ConstPropMode; use either::{Left, Right};
use crate::MirLint;
use rustc_const_eval::interpret::Immediate; use rustc_const_eval::interpret::Immediate;
use rustc_const_eval::interpret::{ use rustc_const_eval::interpret::{
self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup, self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup,
@ -26,12 +26,17 @@ use rustc_session::lint;
use rustc_span::Span; use rustc_span::Span;
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
use std::cell::Cell;
use crate::const_prop::CanConstProp;
use crate::const_prop::ConstPropMachine;
use crate::const_prop::ConstPropMode;
use crate::MirLint;
/// The maximum number of bytes that we'll allocate space for a local or the return value. /// The maximum number of bytes that we'll allocate space for a local or the return value.
/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
/// Severely regress performance. /// Severely regress performance.
const MAX_ALLOC_LIMIT: u64 = 1024; const MAX_ALLOC_LIMIT: u64 = 1024;
pub struct ConstProp; pub struct ConstProp;
impl<'tcx> MirLint<'tcx> for ConstProp { impl<'tcx> MirLint<'tcx> for ConstProp {
@ -243,7 +248,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// Try to read the local as an immediate so that if it is representable as a scalar, we can // Try to read the local as an immediate so that if it is representable as a scalar, we can
// handle it as such, but otherwise, just return the value as is. // handle it as such, but otherwise, just return the value as is.
Some(match self.ecx.read_immediate_raw(&op) { Some(match self.ecx.read_immediate_raw(&op) {
Ok(Ok(imm)) => imm.into(), Ok(Left(imm)) => imm.into(),
_ => op, _ => op,
}) })
} }
@ -266,7 +271,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
{ {
// Overwrite the PC -- whatever the interpreter does to it does not make any sense anyway. // Overwrite the PC -- whatever the interpreter does to it does not make any sense anyway.
self.ecx.frame_mut().loc = Err(source_info.span); self.ecx.frame_mut().loc = Right(source_info.span);
match f(self) { match f(self) {
Ok(val) => Some(val), Ok(val) => Some(val),
Err(error) => { Err(error) => {