Introduce Operand::RuntimeChecks.
This commit is contained in:
parent
1a227bd47f
commit
6319bee585
45 changed files with 167 additions and 148 deletions
|
|
@ -764,6 +764,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
{
|
||||
// Just point to the function, to reduce the chance of overlapping spans.
|
||||
let function_span = match func {
|
||||
Operand::RuntimeChecks(_) => span,
|
||||
Operand::Constant(c) => c.span,
|
||||
Operand::Copy(place) | Operand::Move(place) => {
|
||||
if let Some(l) = place.as_local() {
|
||||
|
|
@ -809,6 +810,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
{
|
||||
// Just point to the function, to reduce the chance of overlapping spans.
|
||||
let function_span = match func {
|
||||
Operand::RuntimeChecks(_) => span,
|
||||
Operand::Constant(c) => c.span,
|
||||
Operand::Copy(place) | Operand::Move(place) => {
|
||||
if let Some(l) = place.as_local() {
|
||||
|
|
|
|||
|
|
@ -1696,7 +1696,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
_ => propagate_closure_used_mut_place(self, place),
|
||||
}
|
||||
}
|
||||
Operand::Constant(..) => {}
|
||||
Operand::Constant(..) | Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1747,7 +1747,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
state,
|
||||
);
|
||||
}
|
||||
Operand::Constant(_) => {}
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
Operand::Constant(_) => {}
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1023,7 +1023,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
// element, so we require the `Copy` trait.
|
||||
if len.try_to_target_usize(tcx).is_none_or(|len| len > 1) {
|
||||
match operand {
|
||||
Operand::Copy(..) | Operand::Constant(..) => {
|
||||
Operand::Copy(..) | Operand::Constant(..) | Operand::RuntimeChecks(_) => {
|
||||
// These are always okay: direct use of a const, or a value that can
|
||||
// evidently be copied.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use rustc_ast::InlineAsmOptions;
|
|||
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::layout::FnAbiOf;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{ScalarInt, TypeVisitableExt};
|
||||
use rustc_session::config::OutputFilenames;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
|
|
@ -1039,6 +1039,12 @@ pub(crate) fn codegen_operand<'tcx>(
|
|||
cplace.to_cvalue(fx)
|
||||
}
|
||||
Operand::Constant(const_) => crate::constant::codegen_constant_operand(fx, const_),
|
||||
Operand::RuntimeChecks(checks) => {
|
||||
let int = checks.value(fx.tcx.sess);
|
||||
let int = ScalarInt::try_from_uint(int, Size::from_bits(1)).unwrap();
|
||||
let layout = fx.layout_of(fx.tcx.types.bool);
|
||||
return CValue::const_val(fx, layout, int);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -215,11 +215,6 @@ pub(crate) fn codegen_const_value<'tcx>(
|
|||
CValue::by_val(val, layout)
|
||||
}
|
||||
},
|
||||
ConstValue::RuntimeChecks(checks) => {
|
||||
let int = checks.value(fx.tcx.sess);
|
||||
let int = ScalarInt::try_from_uint(int, Size::from_bits(1)).unwrap();
|
||||
return CValue::const_val(fx, layout, int);
|
||||
}
|
||||
ConstValue::Indirect { alloc_id, offset } => CValue::by_ref(
|
||||
Pointer::new(pointer_for_allocation(fx, alloc_id))
|
||||
.offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
|
||||
|
|
@ -545,6 +540,10 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
|
|||
operand: &Operand<'tcx>,
|
||||
) -> Option<ScalarInt> {
|
||||
match operand {
|
||||
Operand::RuntimeChecks(checks) => {
|
||||
let int = checks.value(fx.tcx.sess);
|
||||
ScalarInt::try_from_uint(int, Size::from_bits(1))
|
||||
}
|
||||
Operand::Constant(const_) => eval_mir_constant(fx, const_).0.try_to_scalar_int(),
|
||||
// FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored
|
||||
// inside a temporary before being passed to the intrinsic requiring the const argument.
|
||||
|
|
|
|||
|
|
@ -165,14 +165,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
|
||||
OperandValue::Immediate(llval)
|
||||
}
|
||||
mir::ConstValue::RuntimeChecks(checks) => {
|
||||
let BackendRepr::Scalar(scalar) = layout.backend_repr else {
|
||||
bug!("from_const: invalid ByVal layout: {:#?}", layout);
|
||||
};
|
||||
let x = Scalar::from_bool(checks.value(bx.tcx().sess));
|
||||
let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
|
||||
OperandValue::Immediate(llval)
|
||||
}
|
||||
ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
|
||||
ConstValue::Slice { alloc_id, meta } => {
|
||||
let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else {
|
||||
|
|
@ -1060,6 +1052,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
OperandRef { move_annotation, ..self.codegen_consume(bx, place.as_ref()) }
|
||||
}
|
||||
|
||||
mir::Operand::RuntimeChecks(checks) => {
|
||||
let layout = bx.layout_of(bx.tcx().types.bool);
|
||||
let BackendRepr::Scalar(scalar) = layout.backend_repr else {
|
||||
bug!("from_const: invalid ByVal layout: {:#?}", layout);
|
||||
};
|
||||
let x = Scalar::from_bool(checks.value(bx.tcx().sess));
|
||||
let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
|
||||
let val = OperandValue::Immediate(llval);
|
||||
OperandRef { val, layout, move_annotation: None }
|
||||
}
|
||||
|
||||
mir::Operand::Constant(ref constant) => {
|
||||
let constant_ty = self.monomorphize(constant.ty());
|
||||
// Most SIMD vector constants should be passed as immediates.
|
||||
|
|
|
|||
|
|
@ -338,6 +338,7 @@ where
|
|||
Operand::Copy(place) | Operand::Move(place) => {
|
||||
return in_place::<Q, _>(cx, in_local, place.as_ref());
|
||||
}
|
||||
Operand::RuntimeChecks(_) => return Q::in_any_value_of_ty(cx, cx.tcx.types.bool),
|
||||
|
||||
Operand::Constant(c) => c,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,6 +122,13 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn runtime_checks(_ecx: &InterpCx<'tcx, Self>, r: RuntimeChecks) -> InterpResult<'tcx, bool> {
|
||||
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||
panic!("compiletime machine evaluated {r:?}")
|
||||
}
|
||||
|
||||
fn binary_ptr_op(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
bin_op: BinOp,
|
||||
|
|
|
|||
|
|
@ -637,6 +637,16 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
Err(ConstEvalErrKind::AssertFailure(err)).into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn runtime_checks(
|
||||
_ecx: &InterpCx<'tcx, Self>,
|
||||
_r: mir::RuntimeChecks,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||
interp_ok(true)
|
||||
}
|
||||
|
||||
fn binary_ptr_op(
|
||||
_ecx: &InterpCx<'tcx, Self>,
|
||||
_bin_op: mir::BinOp,
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ pub trait Machine<'tcx>: Sized {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Determines the result of a `NullaryOp::RuntimeChecks` invocation.
|
||||
/// Determines the result of a `Operand::RuntimeChecks` invocation.
|
||||
fn runtime_checks(
|
||||
_ecx: &InterpCx<'tcx, Self>,
|
||||
r: mir::RuntimeChecks,
|
||||
|
|
@ -680,16 +680,6 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
|||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn runtime_checks(
|
||||
_ecx: &InterpCx<$tcx, Self>,
|
||||
_r: mir::RuntimeChecks,
|
||||
) -> InterpResult<$tcx, bool> {
|
||||
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||
interp_ok(true)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn adjust_global_allocation<'b>(
|
||||
_ecx: &InterpCx<$tcx, Self>,
|
||||
|
|
|
|||
|
|
@ -845,6 +845,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// FIXME: do some more logic on `move` to invalidate the old location
|
||||
&Copy(place) | &Move(place) => self.eval_place_to_op(place, layout)?,
|
||||
|
||||
&RuntimeChecks(checks) => {
|
||||
let val = M::runtime_checks(self, checks)?;
|
||||
ImmTy::from_bool(val, self.tcx()).into()
|
||||
}
|
||||
|
||||
Constant(constant) => {
|
||||
let c = self.instantiate_from_current_frame_and_normalize_erasing_regions(
|
||||
constant.const_,
|
||||
|
|
@ -892,10 +897,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO);
|
||||
Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self)
|
||||
}
|
||||
mir::ConstValue::RuntimeChecks(checks) => {
|
||||
let val = M::runtime_checks(self, checks)?;
|
||||
Scalar::from_bool(val).into()
|
||||
}
|
||||
};
|
||||
interp_ok(OpTy { op: Operand::Immediate(imm), layout })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
move_definitely_disjoint: bool,
|
||||
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
|
||||
interp_ok(match op {
|
||||
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
|
||||
mir::Operand::Copy(_) | mir::Operand::Constant(_) | mir::Operand::RuntimeChecks(_) => {
|
||||
// Make a regular copy.
|
||||
let op = self.eval_operand(op, None)?;
|
||||
FnArg::Copy(op)
|
||||
|
|
|
|||
|
|
@ -68,10 +68,6 @@ pub enum ConstValue {
|
|||
/// Offset into `alloc`
|
||||
offset: Size,
|
||||
},
|
||||
|
||||
/// Special constants whose value depends on the evaluation context. Their value depends on a
|
||||
/// flag on the crate being codegenned.
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
|
@ -81,10 +77,7 @@ impl ConstValue {
|
|||
#[inline]
|
||||
pub fn try_to_scalar(&self) -> Option<Scalar> {
|
||||
match *self {
|
||||
ConstValue::Indirect { .. }
|
||||
| ConstValue::Slice { .. }
|
||||
| ConstValue::ZeroSized
|
||||
| ConstValue::RuntimeChecks(_) => None,
|
||||
ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
|
||||
ConstValue::Scalar(val) => Some(val),
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +133,7 @@ impl ConstValue {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<&'tcx [u8]> {
|
||||
let (alloc_id, start, len) = match self {
|
||||
ConstValue::Scalar(_) | ConstValue::ZeroSized | ConstValue::RuntimeChecks(_) => {
|
||||
ConstValue::Scalar(_) | ConstValue::ZeroSized => {
|
||||
bug!("`try_get_slice_bytes` on non-slice constant")
|
||||
}
|
||||
&ConstValue::Slice { alloc_id, meta } => (alloc_id, 0, meta),
|
||||
|
|
@ -192,9 +185,7 @@ impl ConstValue {
|
|||
/// Can return `true` even if there is no provenance.
|
||||
pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
|
||||
match *self {
|
||||
ConstValue::ZeroSized
|
||||
| ConstValue::Scalar(Scalar::Int(_))
|
||||
| ConstValue::RuntimeChecks(_) => return false,
|
||||
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
|
||||
ConstValue::Scalar(Scalar::Ptr(..)) => return true,
|
||||
// It's hard to find out the part of the allocation we point to;
|
||||
// just conservatively check everything.
|
||||
|
|
@ -233,29 +224,6 @@ impl ConstValue {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum RuntimeChecks {
|
||||
/// Returns whether we should perform some UB-checking at runtime.
|
||||
/// See the `ub_checks` intrinsic docs for details.
|
||||
UbChecks,
|
||||
/// Returns whether we should perform contract-checking at runtime.
|
||||
/// See the `contract_checks` intrinsic docs for details.
|
||||
ContractChecks,
|
||||
/// Returns whether we should perform some overflow-checking at runtime.
|
||||
/// See the `overflow_checks` intrinsic docs for details.
|
||||
OverflowChecks,
|
||||
}
|
||||
|
||||
impl RuntimeChecks {
|
||||
pub fn value(self, sess: &rustc_session::Session) -> bool {
|
||||
match self {
|
||||
Self::UbChecks => sess.ub_checks(),
|
||||
Self::ContractChecks => sess.contract_checks(),
|
||||
Self::OverflowChecks => sess.overflow_checks(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Constants
|
||||
|
||||
|
|
@ -549,7 +517,6 @@ impl<'tcx> Const<'tcx> {
|
|||
ConstValue::Slice { .. }
|
||||
| ConstValue::ZeroSized
|
||||
| ConstValue::Scalar(_)
|
||||
| ConstValue::RuntimeChecks(_)
|
||||
| ConstValue::Indirect { .. },
|
||||
_,
|
||||
) => true,
|
||||
|
|
|
|||
|
|
@ -607,9 +607,6 @@ impl<'tcx> Body<'tcx> {
|
|||
typing_env,
|
||||
crate::ty::EarlyBinder::bind(constant.const_),
|
||||
);
|
||||
if let Const::Val(ConstValue::RuntimeChecks(check), _) = mono_literal {
|
||||
return Some(check.value(tcx.sess) as u128);
|
||||
}
|
||||
mono_literal.try_eval_bits(tcx, typing_env)
|
||||
};
|
||||
|
||||
|
|
@ -623,6 +620,10 @@ impl<'tcx> Body<'tcx> {
|
|||
let bits = eval_mono_const(constant)?;
|
||||
return Some((bits, targets));
|
||||
}
|
||||
Operand::RuntimeChecks(check) => {
|
||||
let bits = check.value(tcx.sess) as u128;
|
||||
return Some((bits, targets));
|
||||
}
|
||||
Operand::Move(place) | Operand::Copy(place) => place,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1255,6 +1255,7 @@ impl<'tcx> Debug for Operand<'tcx> {
|
|||
Constant(ref a) => write!(fmt, "{a:?}"),
|
||||
Copy(ref place) => write!(fmt, "copy {place:?}"),
|
||||
Move(ref place) => write!(fmt, "move {place:?}"),
|
||||
RuntimeChecks(checks) => write!(fmt, "const {checks:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1518,7 +1519,6 @@ pub fn write_allocations<'tcx>(
|
|||
match val {
|
||||
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
|
||||
ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
|
||||
ConstValue::RuntimeChecks(_) => None,
|
||||
ConstValue::ZeroSized => None,
|
||||
ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {
|
||||
// FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
|
||||
|
|
@ -1969,7 +1969,6 @@ fn pretty_print_const_value_tcx<'tcx>(
|
|||
fmt.write_str(&p.into_buffer())?;
|
||||
return Ok(());
|
||||
}
|
||||
(ConstValue::RuntimeChecks(checks), _) => return write!(fmt, "{checks:?}"),
|
||||
(ConstValue::ZeroSized, ty::FnDef(d, s)) => {
|
||||
let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
p.print_alloc_ids = true;
|
||||
|
|
|
|||
|
|
@ -642,7 +642,7 @@ impl<'tcx> Operand<'tcx> {
|
|||
|
||||
pub fn to_copy(&self) -> Self {
|
||||
match *self {
|
||||
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
|
||||
Operand::Copy(_) | Operand::Constant(_) | Operand::RuntimeChecks(_) => self.clone(),
|
||||
Operand::Move(place) => Operand::Copy(place),
|
||||
}
|
||||
}
|
||||
|
|
@ -652,7 +652,7 @@ impl<'tcx> Operand<'tcx> {
|
|||
pub fn place(&self) -> Option<Place<'tcx>> {
|
||||
match self {
|
||||
Operand::Copy(place) | Operand::Move(place) => Some(*place),
|
||||
Operand::Constant(_) => None,
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -661,7 +661,7 @@ impl<'tcx> Operand<'tcx> {
|
|||
pub fn constant(&self) -> Option<&ConstOperand<'tcx>> {
|
||||
match self {
|
||||
Operand::Constant(x) => Some(&**x),
|
||||
Operand::Copy(_) | Operand::Move(_) => None,
|
||||
Operand::Copy(_) | Operand::Move(_) | Operand::RuntimeChecks(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -681,6 +681,7 @@ impl<'tcx> Operand<'tcx> {
|
|||
match self {
|
||||
&Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
|
||||
Operand::Constant(c) => c.const_.ty(),
|
||||
Operand::RuntimeChecks(_) => tcx.types.bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -693,6 +694,7 @@ impl<'tcx> Operand<'tcx> {
|
|||
local_decls.local_decls()[l.local].source_info.span
|
||||
}
|
||||
Operand::Constant(c) => c.span,
|
||||
Operand::RuntimeChecks(_) => DUMMY_SP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1327,6 +1327,10 @@ pub enum Operand<'tcx> {
|
|||
|
||||
/// Constants are already semantically values, and remain unchanged.
|
||||
Constant(Box<ConstOperand<'tcx>>),
|
||||
|
||||
/// Special constants whose value depends on the evaluation context. Their value depends on a
|
||||
/// flag on the crate being codegenned.
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
|
|
@ -1558,6 +1562,29 @@ pub enum AggregateKind<'tcx> {
|
|||
RawPtr(Ty<'tcx>, Mutability),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum RuntimeChecks {
|
||||
/// Returns whether we should perform some UB-checking at runtime.
|
||||
/// See the `ub_checks` intrinsic docs for details.
|
||||
UbChecks,
|
||||
/// Returns whether we should perform contract-checking at runtime.
|
||||
/// See the `contract_checks` intrinsic docs for details.
|
||||
ContractChecks,
|
||||
/// Returns whether we should perform some overflow-checking at runtime.
|
||||
/// See the `overflow_checks` intrinsic docs for details.
|
||||
OverflowChecks,
|
||||
}
|
||||
|
||||
impl RuntimeChecks {
|
||||
pub fn value(self, sess: &rustc_session::Session) -> bool {
|
||||
match self {
|
||||
Self::UbChecks => sess.ub_checks(),
|
||||
Self::ContractChecks => sess.contract_checks(),
|
||||
Self::OverflowChecks => sess.overflow_checks(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
|
||||
pub enum UnOp {
|
||||
|
|
|
|||
|
|
@ -845,6 +845,7 @@ macro_rules! make_mir_visitor {
|
|||
Operand::Constant(constant) => {
|
||||
self.visit_const_operand(constant, location);
|
||||
}
|
||||
Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
|
|||
let value = match operand {
|
||||
Operand::Constant(c) => VarDebugInfoContents::Const(*c),
|
||||
Operand::Copy(p) | Operand::Move(p) => VarDebugInfoContents::Place(p),
|
||||
Operand::RuntimeChecks(_) => unreachable!(),
|
||||
};
|
||||
let dbginfo = VarDebugInfo {
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -1099,7 +1099,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
Some(DropData { source_info, local, kind: DropKind::Value })
|
||||
}
|
||||
Operand::Constant(_) => None,
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -1563,7 +1563,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
// look for moves of a local variable, like `MOVE(_X)`
|
||||
let locals_moved = operands.iter().flat_map(|operand| match operand.node {
|
||||
Operand::Copy(_) | Operand::Constant(_) => None,
|
||||
Operand::Copy(_) | Operand::Constant(_) | Operand::RuntimeChecks(_) => None,
|
||||
Operand::Move(place) => place.as_local(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -546,9 +546,10 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
|
||||
fn gather_operand(&mut self, operand: &Operand<'tcx>) {
|
||||
match *operand {
|
||||
Operand::Constant(..) | Operand::Copy(..) => {} // not-a-move
|
||||
// not-a-move
|
||||
Operand::Constant(..) | Operand::Copy(..) | Operand::RuntimeChecks(_) => {}
|
||||
// a move
|
||||
Operand::Move(place) => {
|
||||
// a move
|
||||
self.gather_move(place);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
|||
self.penalty += CALL_PENALTY;
|
||||
}
|
||||
TerminatorKind::SwitchInt { discr, targets } => {
|
||||
if discr.constant().is_some() {
|
||||
if matches!(discr, Operand::Constant(_) | Operand::RuntimeChecks(_)) {
|
||||
// Not only will this become a `Goto`, but likely other
|
||||
// things will be removable as unreachable.
|
||||
self.bonus += CONST_SWITCH_BONUS;
|
||||
|
|
|
|||
|
|
@ -211,6 +211,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
|
|||
state: &mut State<FlatSet<Scalar>>,
|
||||
) -> ValueOrPlace<FlatSet<Scalar>> {
|
||||
match operand {
|
||||
Operand::RuntimeChecks(_) => ValueOrPlace::TOP,
|
||||
Operand::Constant(box constant) => {
|
||||
ValueOrPlace::Value(self.handle_constant(constant, state))
|
||||
}
|
||||
|
|
@ -530,6 +531,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
|
|||
operand: &Operand<'tcx>,
|
||||
) {
|
||||
match operand {
|
||||
Operand::RuntimeChecks(_) => {}
|
||||
Operand::Copy(rhs) | Operand::Move(rhs) => {
|
||||
if let Some(rhs) = self.map.find(rhs.as_ref()) {
|
||||
state.insert_place_idx(place, rhs, &self.map);
|
||||
|
|
@ -1036,7 +1038,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> {
|
|||
self.super_operand(operand, location)
|
||||
}
|
||||
}
|
||||
Operand::Constant(_) => {}
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,11 +117,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
|
|||
unreachable!()
|
||||
};
|
||||
// Always correct since we can only switch on `Copy` types
|
||||
let parent_op = match parent_op {
|
||||
Operand::Move(x) => Operand::Copy(*x),
|
||||
Operand::Copy(x) => Operand::Copy(*x),
|
||||
Operand::Constant(x) => Operand::Constant(x.clone()),
|
||||
};
|
||||
let parent_op = parent_op.to_copy();
|
||||
let parent_ty = parent_op.ty(body.local_decls(), tcx);
|
||||
let statements_before = bbs[parent].statements.len();
|
||||
let parent_end = Location { block: parent, statement_index: statements_before };
|
||||
|
|
|
|||
|
|
@ -136,12 +136,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn nth_arg_span(&self, args: &[Spanned<Operand<'tcx>>], n: usize) -> Span {
|
||||
match &args[n].node {
|
||||
Operand::Copy(place) | Operand::Move(place) => {
|
||||
self.body.local_decls[place.local].source_info.span
|
||||
}
|
||||
Operand::Constant(constant) => constant.span,
|
||||
}
|
||||
args[n].node.span(&self.body.local_decls)
|
||||
}
|
||||
|
||||
fn emit_lint(
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ enum Value<'a, 'tcx> {
|
|||
Discriminant(VnIndex),
|
||||
|
||||
// Operations.
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
UnaryOp(UnOp, VnIndex),
|
||||
BinaryOp(BinOp, VnIndex, VnIndex),
|
||||
Cast {
|
||||
|
|
@ -569,6 +570,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
|
||||
|
||||
Opaque(_) => return None,
|
||||
// Keep runtime check constants as symbolic.
|
||||
RuntimeChecks(..) => return None,
|
||||
|
||||
// In general, evaluating repeat expressions just consumes a lot of memory.
|
||||
// But in the special case that the element is just Immediate::Uninit, we can evaluate
|
||||
|
|
@ -1005,11 +1008,16 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
location: Location,
|
||||
) -> Option<VnIndex> {
|
||||
match *operand {
|
||||
Operand::RuntimeChecks(c) => {
|
||||
Some(self.insert(self.tcx.types.bool, Value::RuntimeChecks(c)))
|
||||
}
|
||||
Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
|
||||
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
|
||||
let value = self.simplify_place_value(place, location)?;
|
||||
if let Some(const_) = self.try_as_constant(value) {
|
||||
*operand = Operand::Constant(Box::new(const_));
|
||||
} else if let Value::RuntimeChecks(c) = self.get(value) {
|
||||
*operand = Operand::RuntimeChecks(c);
|
||||
}
|
||||
Some(value)
|
||||
}
|
||||
|
|
@ -1777,6 +1785,8 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
|
|||
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
|
||||
if let Some(const_) = self.try_as_constant(index) {
|
||||
Some(Operand::Constant(Box::new(const_)))
|
||||
} else if let Value::RuntimeChecks(c) = self.get(index) {
|
||||
Some(Operand::RuntimeChecks(c))
|
||||
} else if let Some(place) = self.try_as_place(index, location, false) {
|
||||
self.reused_locals.insert(place.local);
|
||||
Some(Operand::Copy(place))
|
||||
|
|
|
|||
|
|
@ -363,11 +363,15 @@ impl<'tcx> MutVisitor<'tcx> for SimplifyUbCheck<'tcx> {
|
|||
}
|
||||
|
||||
fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _: Location) {
|
||||
if let Operand::Constant(c) = operand
|
||||
&& let Const::Val(c, _) = &mut c.const_
|
||||
&& let ConstValue::RuntimeChecks(RuntimeChecks::UbChecks) = c
|
||||
{
|
||||
*c = ConstValue::from_bool(self.tcx.sess.ub_checks());
|
||||
if let Operand::RuntimeChecks(RuntimeChecks::UbChecks) = operand {
|
||||
*operand = Operand::Constant(Box::new(ConstOperand {
|
||||
span: rustc_span::DUMMY_SP,
|
||||
user_ty: None,
|
||||
const_: Const::Val(
|
||||
ConstValue::from_bool(self.tcx.sess.ub_checks()),
|
||||
self.tcx.types.bool,
|
||||
),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -477,6 +477,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
|
|||
let Some(rhs) = self.map.find(rhs.as_ref()) else { return };
|
||||
self.process_copy(lhs, rhs, state)
|
||||
}
|
||||
Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
/// or `eval_place`, depending on the variant of `Operand` used.
|
||||
fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option<ImmTy<'tcx>> {
|
||||
match *op {
|
||||
Operand::RuntimeChecks(_) => None,
|
||||
Operand::Constant(ref c) => self.eval_constant(c),
|
||||
Operand::Move(place) | Operand::Copy(place) => self.eval_place(place),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ fn remap_mir_for_const_eval_select<'tcx>(
|
|||
if context == hir::Constness::Const { called_in_const } else { called_at_rt };
|
||||
let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) =
|
||||
match tupled_args.node {
|
||||
Operand::Constant(_) => {
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => {
|
||||
// There is no good way of extracting a tuple arg from a constant
|
||||
// (const generic stuff) so we just create a temporary and deconstruct
|
||||
// that.
|
||||
|
|
|
|||
|
|
@ -35,14 +35,7 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
|
|||
terminator.source_info,
|
||||
StatementKind::Assign(Box::new((
|
||||
*destination,
|
||||
Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
|
||||
span: terminator.source_info.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(
|
||||
ConstValue::RuntimeChecks(op),
|
||||
tcx.types.bool,
|
||||
),
|
||||
}))),
|
||||
Rvalue::Use(Operand::RuntimeChecks(op)),
|
||||
))),
|
||||
));
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
|
|
|
|||
|
|
@ -360,6 +360,9 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
match operand {
|
||||
Operand::Copy(place) | Operand::Move(place) => self.validate_place(place.as_ref()),
|
||||
|
||||
// Promoting a runtime check would transform a runtime error into a compile-time error.
|
||||
Operand::RuntimeChecks(_) => Err(Unpromotable),
|
||||
|
||||
// The qualifs for a constant (e.g. `HasMutInterior`) are checked in
|
||||
// `validate_rvalue` upon access.
|
||||
Operand::Constant(c) => {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
|
|||
{
|
||||
Some(const_operand)
|
||||
}
|
||||
Operand::Copy(_) | Operand::Move(_) => None,
|
||||
Operand::Copy(_) | Operand::Move(_) | Operand::RuntimeChecks(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -392,11 +392,14 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
|
|||
// a_1 = move? place.1
|
||||
// ...
|
||||
// ```
|
||||
StatementKind::Assign(box (lhs, Rvalue::Use(ref op))) => {
|
||||
let (rplace, copy) = match *op {
|
||||
Operand::Copy(rplace) => (rplace, true),
|
||||
Operand::Move(rplace) => (rplace, false),
|
||||
Operand::Constant(_) => bug!(),
|
||||
StatementKind::Assign(box (
|
||||
lhs,
|
||||
Rvalue::Use(ref op @ (Operand::Copy(rplace) | Operand::Move(rplace))),
|
||||
)) => {
|
||||
let copy = match *op {
|
||||
Operand::Copy(_) => true,
|
||||
Operand::Move(_) => false,
|
||||
Operand::Constant(_) | Operand::RuntimeChecks(_) => bug!(),
|
||||
};
|
||||
if let Some(final_locals) = self.replacements.place_fragments(lhs) {
|
||||
for (field, ty, new_local) in final_locals {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ pub(crate) fn try_new_allocation<'tcx>(
|
|||
ConstValue::Scalar(scalar) => {
|
||||
alloc::try_new_scalar(layout, scalar, cx).map(|alloc| alloc.stable(tables, cx))
|
||||
}
|
||||
ConstValue::RuntimeChecks(_) => todo!(),
|
||||
ConstValue::ZeroSized => Ok(new_empty_allocation(layout.align.abi)),
|
||||
ConstValue::Slice { alloc_id, meta } => {
|
||||
alloc::try_new_slice(layout, alloc_id, meta, cx).map(|alloc| alloc.stable(tables, cx))
|
||||
|
|
|
|||
|
|
@ -673,6 +673,7 @@ pub enum Operand {
|
|||
Copy(Place),
|
||||
Move(Place),
|
||||
Constant(ConstOperand),
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
|
|
@ -695,6 +696,16 @@ pub struct ConstOperand {
|
|||
pub const_: MirConst,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
|
||||
pub enum RuntimeChecks {
|
||||
/// cfg!(ub_checks), but at codegen time
|
||||
UbChecks,
|
||||
/// cfg!(contract_checks), but at codegen time
|
||||
ContractChecks,
|
||||
/// cfg!(overflow_checks), but at codegen time
|
||||
OverflowChecks,
|
||||
}
|
||||
|
||||
/// Debug information pertaining to a user variable.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct VarDebugInfo {
|
||||
|
|
@ -1025,6 +1036,7 @@ impl Operand {
|
|||
match self {
|
||||
Operand::Copy(place) | Operand::Move(place) => place.ty(locals),
|
||||
Operand::Constant(c) => Ok(c.ty()),
|
||||
Operand::RuntimeChecks(_) => Ok(Ty::bool_ty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@ fn pretty_operand(operand: &Operand) -> String {
|
|||
format!("move {mv:?}")
|
||||
}
|
||||
Operand::Constant(cnst) => pretty_mir_const(&cnst.const_),
|
||||
Operand::RuntimeChecks(checks) => format!("const {checks:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ macro_rules! make_mir_visitor {
|
|||
Operand::Constant(constant) => {
|
||||
self.visit_const_operand(constant, location);
|
||||
}
|
||||
Operand::RuntimeChecks(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1357,7 +1357,6 @@ pub enum ConstantKind {
|
|||
Ty(TyConst),
|
||||
Allocated(Allocation),
|
||||
Unevaluated(UnevaluatedConst),
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
Param(ParamConst),
|
||||
/// Store ZST constants.
|
||||
/// We have to special handle these constants since its type might be generic.
|
||||
|
|
@ -1377,16 +1376,6 @@ pub struct UnevaluatedConst {
|
|||
pub promoted: Option<Promoted>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
|
||||
pub enum RuntimeChecks {
|
||||
/// cfg!(ub_checks), but at codegen time
|
||||
UbChecks,
|
||||
/// cfg!(contract_checks), but at codegen time
|
||||
ContractChecks,
|
||||
/// cfg!(overflow_checks), but at codegen time
|
||||
OverflowChecks,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
|
||||
pub enum TraitSpecializationKind {
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
|
|||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::RuntimeChecks {
|
||||
type T = crate::ty::RuntimeChecks;
|
||||
type T = crate::mir::RuntimeChecks;
|
||||
fn stable<'cx>(
|
||||
&self,
|
||||
_: &mut Tables<'cx, BridgeTys>,
|
||||
|
|
@ -320,9 +320,9 @@ impl<'tcx> Stable<'tcx> for mir::RuntimeChecks {
|
|||
) -> Self::T {
|
||||
use rustc_middle::mir::RuntimeChecks::*;
|
||||
match self {
|
||||
UbChecks => crate::ty::RuntimeChecks::UbChecks,
|
||||
ContractChecks => crate::ty::RuntimeChecks::ContractChecks,
|
||||
OverflowChecks => crate::ty::RuntimeChecks::OverflowChecks,
|
||||
UbChecks => crate::mir::RuntimeChecks::UbChecks,
|
||||
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
|
||||
OverflowChecks => crate::mir::RuntimeChecks::OverflowChecks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -379,6 +379,7 @@ impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
|
|||
Copy(place) => crate::mir::Operand::Copy(place.stable(tables, cx)),
|
||||
Move(place) => crate::mir::Operand::Move(place.stable(tables, cx)),
|
||||
Constant(c) => crate::mir::Operand::Constant(c.stable(tables, cx)),
|
||||
RuntimeChecks(c) => crate::mir::Operand::RuntimeChecks(c.stable(tables, cx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -886,13 +887,6 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> {
|
|||
let ty = ty.stable(tables, cx);
|
||||
MirConst::new(ConstantKind::ZeroSized, ty, id)
|
||||
}
|
||||
mir::Const::Val(mir::ConstValue::RuntimeChecks(checks), ty) => {
|
||||
let ty = cx.lift(ty).unwrap();
|
||||
let checks = cx.lift(checks).unwrap();
|
||||
let ty = ty.stable(tables, cx);
|
||||
let kind = ConstantKind::RuntimeChecks(checks.stable(tables, cx));
|
||||
MirConst::new(kind, ty, id)
|
||||
}
|
||||
mir::Const::Val(val, ty) => {
|
||||
let ty = cx.lift(ty).unwrap();
|
||||
let val = cx.lift(val).unwrap();
|
||||
|
|
|
|||
|
|
@ -68,9 +68,7 @@ impl Visitable for MirConst {
|
|||
super::ty::ConstantKind::Ty(ct) => ct.visit(visitor)?,
|
||||
super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?,
|
||||
super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?,
|
||||
super::ty::ConstantKind::RuntimeChecks(_)
|
||||
| super::ty::ConstantKind::Param(_)
|
||||
| super::ty::ConstantKind::ZeroSized => {}
|
||||
super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {}
|
||||
}
|
||||
self.ty().visit(visitor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> {
|
|||
immutable_borrowers.push(p.local);
|
||||
}
|
||||
},
|
||||
mir::Operand::Constant(..) => (),
|
||||
mir::Operand::Constant(..) | mir::Operand::RuntimeChecks(..) => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
|
|||
|
||||
let mut visit_op = |op: &mir::Operand<'_>| match op {
|
||||
mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
|
||||
mir::Operand::Constant(..) => (),
|
||||
mir::Operand::Constant(..) | mir::Operand::RuntimeChecks(..) => (),
|
||||
};
|
||||
|
||||
match rvalue {
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ fn check_operand<'tcx>(
|
|||
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
|
||||
None => Ok(()),
|
||||
},
|
||||
Operand::RuntimeChecks(..) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ type Demo = [u8; 3];
|
|||
#[no_mangle]
|
||||
pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
|
||||
// CHECK-NOT: sub
|
||||
// CHECK: %2 = icmp ne ptr %1, null
|
||||
// CHECK-NEXT: tail call void @llvm.assume(i1 %2)
|
||||
// CHECK-NEXT: %[[RET:.+]] = icmp eq ptr {{%0, %1|%1, %0}}
|
||||
// CHECK-NEXT: ret i1 %[[RET]]
|
||||
// CHECK: %[[RET:.+]] = icmp eq ptr {{%y.0, %y.1|%y.1, %y.0}}
|
||||
// CHECK: ret i1 %[[RET]]
|
||||
y.len() == 0
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +31,7 @@ struct MyZST;
|
|||
// CHECK-LABEL: @slice_zst_iter_len_eq_zero
|
||||
#[no_mangle]
|
||||
pub fn slice_zst_iter_len_eq_zero(y: std::slice::Iter<'_, MyZST>) -> bool {
|
||||
// CHECK: %[[RET:.+]] = icmp eq ptr %1, null
|
||||
// CHECK: %[[RET:.+]] = icmp eq ptr %y.1, null
|
||||
// CHECK: ret i1 %[[RET]]
|
||||
y.len() == 0
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue