From 67aca498c6b17b8888f17c5035e2c6f257e32c5c Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Mon, 23 May 2022 09:20:57 -0500 Subject: [PATCH 1/5] Put a bound on collection misbehavior As currently written, when a logic error occurs in a collection's trait parameters, this allows *completely arbitrary* misbehavior, so long as it does not cause undefined behavior in std. However, because the extent of misbehavior is not specified, it is allowed for *any* code in std to start misbehaving in arbitrary ways which are not formally UB; consider the theoretical example of a global which gets set on an observed logic error. Because the misbehavior is only bound by not resulting in UB from safe APIs and the crate-level encapsulation boundary of all of std, this makes writing user unsafe code that utilizes std theoretically impossible, as it now relies on undocumented QOI that unrelated parts of std cannot be caused to misbehave by a misuse of std::collections APIs. In practice, this is a nonconcern, because std has reasonable QOI and an implementation that takes advantage of this freedom is essentially a malicious implementation and only compliant by the most langauage-lawyer reading of the documentation. To close this hole, we just add a small clause to the existing logic error paragraph that ensures that any misbehavior is limited to the collection which observed the logic error, making it more plausible to prove the soundness of user unsafe code. This is not meant to be formal; a formal refinement would likely need to mention that values derived from the collection can also misbehave after a logic error is observed, as well as define what it means to "observe" a logic error in the first place. This fix errs on the side of informality in order to close the hole without complicating a normal reading which can assume a reasonable nonmalicious QOI. See also [discussion on IRLO][1]. [1]: https://internals.rust-lang.org/t/using-std-collections-and-unsafe-anything-can-happen/16640 --- library/alloc/src/collections/binary_heap.rs | 7 ++++--- library/alloc/src/collections/btree/map.rs | 6 +++--- library/alloc/src/collections/btree/set.rs | 6 +++--- library/std/src/collections/hash/map.rs | 3 ++- library/std/src/collections/hash/set.rs | 15 ++++++++------- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index c3c1d0c92a86b..839088eac21ea 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -166,9 +166,10 @@ mod tests; /// item's ordering relative to any other item, as determined by the [`Ord`] /// trait, changes while it is in the heap. This is normally only possible /// through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. The -/// behavior resulting from such a logic error is not specified (it -/// could include panics, incorrect results, aborts, memory leaks, or -/// non-termination) but will not be undefined behavior. +/// behavior resulting from such a logic error is not specified, but will +/// be encapsulated to the `BinaryHeap` that observed the logic error and not +/// result in undefined behavior. This could include panics, incorrect results, +/// aborts, memory leaks, and non-termination. /// /// # Examples /// diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 264c217c9ef23..28b44e3acb9da 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -64,9 +64,9 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// It is a logic error for a key to be modified in such a way that the key's ordering relative to /// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified (it could include panics, -/// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined -/// behavior. +/// The behavior resulting from such a logic error is not specified, but will be encapsulated to the +/// `BTreeSet` that observed the logic error and not result in undefined behavior. This could +/// include panics, incorrect results, aborts, memory leaks, and non-termination. /// /// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::values`], or /// [`BTreeMap::keys`] produce their items in order by key, and take worst-case logarithmic and diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index d6733425288d4..20ef834eaeef3 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -23,9 +23,9 @@ use super::Recover; /// It is a logic error for an item to be modified in such a way that the item's ordering relative /// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified (it could include panics, -/// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined -/// behavior. +/// The behavior resulting from such a logic error is not specified, but will be encapsulated to the +/// `BTreeSet` that observed the logic error and not result in undefined behavior. This could +/// include panics, incorrect results, aborts, memory leaks, and non-termination. /// /// Iterators returned by [`BTreeSet::iter`] produce their items in order, and take worst-case /// logarithmic and amortized constant time per item returned. diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 969f5dde4f05d..4ec423eb27f31 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -54,7 +54,8 @@ use crate::sys; /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. /// The behavior resulting from such a logic error is not specified, but will -/// not result in undefined behavior. This could include panics, incorrect results, +/// be encapsulated to the `HashMap` that observed the logic error and not +/// result in undefined behavior. This could include panics, incorrect results, /// aborts, memory leaks, and non-termination. /// /// The hash table implementation is a Rust port of Google's [SwissTable]. diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 4ac0e081c2e2d..da0572047eca8 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -33,13 +33,14 @@ use super::map::{map_try_reserve_error, RandomState}; /// In other words, if two keys are equal, their hashes must be equal. /// /// -/// It is a logic error for an item to be modified in such a way that the -/// item's hash, as determined by the [`Hash`] trait, or its equality, as -/// determined by the [`Eq`] trait, changes while it is in the set. This is -/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or -/// unsafe code. The behavior resulting from such a logic error is not -/// specified (it could include panics, incorrect results, aborts, memory -/// leaks, or non-termination) but will not be undefined behavior. +/// It is a logic error for a key to be modified in such a way that the key's +/// hash, as determined by the [`Hash`] trait, or its equality, as determined by +/// the [`Eq`] trait, changes while it is in the map. This is normally only +/// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// The behavior resulting from such a logic error is not specified, but will +/// be encapsulated to the `HashSet` that observed the logic error and not +/// result in undefined behavior. This could include panics, incorrect results, +/// aborts, memory leaks, and non-termination. /// /// # Examples /// From dff602fc18be295d1b87196a6eee1c72673e6bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Tue, 31 May 2022 00:00:00 +0000 Subject: [PATCH 2/5] Add a pointer to address cast kind A pointer to address cast are often special-cased. Introduce a dedicated cast kind to make them easy distinguishable. --- compiler/rustc_borrowck/src/type_check/mod.rs | 20 ++++++++-- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 10 +++-- .../rustc_const_eval/src/interpret/cast.rs | 37 +++++++++++-------- .../src/transform/check_consts/check.rs | 13 ++----- .../src/transform/promote_consts.rs | 16 ++------ compiler/rustc_middle/src/mir/mod.rs | 10 +++++ compiler/rustc_middle/src/mir/tcx.rs | 17 --------- .../src/build/expr/as_rvalue.rs | 15 ++++++-- .../rustc_mir_dataflow/src/impls/liveness.rs | 23 ++++-------- .../src/dead_store_elimination.rs | 4 +- ..._prop_fails_gracefully.main.ConstProp.diff | 2 +- .../reify_fn_ptr.main.ConstProp.diff | 2 +- ...s.pointer_to_int.DeadStoreElimination.diff | 4 +- .../clippy_utils/src/qualify_min_const_fn.rs | 15 +++----- 15 files changed, 96 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 4a2b2942008b8..d9d31ab2c89a1 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2147,6 +2147,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } + CastKind::PointerAddress => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(*ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => (), + _ => { + span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty) + } + } + } + CastKind::Misc => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); @@ -2155,7 +2167,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { (None, _) | (_, None | Some(CastTy::FnPtr)) | (Some(CastTy::Float), Some(CastTy::Ptr(_))) - | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Float)) => { + | ( + Some(CastTy::Ptr(_) | CastTy::FnPtr), + Some(CastTy::Float | CastTy::Int(_)), + ) => { span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty,) } ( @@ -2163,8 +2178,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(CastTy::Int(_) | CastTy::Float | CastTy::Ptr(_)), ) | (Some(CastTy::Float), Some(CastTy::Int(_) | CastTy::Float)) - | (Some(CastTy::Ptr(_)), Some(CastTy::Int(_) | CastTy::Ptr(_))) - | (Some(CastTy::FnPtr), Some(CastTy::Int(_) | CastTy::Ptr(_))) => (), + | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Ptr(_))) => (), } } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3fe112d794b44..7c59ce354c01a 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -607,7 +607,7 @@ fn codegen_stmt<'tcx>( let operand = codegen_operand(fx, operand); lval.write_cvalue(fx, operand.cast_pointer_to(to_layout)); } - Rvalue::Cast(CastKind::Misc, ref operand, to_ty) => { + Rvalue::Cast(CastKind::Misc | CastKind::PointerAddress, ref operand, to_ty) => { let operand = codegen_operand(fx, operand); let from_ty = operand.layout().ty; let to_ty = fx.monomorphize(to_ty); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index fd29c9e281b92..bd88aa33372df 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -181,6 +181,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty)); let val = match *kind { + mir::CastKind::PointerAddress => { + assert!(bx.cx().is_backend_immediate(cast)); + let llptr = operand.immediate(); + let llcast_ty = bx.cx().immediate_backend_type(cast); + let lladdr = bx.ptrtoint(llptr, llcast_ty); + OperandValue::Immediate(lladdr) + } mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => { match *operand.layout.ty.kind() { ty::FnDef(def_id, substs) => { @@ -362,9 +369,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => { bx.pointercast(llval, ll_t_out) } - (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { - bx.ptrtoint(llval, ll_t_out) - } (CastTy::Int(_), CastTy::Ptr(_)) => { let usize_llval = bx.intcast(llval, bx.cx().type_isize(), signed); bx.inttoptr(usize_llval, ll_t_out) diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index be34a77bdba96..af563c1450ea0 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -1,3 +1,4 @@ +use std::assert_matches::assert_matches; use std::convert::TryFrom; use rustc_apfloat::ieee::{Double, Single}; @@ -30,6 +31,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.unsize_into(src, cast_ty, dest)?; } + PointerAddress => { + let src = self.read_immediate(src)?; + let res = self.pointer_address_cast(&src, cast_ty)?; + self.write_immediate(res, dest)?; + } + Misc => { let src = self.read_immediate(src)?; let res = self.misc_cast(&src, cast_ty)?; @@ -174,23 +181,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // # The remaining source values are scalar and "int-like". let scalar = src.to_scalar()?; + Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) + } - // If we are casting from a pointer to something - // that is not a pointer, mark the pointer as exposed - if src.layout.ty.is_any_ptr() && !cast_ty.is_any_ptr() { - let ptr = self.scalar_to_ptr(scalar)?; - - match ptr.into_pointer_or_addr() { - Ok(ptr) => { - M::expose_ptr(self, ptr)?; - } - Err(_) => { - // do nothing, exposing an invalid pointer - // has no meaning - } - }; - } + pub fn pointer_address_cast( + &mut self, + src: &ImmTy<'tcx, M::PointerTag>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Immediate> { + assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_)); + assert!(cast_ty.is_integral()); + let scalar = src.to_scalar()?; + let ptr = self.scalar_to_ptr(scalar)?; + match ptr.into_pointer_or_addr() { + Ok(ptr) => M::expose_ptr(self, ptr)?, + Err(_) => {} // do nothing, exposing an invalid pointer has no meaning + }; Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index eea6e2a47a94b..4ef33d62a6bfe 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -8,7 +8,6 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeFoldable}; @@ -543,16 +542,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // in the type of any local, which also excludes casts). } - Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { - let operand_ty = operand.ty(self.body, self.tcx); - let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - - if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { - self.check_op(ops::RawPtrToIntCast); - } + Rvalue::Cast(CastKind::PointerAddress, _, _) => { + self.check_op(ops::RawPtrToIntCast); } + Rvalue::Cast(CastKind::Misc, _, _) => {} + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index fc6b8a1a7234c..ea23bd14d2538 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -16,7 +16,6 @@ use rustc_hir as hir; use rustc_middle::mir::traversal::ReversePostorderIter; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -502,18 +501,11 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), - Rvalue::Cast(kind, operand, cast_ty) => { - if matches!(kind, CastKind::Misc) { - let operand_ty = operand.ty(self.body, self.tcx); - let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast"); - if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { - // ptr-to-int casts are not possible in consts and thus not promotable - return Err(Unpromotable); - } - // int-to-ptr casts are fine, they just use the integer value at pointer type. - } + // ptr-to-int casts are not possible in consts and thus not promotable + Rvalue::Cast(CastKind::PointerAddress, _, _) => return Err(Unpromotable), + // int-to-ptr casts are fine, they just use the integer value at pointer type. + Rvalue::Cast(_, operand, _) => { self.validate_operand(operand)?; } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index b09d39996f4de..1b63c8d67ca14 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2604,9 +2604,19 @@ pub enum Rvalue<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(Rvalue<'_>, 40); +impl<'tcx> Rvalue<'tcx> { + #[inline] + pub fn is_pointer_int_cast(&self) -> bool { + matches!(self, Rvalue::Cast(CastKind::PointerAddress, _, _)) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum CastKind { Misc, + /// A pointer to address cast. A cast between a pointer and an integer type, + /// or between a function pointer and an integer type. + PointerAddress, Pointer(PointerCast), } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index f1fb484a801ba..c93b7a9550229 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -4,7 +4,6 @@ */ use crate::mir::*; -use crate::ty::cast::CastTy; use crate::ty::subst::Subst; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; @@ -224,22 +223,6 @@ impl<'tcx> Rvalue<'tcx> { _ => RvalueInitializationState::Deep, } } - - pub fn is_pointer_int_cast(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> bool - where - D: HasLocalDecls<'tcx>, - { - if let Rvalue::Cast(CastKind::Misc, src_op, dest_ty) = self { - if let Some(CastTy::Int(_)) = CastTy::from_ty(*dest_ty) { - let src_ty = src_op.ty(local_decls, tcx); - if let Some(CastTy::FnPtr | CastTy::Ptr(_)) = CastTy::from_ty(src_ty) { - return true; - } - } - } - - false - } } impl<'tcx> Operand<'tcx> { diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 0a0c7659b086d..2b137046c7f73 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -11,6 +11,7 @@ use rustc_middle::mir::AssertKind; use rustc_middle::mir::Place; use rustc_middle::mir::*; use rustc_middle::thir::*; +use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::{self, Ty, UpvarSubsts}; use rustc_span::Span; @@ -188,11 +189,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Use(Operand::Move(Place::from(result)))) } ExprKind::Cast { source } => { + let source = &this.thir[source]; + let from_ty = CastTy::from_ty(source.ty); + let cast_ty = CastTy::from_ty(expr.ty); + let cast_kind = match (from_ty, cast_ty) { + (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + CastKind::PointerAddress + } + (_, _) => CastKind::Misc, + }; let source = unpack!( - block = - this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No) + block = this.as_operand(block, scope, source, None, NeedsTemporary::No) ); - block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty)) + block.and(Rvalue::Cast(cast_kind, source, expr.ty)) } ExprKind::Pointer { cast, source } => { let source = unpack!( diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 4350eb6cdd3b1..7076fbe1bdb53 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -1,7 +1,6 @@ use rustc_index::bit_set::{BitSet, ChunkedBitSet}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{self, Local, LocalDecls, Location, Place, StatementKind}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::mir::{self, Local, Location, Place, StatementKind}; use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis}; @@ -193,27 +192,21 @@ impl DefUse { /// This is basically written for dead store elimination and nothing else. /// /// All of the caveats of `MaybeLiveLocals` apply. -pub struct MaybeTransitiveLiveLocals<'a, 'tcx> { +pub struct MaybeTransitiveLiveLocals<'a> { always_live: &'a BitSet, - local_decls: &'a LocalDecls<'tcx>, - tcx: TyCtxt<'tcx>, } -impl<'a, 'tcx> MaybeTransitiveLiveLocals<'a, 'tcx> { +impl<'a> MaybeTransitiveLiveLocals<'a> { /// The `always_alive` set is the set of locals to which all stores should unconditionally be /// considered live. /// /// This should include at least all locals that are ever borrowed. - pub fn new( - always_live: &'a BitSet, - local_decls: &'a LocalDecls<'tcx>, - tcx: TyCtxt<'tcx>, - ) -> Self { - MaybeTransitiveLiveLocals { always_live, local_decls, tcx } + pub fn new(always_live: &'a BitSet) -> Self { + MaybeTransitiveLiveLocals { always_live } } } -impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a, 'tcx> { +impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> { type Domain = ChunkedBitSet; type Direction = Backward; @@ -241,7 +234,7 @@ impl<'a> GenKill for TransferWrapper<'a> { } } -impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a, 'tcx> { +impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { fn apply_statement_effect( &self, trans: &mut Self::Domain, @@ -251,7 +244,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a, 'tcx> { // Compute the place that we are storing to, if any let destination = match &statement.kind { StatementKind::Assign(assign) => { - if assign.1.is_pointer_int_cast(self.local_decls, self.tcx) { + if assign.1.is_pointer_int_cast() { // Pointer to int casts may be side-effects due to exposing the provenance. // While the model is undecided, we should be conservative. See // diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 84f2ee639e4d8..8becac34ed7ee 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -24,7 +24,7 @@ use rustc_mir_dataflow::{impls::MaybeTransitiveLiveLocals, Analysis}; /// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It /// can be generated via the [`get_borrowed_locals`] function. pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitSet) { - let mut live = MaybeTransitiveLiveLocals::new(borrowed, &body.local_decls, tcx) + let mut live = MaybeTransitiveLiveLocals::new(borrowed) .into_engine(tcx, body) .iterate_to_fixpoint() .into_results_cursor(body); @@ -34,7 +34,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { let loc = Location { block: bb, statement_index }; if let StatementKind::Assign(assign) = &statement.kind { - if assign.1.is_pointer_int_cast(&body.local_decls, tcx) { + if assign.1.is_pointer_int_cast() { continue; } } diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff index 466c286c9d7c9..4fdd4b2b4bb86 100644 --- a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff @@ -21,7 +21,7 @@ // + span: $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 // + literal: Const { ty: &i32, val: Unevaluated(FOO, [], None) } _2 = &raw const (*_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 - _1 = move _2 as usize (Misc); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:39 + _1 = move _2 as usize (PointerAddress); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:39 StorageDead(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:38: 7:39 StorageDead(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:39: 7:40 StorageLive(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff b/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff index 1aeeae91d203f..7eb34ed5469ba 100644 --- a/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff @@ -17,7 +17,7 @@ // mir::Constant // + span: $DIR/reify_fn_ptr.rs:4:13: 4:17 // + literal: Const { ty: fn() {main}, val: Value(Scalar()) } - _2 = move _3 as usize (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + _2 = move _3 as usize (PointerAddress); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 StorageDead(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:25: 4:26 _1 = move _2 as *const fn() (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 StorageDead(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:40: 4:41 diff --git a/src/test/mir-opt/dead-store-elimination/provenance_soundness.pointer_to_int.DeadStoreElimination.diff b/src/test/mir-opt/dead-store-elimination/provenance_soundness.pointer_to_int.DeadStoreElimination.diff index 2250159c81667..bf32245e30098 100644 --- a/src/test/mir-opt/dead-store-elimination/provenance_soundness.pointer_to_int.DeadStoreElimination.diff +++ b/src/test/mir-opt/dead-store-elimination/provenance_soundness.pointer_to_int.DeadStoreElimination.diff @@ -19,12 +19,12 @@ StorageLive(_2); // scope 0 at $DIR/provenance_soundness.rs:8:9: 8:11 StorageLive(_3); // scope 0 at $DIR/provenance_soundness.rs:8:14: 8:15 _3 = _1; // scope 0 at $DIR/provenance_soundness.rs:8:14: 8:15 - _2 = move _3 as usize (Misc); // scope 0 at $DIR/provenance_soundness.rs:8:14: 8:24 + _2 = move _3 as usize (PointerAddress); // scope 0 at $DIR/provenance_soundness.rs:8:14: 8:24 StorageDead(_3); // scope 0 at $DIR/provenance_soundness.rs:8:23: 8:24 StorageLive(_4); // scope 1 at $DIR/provenance_soundness.rs:9:9: 9:11 StorageLive(_5); // scope 1 at $DIR/provenance_soundness.rs:9:14: 9:15 _5 = _1; // scope 1 at $DIR/provenance_soundness.rs:9:14: 9:15 - _4 = move _5 as isize (Misc); // scope 1 at $DIR/provenance_soundness.rs:9:14: 9:24 + _4 = move _5 as isize (PointerAddress); // scope 1 at $DIR/provenance_soundness.rs:9:14: 9:24 StorageDead(_5); // scope 1 at $DIR/provenance_soundness.rs:9:23: 9:24 _0 = const (); // scope 0 at $DIR/provenance_soundness.rs:7:32: 10:2 StorageDead(_4); // scope 1 at $DIR/provenance_soundness.rs:10:1: 10:2 diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 78d8f1e213af0..283b20fc24d82 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -125,16 +125,11 @@ fn check_rvalue<'tcx>( Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { check_place(tcx, *place, span, body) }, - Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { - use rustc_middle::ty::cast::CastTy; - let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast"); - match (cast_in, cast_out) { - (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { - Err((span, "casting pointers to ints is unstable in const fn".into())) - }, - _ => check_operand(tcx, operand, span, body), - } + Rvalue::Cast(CastKind::PointerAddress, _, _) => { + Err((span, "casting pointers to ints is unstable in const fn".into())) + }, + Rvalue::Cast(CastKind::Misc, operand, _) => { + check_operand(tcx, operand, span, body) }, Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { check_operand(tcx, operand, span, body) From e6b1003c950221a4afac33ecd64877c3ca56ddf8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 May 2022 17:56:35 -0700 Subject: [PATCH 3/5] BTreeSet->BTreeMap (fix copy/paste mistake in documentation) Co-authored-by: lcnr --- library/alloc/src/collections/btree/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 28b44e3acb9da..6027991a0ed2f 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -65,7 +65,7 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. /// The behavior resulting from such a logic error is not specified, but will be encapsulated to the -/// `BTreeSet` that observed the logic error and not result in undefined behavior. This could +/// `BTreeMap` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// /// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::values`], or From 5dae6c1b96cb832d20c2561852fa3504a341d4ad Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 31 May 2022 12:28:57 +0200 Subject: [PATCH 4/5] alloc: remove repeated word in comment Linux's `checkpatch.pl` reports: ```txt #42544: FILE: rust/alloc/vec/mod.rs:2692: WARNING: Possible repeated word: 'to' + // - Elements are :Copy so it's OK to to copy them, without doing ``` Signed-off-by: Miguel Ojeda --- library/alloc/src/vec/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index d222fcb445f58..fa3fbb865827a 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2470,7 +2470,7 @@ impl ExtendFromWithinSpec for Vec { // SAFETY: // - Both pointers are created from unique slice references (`&mut [_]`) // so they are valid and do not overlap. - // - Elements are :Copy so it's OK to to copy them, without doing + // - Elements are :Copy so it's OK to copy them, without doing // anything with the original values // - `count` is equal to the len of `source`, so source is valid for // `count` reads From cf66d9135a70bfd2d000913305eb09230952abb8 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 31 May 2022 20:35:41 +0900 Subject: [PATCH 5/5] Add regression test for #71546 --- src/test/ui/borrowck/issue-71546.rs | 16 ++++++++++++++++ src/test/ui/borrowck/issue-71546.stderr | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/test/ui/borrowck/issue-71546.rs create mode 100644 src/test/ui/borrowck/issue-71546.stderr diff --git a/src/test/ui/borrowck/issue-71546.rs b/src/test/ui/borrowck/issue-71546.rs new file mode 100644 index 0000000000000..4486ec5b081e3 --- /dev/null +++ b/src/test/ui/borrowck/issue-71546.rs @@ -0,0 +1,16 @@ +// Regression test for #71546. + +pub fn serialize_as_csv(value: &V) -> Result +where + V: 'static, + for<'a> &'a V: IntoIterator, + for<'a> <&'a V as IntoIterator>::Item: ToString + 'static, +{ + let csv_str: String = value //~ ERROR: the associated type `<&'a V as IntoIterator>::Item` may not live long enough + .into_iter() + .map(|elem| elem.to_string()) + .collect::(); + Ok(csv_str) +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-71546.stderr b/src/test/ui/borrowck/issue-71546.stderr new file mode 100644 index 0000000000000..a3136b6298621 --- /dev/null +++ b/src/test/ui/borrowck/issue-71546.stderr @@ -0,0 +1,20 @@ +error[E0310]: the associated type `<&'a V as IntoIterator>::Item` may not live long enough + --> $DIR/issue-71546.rs:9:27 + | +LL | let csv_str: String = value + | ___________________________^ +LL | | .into_iter() +LL | | .map(|elem| elem.to_string()) + | |_____________________________________^ + | + = help: consider adding an explicit lifetime bound `<&'a V as IntoIterator>::Item: 'static`... + = note: ...so that the type `<&'a V as IntoIterator>::Item` will meet its required lifetime bounds... +note: ...that is required by this bound + --> $DIR/issue-71546.rs:7:55 + | +LL | for<'a> <&'a V as IntoIterator>::Item: ToString + 'static, + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`.