diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ffa067479773f..d9ae0aa0b2307 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -2,6 +2,7 @@ use rustc_feature::AttributeStability; use rustc_hir::attrs::{ CoverageAttrKind, InstrumentFnAttr, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy, }; +use rustc_hir::find_attr; use rustc_session::diagnostics::feature_err; use rustc_span::edition::Edition::Edition2024; @@ -11,6 +12,7 @@ use crate::session_diagnostics::{ EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem, + TrackCallerOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -346,6 +348,25 @@ impl NoArgsAttributeParser for TrackCallerParser { ]); const STABILITY: AttributeStability = AttributeStability::Stable; const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller; + + fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + match cx.target { + Target::Fn => { + // `#[track_caller]` is not valid on weak lang items because they are called via + // `extern` declarations and `#[track_caller]` would alter their ABI. + if let Some(item) = find_attr!(cx.parsed_attrs, Lang(item) => item) + && item.is_weak() + { + cx.emit_err(TrackCallerOnLangItem { + attr_span, + name: item.name(), + sig_span: cx.target_span, + }); + } + } + _ => {} + } + } } pub(crate) struct NoMangleParser; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b09fbc015807a..3a1ffcb47844e 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -118,6 +118,26 @@ pub(crate) struct TargetFeatureOnLangItem { pub item_span: Span, } +#[derive(Diagnostic)] +#[diag( + "{$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item +} function is not allowed to have `#[track_caller]`" +)] +pub(crate) struct TrackCallerOnLangItem { + #[primary_span] + pub attr_span: Span, + pub name: Symbol, + #[label( + "{$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item + } function is not allowed to have `#[track_caller]`" + )] + pub sig_span: Span, +} + #[derive(Diagnostic)] #[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f3eb55afa4ec7..10e20ac06864f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1604,8 +1604,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ), } } - CastKind::Subtype => { - bug!("CastKind::Subtype shouldn't exist in borrowck") + CastKind::Subtype | CastKind::BoxDerefTransmute => { + bug!("CastKind::{cast_kind:?} shouldn't exist in borrowck") } } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 451983a9053b8..d57f662fc1938 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -823,7 +823,11 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let operand = codegen_operand(fx, operand); crate::unsize::coerce_unsized_into(fx, operand, lval); } - Rvalue::Cast(CastKind::Transmute | CastKind::Subtype, ref operand, _to_ty) => { + Rvalue::Cast( + CastKind::Transmute | CastKind::BoxDerefTransmute | CastKind::Subtype, + ref operand, + _to_ty, + ) => { let operand = codegen_operand(fx, operand); lval.write_cvalue_transmute(fx, operand); } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index a5d78fc8a7f35..344a4834862e4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -619,7 +619,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("Unsupported cast of {operand:?} to {cast:?}"); }) } - mir::CastKind::Transmute | mir::CastKind::Subtype => { + mir::CastKind::Transmute | mir::CastKind::BoxDerefTransmute | mir::CastKind::Subtype => { self.codegen_transmute_operand(bx, operand, cast) } }; diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 8c0500e0e6593..d5c7a9762cd4a 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -17,7 +17,7 @@ use super::{ throw_ub_format, }; use crate::enter_trace_span; -use crate::interpret::Writeable; +use crate::interpret::{Projectable, Writeable}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn cast( @@ -31,10 +31,66 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // possible. let cast_layout = if cast_ty == dest.layout.ty { dest.layout } else { self.layout_of(cast_ty)? }; - // FIXME: In which cases should we trigger UB when the source is uninit? + + // Check that the input is valid. + // Can be skipped for transmuts and unsizing as those do validation below. + if !matches!( + cast_kind, + CastKind::Transmute + | CastKind::Subtype + | CastKind::BoxDerefTransmute + | CastKind::PointerCoercion(PointerCoercion::Unsize, _) + ) && M::enforce_validity(self, src.layout) + { + match src.layout.ty.kind() { + ty::RawPtr { .. } => { + // We only need to check anything for wide pointers. + if matches!(src.layout.backend_repr, rustc_abi::BackendRepr::ScalarPair { .. }) + { + self.deref_pointer(src)?; + } + } + ty::FnPtr { .. } => { + let ptr = self.read_pointer(src)?; + self.get_ptr_fn(ptr)?; + } + ty::Closure(_closure, args) => { + // Can only happen for non-capturing closures, which have nothing to validate. + let args = args.as_closure(); + assert!(args.upvar_tys().is_empty()); + } + // Types that have no requirements or whose requirements are checked by the actual + // cast operation. + ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Bool + | ty::Char + | ty::FnDef(..) => {} + + _ => { + span_bug!( + self.cur_span(), + "unexpected input type in non-transmute/unsize cast: {}", + src.layout.ty + ) + } + } + } + match cast_kind { CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { self.unsize_into(src, cast_layout, dest)?; + // Validate the entire thing and reset any padding in the output. + // It is enough to validate the output because we are only adding metadata, + // not discarding anything from the input that may have been invalid. + if M::enforce_validity(self, dest.layout()) { + self.validate_place( + dest, + M::enforce_validity_recursively(self, dest.layout()), + /*reset_provenance_and_padding*/ true, + )?; + } } CastKind::PointerExposeProvenance => { @@ -133,7 +189,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::Transmute | CastKind::Subtype => { + CastKind::Transmute | CastKind::Subtype | CastKind::BoxDerefTransmute => { assert!(src.layout.is_sized()); assert!(dest.layout.is_sized()); assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely... @@ -147,6 +203,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); } + if matches!(cast_kind, CastKind::BoxDerefTransmute) { + // Do the extra UB checking by making the input an actual `Box` pointer + // and dereferencing it. + let ptr = self.read_immediate(src)?; + let pointee_ty = cast_ty.builtin_deref(true).unwrap(); + let box_ty = Ty::new_box(*self.tcx, pointee_ty); + let ptr = ptr.transmute(self.layout_of(box_ty)?, self)?; + self.deref_pointer(&ptr)?; + } + + // This does validation at `src` and `dest` type. self.copy_op_allow_transmute(src, dest)?; } } @@ -266,6 +333,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.backend_repr.is_signed(); // Also asserts that abi is `Scalar`. + // We go through the actual type of `src` to ensure the value is valid. let v = match src_layout.ty.kind() { ty::Uint(_) | ty::RawPtr(..) | ty::FnPtr(..) => scalar.to_uint(src_layout.size)?, ty::Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters @@ -458,6 +526,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// Perform an unsizing coercion. The caller is responsible for checking validity afterwards! pub fn unsize_into( &mut self, src: &OpTy<'tcx, M::Provenance>, @@ -489,7 +558,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if src_field.layout.is_1zst() && cast_ty_field.is_1zst() { // Skip 1-ZST fields. } else if src_field.layout.ty == cast_ty_field.ty { - self.copy_op(&src_field, &dst_field)?; + // The caller performs validation. + self.copy_op_no_validate( + &src_field, &dst_field, /* allow_transmute */ false, + )?; } else { if found_cast_field { span_bug!(self.cur_span(), "unsize_into: more than one field to cast"); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 5b6bc54897dab..f8c308ece55b7 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -476,7 +476,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check that the memory between them is dereferenceable at all, starting from the // origin pointer: `dist` is `a - b`, so it is based on `b`. - self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable) + self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable("pointer")) .map_err_kind(|_| { // This could mean they point to different allocations, or they point to the same allocation // but not the entire range between the pointers is in-bounds. @@ -498,7 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.check_ptr_access_signed( a, dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large - CheckInAllocMsg::Dereferenceable, + CheckInAllocMsg::Dereferenceable("pointer"), ) .map_err_kind(|_| { // Make the error more specific. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 0598fbfad6e4d..3f353d50f191a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1070,14 +1070,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { expected_trait: Option<&'tcx ty::List>>, ) -> InterpResult<'tcx, Ty<'tcx>> { trace!("get_ptr_vtable({:?})", ptr); - let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr, 0)?; + let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr, 0).map_err_kind(|err| { + let err_ub!(DanglingIntPointer { addr, .. }) = err else { bug!() }; + err_ub!(InvalidVTablePointer(Pointer::without_provenance(addr))) + })?; if offset.bytes() != 0 { - throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) + throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset).into())) } let Some(GlobalAlloc::VTable(ty, vtable_dyn_type)) = self.tcx.try_get_global_alloc(alloc_id) else { - throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) + throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset).into())) }; if let Some(expected_dyn_type) = expected_trait { self.check_vtable_for_type(vtable_dyn_type, expected_dyn_type)?; @@ -1719,11 +1722,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: i64, ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> { self.ptr_try_get_alloc_id(ptr, size) - .map_err(|offset| { + .map_err(|addr| { err_ub!(DanglingIntPointer { - addr: offset, + addr, inbounds_size: size, - msg: CheckInAllocMsg::Dereferenceable + msg: CheckInAllocMsg::Dereferenceable("pointer") }) }) .into() diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 0c1e171c0edc9..7ef1597c1c06f 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -640,7 +640,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// 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. /// Note that for a given layout, this operation will either always return Left or Right! - /// succeed! Whether it returns Left depends on whether the layout can be represented + /// Whether it returns Left depends on whether the layout can be represented /// 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. diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 95e513cc97b4e..5739b4a005491 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -441,8 +441,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - /// Returns the result of the specified operation, whether it overflowed, and - /// the result type. + /// Returns the result of the specified operation. pub fn unary_op( &self, un_op: mir::UnOp, @@ -499,6 +498,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } ty::RawPtr(..) | ty::Ref(..) => { assert_eq!(un_op, PtrMetadata); + self.deref_pointer(val)?; // validity check let (_, meta) = val.to_scalar_and_meta(); interp_ok(match meta { MemPlaceMeta::Meta(scalar) => { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index d317419ff7e2b..49e9e74191f39 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -13,9 +13,10 @@ use tracing::field::Empty; use tracing::{instrument, trace}; use super::{ - AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, - InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, - Projectable, Provenance, Scalar, alloc_range, interp_ok, mir_assign_valid_types, + AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CheckInAllocMsg, CtfeProvenance, ImmTy, + Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, + Operand, Pointer, Projectable, Provenance, Scalar, alloc_range, err_ub, err_ub_format, + interp_ok, mir_assign_valid_types, throw_ub_format, }; use crate::enter_trace_span; @@ -462,22 +463,74 @@ where /// Take an operand, representing a pointer, and dereference it to a place. /// Corresponds to the `*` operator in Rust. + /// Unlike `imm_ptr_to_mplace`, this checks that the pointer is valid for its type. #[instrument(skip(self), level = "trace")] pub fn deref_pointer( &self, src: &impl Projectable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - if src.layout().ty.is_box() { - // Derefer should have removed all Box derefs. - // Some `Box` are not immediates (if they have a custom allocator) - // so the code below would fail. + let ptr_ty = src.layout().ty; + if !(ptr_ty.is_ref() || ptr_ty.is_raw_ptr() || ptr_ty.is_box_global(*self.tcx)) { bug!("dereferencing {}", src.layout().ty); } let val = self.read_immediate(src)?; + // Construct a place for that pointer. trace!("deref to {} on {:?}", val.layout.ty, *val); - let mplace = self.imm_ptr_to_mplace(&val)?; + + // This is conceptually a typed load from `src` to get the pointer. Most of the time when + // we do typed loads for primitive operations, all relevant invariants are checked + // implicitly, e.g. when we call `to_bool()` on a Boolean. + // But here, we do need to specifically check for metadata validity, null, alignment, and + // dereferenceability, or they will not be checked anywhere at all. + // This duplicates some of the logic in the validity check, but so far we found no + // good way to share that logic. + if ptr_ty.is_ref() || ptr_ty.is_box() { + let kind = if ptr_ty.is_ref() { "reference" } else { "box" }; + + // Null check. + let scalar_ptr = Scalar::from_maybe_pointer(mplace.ptr(), self); + if self.scalar_may_be_null(scalar_ptr)? { + let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar_ptr, Scalar::Ptr(..)); + throw_ub_format!( + "dereferencing a {maybe}null {kind}", + maybe = if maybe { "maybe-" } else { "" } + ); + } + + // Dereferencability and alignment check. This also implicitly checks metadata validity. + let (size, align) = self + .size_and_align_of_val(&mplace)? + .unwrap_or_else(|| (mplace.layout.size, mplace.layout.align.abi)); + self.check_ptr_access(mplace.ptr(), size, CheckInAllocMsg::Dereferenceable(kind))?; + self.check_ptr_align(mplace.ptr(), align).map_err_kind(|err| { + let err_ub!(AlignmentCheckFailed(Misalignment { required, has }, _msg)) = err else { bug!() }; + err_ub_format!( + "encountered an unaligned {kind} (required {required_bytes} byte alignment but found {found_bytes})", + required_bytes = required.bytes(), + found_bytes = has.bytes() + ) + })?; + } else { + assert!(ptr_ty.is_raw_ptr()); + // For raw pointers, the validity invariant is pretty weak, but we do require the vtable + // to make sense, so we do have to check that if there is one. + if mplace.layout.is_unsized() { + let tail = self.tcx.struct_tail_for_codegen(mplace.layout.ty, self.typing_env); + match tail.kind() { + ty::Dynamic(data, _) => { + let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; + self.get_ptr_vtable_ty(vtable, Some(data))?; + } + ty::Slice(..) | ty::Str | ty::Foreign(..) => { + // Nothing to check (`read_immediate` already ensured initialization). + } + _ => bug!("Unexpected unsized type tail: {:?}", tail), + } + } + } + interp_ok(mplace) } @@ -831,8 +884,13 @@ where self.copy_op_inner(src, dest, /* allow_transmute */ false) } - /// Copies the data from an operand to a place. - /// `allow_transmute` indicates whether the layouts may disagree. + /// Perform a typed copy of the data from an operand to a place. + /// + /// `allow_transmute` indicates whether the layouts may disagree. In that case there are + /// technically *two* typed copies: `src` is a not-yet-loaded value, so we're doing a typed copy + /// at `src` type from there to some intermediate storage. And then we're doing a second typed + /// copy at `dest` type from that intermediate storage to `dest`. As an optimization, we only + /// make a single direct copy here, but we still have to ensure the data is valid at both types. #[inline(always)] #[instrument(skip(self), level = "trace")] fn copy_op_inner( @@ -841,11 +899,6 @@ where dest: &impl Writeable<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { - // These are technically *two* typed copies: `src` is a not-yet-loaded value, - // so we're doing a typed copy at `src` type from there to some intermediate storage. - // And then we're doing a second typed copy at `dest` type from that intermediate storage to - // `dest`. But as an optimization, we only make a single direct copy here. - // Do the actual copy. self.copy_op_no_validate(src, dest, allow_transmute)?; @@ -874,10 +927,10 @@ where interp_ok(()) } - /// Copies the data from an operand to a place. + /// Perform an untyped copy of the data from an operand to a place. + /// You are responsible for validating that things get copied at the right type. + /// /// `allow_transmute` indicates whether the layouts may disagree. - /// Also, if you use this you are responsible for validating that things get copied at the - /// right type. #[instrument(skip(self), level = "trace")] pub(super) fn copy_op_no_validate( &mut self, diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 27f91b2b89b2c..e4b6ff167c1bd 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -427,4 +427,22 @@ where Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?, }) } + + /// Given a value of type `Box`, returns the inner value of raw pointer type, as well as + /// the allocator. + pub(super) fn project_to_ptr_in_box>( + &self, + box_: &P, + ) -> InterpResult<'tcx, (P, P)> { + // `Box` has two fields: the pointer we care about, and the allocator. + assert_eq!(box_.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); + let [ptr, alloc] = self.project_fields(box_, [FieldIdx::ZERO, FieldIdx::ONE])?; + + // We simply transmute the pointer we care about to the underlying raw pointer. + // (We could project a bit but that would end up at a pattern type that needs a transmute.) + let pointee_ty = box_.layout().ty.boxed_ty().unwrap(); + let raw_ptr_ty = Ty::new_ptr(*self.tcx, pointee_ty, ty::Mutability::Mut); + let raw_ptr = ptr.transmute(self.layout_of(raw_ptr_ty)?, self)?; // The actual raw pointer + interp_ok((raw_ptr, alloc)) + } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 0ffe22d42eb38..9c3ccafa6e41c 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -565,6 +565,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ty: Ty<'tcx>, ptr_kind: PtrKind, ) -> InterpResult<'tcx> { + // Note that some of those checks (those that encode the basic validity invariant of + // pointers) are duplicated in `place_deref`, so changes here might need updates there. let ptr = self.read_immediate(value, ptr_kind.into())?; if self.reset_provenance_and_padding { // There's no padding in a pointer. @@ -604,7 +606,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.check_ptr_access( place.ptr(), size, - CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message + CheckInAllocMsg::Dereferenceable("pointer"), // will anyway be replaced by validity message ), self.path, Ub(DanglingIntPointer { addr: 0, .. }) => diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 92d13b30c5fff..c80a082674141 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -3,7 +3,7 @@ use std::num::NonZero; -use rustc_abi::{FieldIdx, FieldsShape, VariantIdx, Variants}; +use rustc_abi::{FieldsShape, VariantIdx, Variants}; use rustc_middle::mir::interpret::InterpResult; use rustc_middle::ty::{self, Ty}; use tracing::trace; @@ -12,6 +12,9 @@ use super::{InterpCx, MPlaceTy, Machine, Projectable, interp_ok, throw_inval}; /// How to traverse a value and what to do when we are at the leaves. pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { + // The `From` rules out `ImmTy`... we could use a `TryFrom` instead since the only + // case we need this is visiting something unsized which cannot happen when visiting an `ImmTy`. + // But so far this was just not needed. type V: Projectable<'tcx, M::Provenance> + From>; /// The visitor must have an `InterpCx` in it. @@ -111,33 +114,9 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // allocator field. We also assert tons of things to ensure we do not miss // any other fields. - // `Box` has two fields: the pointer we care about, and the allocator. - assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); - let [unique_ptr, alloc] = - self.ecx().project_fields(v, [FieldIdx::ZERO, FieldIdx::ONE])?; - - // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... - // (which means another 2 fields, the second of which is a `PhantomData`) - assert_eq!(unique_ptr.layout().fields.count(), 2); - let [nonnull_ptr, phantom] = - self.ecx().project_fields(&unique_ptr, [FieldIdx::ZERO, FieldIdx::ONE])?; - assert!( - phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), - "2nd field of `Unique` should be PhantomData but is {:?}", - phantom.layout().ty, - ); - - // ... that contains a `NonNull` whose only field finally is a raw ptr we can - // dereference. - assert_eq!(nonnull_ptr.layout().fields.count(), 1); - let pat_ptr = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // `*mut T is !null` - let base = match *pat_ptr.layout().ty.kind() { - ty::Pat(base, _) => self.ecx().layout_of(base)?, - _ => unreachable!(), - }; - let raw_ptr = pat_ptr.transmute(base, self.ecx())?; // The actual raw pointer - - // Hand this actual pointer to the visitor. + let (raw_ptr, alloc) = self.ecx().project_to_ptr_in_box(v)?; + + // Hand the actual pointer to the visitor. self.visit_box(ty, &raw_ptr)?; // The second `Box` field is the allocator, which we recursively check for validity diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index d310c6adb3e44..6986ee1aa837f 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -84,6 +84,7 @@ use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; @@ -104,6 +105,7 @@ use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys; use self::region::region_scope_tree; use crate::diagnostics::{ MissingTraitItemLabel, MissingTraitItemSuggestion, MissingTraitItemSuggestionNone, + MissingTraitItemSuggestionUnstable, }; use crate::{check_c_variadic_abi, diagnostics}; @@ -228,6 +230,7 @@ fn missing_items_suggestions( String, Vec, Vec, + Vec, Vec, ) { let missing_items = @@ -243,8 +246,12 @@ fn missing_items_suggestions( // Obtain the level of indentation ending in `sugg_sp`. let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new); - let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) = - (Vec::new(), Vec::new(), Vec::new()); + let ( + mut missing_trait_item, + mut missing_trait_item_none, + mut missing_trait_item_unstable, + mut missing_trait_item_label, + ) = (Vec::new(), Vec::new(), Vec::new(), Vec::new()); for &trait_item in missing_items { let snippet = with_types_for_signature!(suggestion_signature( @@ -262,20 +269,42 @@ fn missing_items_suggestions( snippet, }); } else { - missing_trait_item_none.push(diagnostics::MissingTraitItemSuggestionNone { - span: sugg_sp, - code, - snippet, - }) + if let EvalResult::Deny { feature, .. } = + tcx.eval_stability(trait_item.def_id, None, sugg_sp, None) + { + missing_trait_item_unstable.push(diagnostics::MissingTraitItemSuggestionUnstable { + span: sugg_sp, + code, + snippet, + feature, + }); + } else { + missing_trait_item_none.push(diagnostics::MissingTraitItemSuggestionNone { + span: sugg_sp, + code, + snippet, + }); + } } } - (missing_items_msg, missing_trait_item, missing_trait_item_none, missing_trait_item_label) + ( + missing_items_msg, + missing_trait_item, + missing_trait_item_none, + missing_trait_item_unstable, + missing_trait_item_label, + ) } fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem]) { - let (missing_items_msg, missing_trait_item, missing_trait_item_none, missing_trait_item_label) = - missing_items_suggestions(tcx, impl_def_id, missing_items); + let ( + missing_items_msg, + missing_trait_item, + missing_trait_item_none, + missing_trait_item_unstable, + missing_trait_item_label, + ) = missing_items_suggestions(tcx, impl_def_id, missing_items); tcx.dcx().emit_err(diagnostics::MissingTraitItem { span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(), @@ -283,6 +312,7 @@ fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ missing_trait_item_label, missing_trait_item, missing_trait_item_none, + missing_trait_item_unstable, }); } @@ -300,8 +330,13 @@ fn missing_items_must_implement_one_of_err( .cloned() .collect::>(); - let (missing_items_msg, missing_trait_item, missing_trait_item_none, missing_trait_item_label) = - missing_items_suggestions(tcx, impl_def_id, &missing_items); + let ( + missing_items_msg, + missing_trait_item, + missing_trait_item_none, + missing_trait_item_unstable, + missing_trait_item_label, + ) = missing_items_suggestions(tcx, impl_def_id, &missing_items); tcx.dcx().emit_err(diagnostics::MissingOneOfTraitItem { span: tcx.def_span(impl_def_id), @@ -309,6 +344,7 @@ fn missing_items_must_implement_one_of_err( missing_items_msg, missing_trait_item_label, missing_trait_item, + missing_trait_item_unstable, missing_trait_item_none, }) } diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index eb0dcb3a346b2..ab6fa34be9fbb 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -919,6 +919,8 @@ pub(crate) struct MissingTraitItem { pub missing_trait_item: Vec, #[subdiagnostic] pub missing_trait_item_none: Vec, + #[subdiagnostic] + pub missing_trait_item_unstable: Vec, pub missing_items_msg: String, } @@ -944,6 +946,21 @@ pub(crate) struct MissingTraitItemSuggestion { pub snippet: String, } +#[derive(Subdiagnostic)] +#[suggestion( + "implement the missing item: `{$snippet}` (unstable, requires feature `{$feature}`)", + style = "hidden", + applicability = "has-placeholders", + code = "{code}" +)] +pub(crate) struct MissingTraitItemSuggestionUnstable { + #[primary_span] + pub span: Span, + pub code: String, + pub snippet: String, + pub feature: Symbol, +} + #[derive(Subdiagnostic)] #[suggestion( "implement the missing item: `{$snippet}`", @@ -972,6 +989,8 @@ pub(crate) struct MissingOneOfTraitItem { pub missing_trait_item: Vec, #[subdiagnostic] pub missing_trait_item_none: Vec, + #[subdiagnostic] + pub missing_trait_item_unstable: Vec, pub missing_items_msg: String, } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index e2c67b29943d6..4636030e8717f 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -251,7 +251,8 @@ pub enum CheckInAllocMsg { /// We are doing pointer arithmetic. InboundsPointerArithmetic, /// None of the above -- generic/unspecific inbounds test. - Dereferenceable, + /// The string is the subject of the test, e.g. "pointer". + Dereferenceable(&'static str), } impl fmt::Display for CheckInAllocMsg { @@ -260,7 +261,7 @@ impl fmt::Display for CheckInAllocMsg { match self { MemoryAccess => write!(f, "memory access failed"), InboundsPointerArithmetic => write!(f, "in-bounds pointer arithmetic failed"), - Dereferenceable => write!(f, "pointer not dereferenceable"), + Dereferenceable(what) => write!(f, "{what} not dereferenceable"), } } } @@ -391,7 +392,7 @@ pub enum UndefinedBehaviorInfo<'tcx> { /// Using a pointer-not-to-a-va-list as variable argument list pointer. InvalidVaListPointer(Pointer), /// Using a pointer-not-to-a-vtable as vtable pointer. - InvalidVTablePointer(Pointer), + InvalidVTablePointer(Pointer>), /// Using a vtable for the wrong trait. InvalidVTableTrait { /// The vtable that was actually referenced by the wide pointer metadata. @@ -452,11 +453,11 @@ impl<'tcx> fmt::Display for UndefinedBehaviorInfo<'tcx> { CheckInAllocMsg::InboundsPointerArithmetic => { write!(f, "attempting to offset pointer by {inbounds_size_fmt}") } - CheckInAllocMsg::Dereferenceable if inbounds_size == 0 => { - write!(f, "pointer must point to some allocation") + CheckInAllocMsg::Dereferenceable(what) if inbounds_size == 0 => { + write!(f, "{what} must point to some allocation") } - CheckInAllocMsg::Dereferenceable => { - write!(f, "pointer must be dereferenceable for {inbounds_size_fmt}") + CheckInAllocMsg::Dereferenceable(what) => { + write!(f, "{what} must be dereferenceable for {inbounds_size_fmt}") } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 9712ac02775d4..17eeb7c3c12aa 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -792,6 +792,7 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::PointerCoercion(_, _) | CastKind::PointerWithExposedProvenance | CastKind::Transmute + | CastKind::BoxDerefTransmute | CastKind::Subtype, _, _, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 5771019ddca46..b005cf0c8d10f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1496,6 +1496,14 @@ pub enum CastKind { /// MIR is well-formed if the input and output types have different sizes, /// but running a transmute between differently-sized types is UB. Transmute, + /// A special transmute used by elaborated `box` deref's to turn the inner pointer into a raw + /// pointer. This is almost equivalent to a regular transmute except that if the input would not + /// be valid as `Box`, the cast is UB. Backends that do not care about UB detection can treat + /// this like a regular transmute. + /// + /// Well-formedness: The input type must be a pointer type or a newtype around one (e.g. + /// `NonNull`). The output type must be a raw pointer. + BoxDerefTransmute, /// A `Subtype` cast is applied to any [`StatementKind::Assign`] where /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index 717c1cb39cb61..5c925b9ecaa42 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> { location, Place::from(ptr_local), Rvalue::Cast( - CastKind::Transmute, + CastKind::BoxDerefTransmute, Operand::Copy( Place::from(place.local) .project_deeper(&build_projection(unique_ty, nonnull_ty), tcx), diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 56a9a179c8fcb..4413d5064bd14 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1386,7 +1386,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - CastKind::Transmute => { + CastKind::Transmute | CastKind::BoxDerefTransmute => { // Unlike `mem::transmute`, a MIR `Transmute` is well-formed // for any two `Sized` types, just potentially UB to run. @@ -1416,6 +1416,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { format!("Cannot transmute to non-`Sized` type {target_type:?}"), ); } + + if matches!(kind, CastKind::BoxDerefTransmute) { + if !target_type.is_raw_ptr() { + self.fail( + location, + format!( + "Cannot BoxDerefTransmute to non-pointer type {target_type}" + ), + ); + } + } } CastKind::Subtype => { if !util::sub_types(self.tcx, self.typing_env, op_ty, *target_type) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 51f3148784c5b..a55d38251c843 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -122,7 +122,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { match attr { Attribute::Parsed(attr_kind) => { - self.check_one_parsed_attribute(hir_id, span, target, item, attrs, attr_kind); + self.check_one_parsed_attribute(hir_id, span, target, item, attr_kind); self.check_unused_attribute(hir_id, attr, None); } Attribute::Unparsed(attr_item) => { @@ -173,7 +173,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, item: Option<&'tcx Item<'tcx>>, - attrs: &[Attribute], attr: &AttributeKind, ) { match attr { @@ -200,9 +199,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_dump_object_lifetime_defaults(hir_id); } AttributeKind::Naked(..) => self.check_naked(hir_id, target), - AttributeKind::TrackCaller(attr_span) => { - self.check_track_caller(hir_id, *attr_span, attrs, target) - } AttributeKind::NonExhaustive(attr_span) => { self.check_non_exhaustive(*attr_span, span, target, item) } @@ -404,6 +400,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::TargetFeature { .. } => {} AttributeKind::TestRunner(..) => (), AttributeKind::ThreadLocal => (), + AttributeKind::TrackCaller(_) => (), AttributeKind::TypeLengthLimit { .. } => (), AttributeKind::Unroll(..) => (), AttributeKind::UnstableFeatureBound(..) => (), @@ -774,34 +771,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if a `#[track_caller]` is applied to a function. - fn check_track_caller( - &self, - hir_id: HirId, - attr_span: Span, - attrs: &[Attribute], - target: Target, - ) { - match target { - Target::Fn => { - // `#[track_caller]` is not valid on weak lang items because they are called via - // `extern` declarations and `#[track_caller]` would alter their ABI. - if let Some(item) = find_attr!(attrs, Lang(item) => item) - && item.is_weak() - { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - - self.dcx().emit_err(diagnostics::LangItemWithTrackCaller { - attr_span, - name: item.name(), - sig_span: sig.span, - }); - } - } - _ => {} - } - } - /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. fn check_non_exhaustive( &self, diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 827469062f0ae..61a32c97b3cc6 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -324,26 +324,6 @@ pub(crate) struct MissingLangItem { pub name: Symbol, } -#[derive(Diagnostic)] -#[diag( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item -} function is not allowed to have `#[track_caller]`" -)] -pub(crate) struct LangItemWithTrackCaller { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[track_caller]`" - )] - pub sig_span: Span, -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index def6c837a2b85..a65798aff1a00 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -1087,6 +1087,7 @@ pub enum CastKind { PtrToPtr, FnPtrToPtr, Transmute, + BoxDerefTransmute, Subtype, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 0e901cf871126..124329526028d 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -365,6 +365,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { PtrToPtr => crate::mir::CastKind::PtrToPtr, FnPtrToPtr => crate::mir::CastKind::FnPtrToPtr, Transmute => crate::mir::CastKind::Transmute, + BoxDerefTransmute => crate::mir::CastKind::BoxDerefTransmute, Subtype => crate::mir::CastKind::Subtype, } } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 578a101b5f29a..deea6465118ed 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -529,6 +529,26 @@ impl<'a, Ty> ArgAbi<'a, Ty> { PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; } + /// Cast to `target`, forwarding `NoUndef` only when the layout provably has no uninit + /// bytes *and* the cast exactly covers the layout (`target.size(cx) == self.layout.size`). + /// A wider cast (e.g. `Uniform::new` rounding a 3-byte aggregate up to an `i32`) covers + /// undef padding bytes that must not be marked `noundef`; a narrower cast does not occur, + /// since a `PassMode::Cast` target always covers the whole value. + pub fn cast_to_maybe_noundef(&mut self, target: T, cx: &C) + where + T: Into, + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout, + { + let target = target.into(); + let attr = if layout_is_noundef(self.layout, cx) && target.size(cx) == self.layout.size { + ArgAttribute::NoUndef + } else { + ArgAttribute::default() + }; + self.cast_to_with_attrs(target, attr.into()); + } + pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: bool) { self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; } @@ -837,12 +857,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { // We want to pass small aggregates as immediates, but using // an LLVM aggregate type for this leads to bad optimizations, // so we pick an appropriately sized integer type instead. - let attr = if layout_is_noundef(arg.layout, cx) { - ArgAttribute::NoUndef - } else { - ArgAttribute::default() - }; - arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into()); + arg.cast_to_maybe_noundef(Reg { kind: RegKind::Integer, size }, cx); } else if self.conv == CanonAbi::RustTail { assert!(arg.layout.is_sized(), "extern \"tail\" arguments must be sized"); arg.pass_by_stack_offset(None); diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ddbdeacbdbfbd..7cfc43a29ce53 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1493,6 +1493,10 @@ impl Box { #[stable(feature = "box_vec_non_null", since = "CURRENT_RUSTC_VERSION")] #[inline] pub fn into_non_null(b: Self) -> NonNull { + // As of August 2026, we cannot utilize `Box::leak` + // because whether or not you can reconstruct the `Box` + // later using `Box::from_raw` or `Box::from_non_null` is + // an open question. // SAFETY: `Box` is guaranteed to be non-null. unsafe { NonNull::new_unchecked(Self::into_raw(b)) } } @@ -1892,12 +1896,12 @@ impl Box { /// has only static references, or none at all, then this may be chosen to be /// `'static`. /// - /// This function is mainly useful for data that lives for the remainder of - /// the program's life. Dropping the returned reference will cause a memory - /// leak. If this is not acceptable, the reference should first be wrapped - /// with the [`Box::from_raw`] function producing a `Box`. This `Box` can - /// then be dropped which will properly destroy `T` and release the - /// allocated memory. + /// This function is mainly useful for data that lives for the remainder of the program's life, + /// i.e., memory that is meant to leak. Reconstructing ("unleaking") a `Box` from the mutable + /// reference returned here (e.g. via [`Box::from_raw`]) is a grey area (meaning it is possible + /// under specific circumstances but many seemingly harmless ways of doing it are undefined + /// behavior) and should be avoided. If the memory should eventually be freed, prefer to use + /// [`Box::into_raw`] or [`Box::into_non_null`] instead. /// /// Note: this is an associated function, which means that you have /// to call it as `Box::leak(b)` instead of `b.leak()`. This diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index eb02664ee7e46..055d743dee6a1 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -259,8 +259,7 @@ fn custom_owner_from_box( drop(unsafe { Box::from_raw(ptr) }) } - // SAFETY: the pointer returned by Box::into_raw is non-null. - let error = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(error)) }; + let error = Box::into_non_null(error); // SAFETY: // * `error` is valid up to a static lifetime, and owns its pointee. @@ -269,8 +268,7 @@ fn custom_owner_from_box( // and will be stored in a `CustomOwner`. let custom = unsafe { Custom::from_raw(kind, error, drop_box_raw, drop_box_raw) }; - // SAFETY: the pointer returned by Box::into_raw is non-null. - let custom = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(custom))) }; + let custom = Box::into_non_null(Box::new(custom)); // SAFETY: the `outer_drop` provided to `custom` is valid for itself. unsafe { CustomOwner::from_raw(custom) } diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index c0123c860b453..490a55f9d10dc 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -1031,19 +1031,11 @@ pub(super) fn default_read_buf_exact( Ok(()) } -mod sealed { - /// This trait being unreachable from outside the crate - /// prevents outside implementations of our extension traits. - /// This allows adding more trait methods in the future. - #[unstable(feature = "sealed", issue = "none")] - pub trait Sealed {} -} - /// Trait for types that can be converted from a fixed-size byte array with a specified endianness #[unstable(feature = "read_le_be_internals", reason = "internals", issue = "none")] // Once we can use associated consts in the types of method parameters, rewrite this to have // `from_le_bytes` and `from_be_bytes` methods, move it to `core`, and make it public. -pub trait FromEndianBytes: sealed::Sealed + Sized { +pub impl(self) trait FromEndianBytes: Sized { #[doc(hidden)] fn read_le_from(r: &mut impl Read) -> Result; @@ -1053,9 +1045,6 @@ pub trait FromEndianBytes: sealed::Sealed + Sized { macro_rules! impl_from_endian_bytes { ($($t:ty),*$(,)?) => {$( - #[unstable(feature = "sealed", issue = "none")] - impl sealed::Sealed for $t {} - #[unstable(feature = "read_le_be_internals", reason = "internals", issue = "none")] impl FromEndianBytes for $t { #[inline] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 75cd7397e8c5d..5cf05071bc329 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -146,7 +146,6 @@ #![feature(io_slice_as_bytes)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] -#![feature(layout_for_ptr)] #![feature(legacy_receiver_trait)] #![feature(likely_unlikely)] #![feature(local_waker)] @@ -203,6 +202,7 @@ #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(fundamental)] +#![feature(impl_restriction)] #![feature(intrinsics)] #![feature(lang_items)] #![feature(min_specialization)] diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 9c0517c3ee2f8..da77bbad6ce7f 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -232,29 +232,33 @@ impl Layout { /// /// - If `T` is `Sized`, this function is always safe to call. /// - If the unsized tail of `T` is: - /// - a [slice], then the length of the slice tail must be an initialized - /// integer, and the size of the *entire value* + /// - a [slice] `[U]`, `str`, or a [trait object] `dyn Trait`, then the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. /// For the special case where the dynamic tail length is 0, this function /// is safe to call. - /// - a [trait object], then the vtable part of the pointer must point - /// to a valid vtable for the type `T` acquired by an unsizing coercion, - /// and the size of the *entire value* - /// (dynamic tail length + statically sized prefix) must fit in `isize`. - /// - an (unstable) [extern type], then this function is always safe to - /// call, but may panic or otherwise return the wrong value, as the - /// extern type's layout is not known. This is the same behavior as - /// [`Layout::for_value`] on a reference to an extern type tail. - /// - otherwise, it is conservatively not allowed to call this function. + // NOTE: the reason this is safe is that if an overflow were to occur already with size 0, + // then we would stop compilation as even the "statically known" part of the type would + // already be too big (or the call may be in dead code and optimized away, but then it + // doesn't matter). + /// - No other kind of unsized tail currently exists that satisfies the trait bounds for this + /// function. If more kinds of unsized tails get introduced in the future, the documentation + /// of this function will have to be extended before it can be used for such types. + /// + /// Here, *unsized tail* refers to the type obtained by recursively descending through the last + /// field of a tuple or struct until we arrived at a built-in unsized type. + /// + /// As a consequence of these rules, it is the case that whenever it is allowed to convert `val` + /// into a shared reference, then it is also allowed to invoke this function. /// /// [trait object]: ../../book/ch17-02-trait-objects.html /// [extern type]: ../../unstable-book/language-features/extern-types.html - #[unstable(feature = "layout_for_ptr", issue = "69835")] + #[stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] - pub const unsafe fn for_value_raw(t: *const T) -> Self { + pub const unsafe fn for_value_raw(val: *const T) -> Self { // SAFETY: we pass along the prerequisites of these functions to the caller - let (size, alignment) = unsafe { (mem::size_of_val_raw(t), Alignment::of_val_raw(t)) }; + let (size, alignment) = unsafe { (mem::size_of_val_raw(val), Alignment::of_val_raw(val)) }; // SAFETY: see rationale in `new` for why this is using the unsafe variant unsafe { Layout::from_size_alignment_unchecked(size, alignment) } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index be5b9d91c410f..9a2ad0004ae4c 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -425,9 +425,8 @@ pub const fn size_of_val(val: &T) -> usize { /// Otherwise, the following conditions must hold: /// /// - If `T` is `Sized`, this function is always safe to call. -/// - If the unsized tail of `T` is: -/// - a [slice], then the length of the slice tail must be an initialized -/// integer, and the size of the *entire value* +/// - If the *unsized tail* of `T` is: +/// - a [slice] `[U]`, `str`, or a [trait object] `dyn Trait`, then the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. /// For the special case where the dynamic tail length is 0, this function /// is safe to call. @@ -435,15 +434,15 @@ pub const fn size_of_val(val: &T) -> usize { // then we would stop compilation as even the "statically known" part of the type would // already be too big (or the call may be in dead code and optimized away, but then it // doesn't matter). -/// - a [trait object], then the vtable part of the pointer must point -/// to a valid vtable acquired by an unsizing coercion, and the size -/// of the *entire value* (dynamic tail length + statically sized prefix) -/// must fit in `isize`. -/// - an (unstable) [extern type], then this function is always safe to -/// call, but may panic or otherwise return the wrong value, as the -/// extern type's layout is not known. This is the same behavior as -/// [`size_of_val`] on a reference to a type with an extern type tail. -/// - otherwise, it is conservatively not allowed to call this function. +/// - No other kind of unsized tail currently exists that satisfies the trait bounds for this +/// function. If more kinds of unsized tails get introduced in the future, the documentation +/// of this function will have to be extended before it can be used for such types. +/// +/// Here, *unsized tail* refers to the type obtained by recursively descending through the last +/// field of a tuple or struct until we arrived at a built-in unsized type. +/// +/// As a consequence of these rules, it is the case that whenever it is allowed to convert `val` +/// into a shared reference, then it is also allowed to invoke this function. /// /// [`size_of::()`]: size_of /// [trait object]: ../../book/ch17-02-trait-objects.html @@ -452,7 +451,6 @@ pub const fn size_of_val(val: &T) -> usize { /// # Examples /// /// ``` -/// #![feature(layout_for_ptr)] /// use std::mem; /// /// assert_eq!(4, size_of_val(&5i32)); @@ -463,7 +461,8 @@ pub const fn size_of_val(val: &T) -> usize { /// ``` #[inline] #[must_use] -#[unstable(feature = "layout_for_ptr", issue = "69835")] +#[stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn size_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::size_of_val(val) } @@ -601,20 +600,23 @@ pub const fn align_of_val(val: &T) -> usize { /// /// - If `T` is `Sized`, this function is always safe to call. /// - If the unsized tail of `T` is: -/// - a [slice], then the length of the slice tail must be an initialized -/// integer, and the size of the *entire value* +/// - a [slice] `[U]`, `str`, or a [trait object] `dyn Trait`, then the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. /// For the special case where the dynamic tail length is 0, this function /// is safe to call. -/// - a [trait object], then the vtable part of the pointer must point -/// to a valid vtable acquired by an unsizing coercion, and the size -/// of the *entire value* (dynamic tail length + statically sized prefix) -/// must fit in `isize`. -/// - an (unstable) [extern type], then this function is always safe to -/// call, but may panic or otherwise return the wrong value, as the -/// extern type's layout is not known. This is the same behavior as -/// [`align_of_val`] on a reference to a type with an extern type tail. -/// - otherwise, it is conservatively not allowed to call this function. +// NOTE: the reason this is safe is that if an overflow were to occur already with size 0, +// then we would stop compilation as even the "statically known" part of the type would +// already be too big (or the call may be in dead code and optimized away, but then it +// doesn't matter). +/// - No other kind of unsized tail currently exists that satisfies the trait bounds for this +/// function. If more kinds of unsized tails get introduced in the future, the documentation +/// of this function will have to be extended before it can be used for such types. +/// +/// Here, *unsized tail* refers to the type obtained by recursively descending through the last +/// field of a tuple or struct until we arrived at a built-in unsized type. +/// +/// As a consequence of these rules, it is the case that whenever it is allowed to convert `val` +/// into a shared reference, then it is also allowed to invoke this function. /// /// [trait object]: ../../book/ch17-02-trait-objects.html /// [extern type]: ../../unstable-book/language-features/extern-types.html @@ -622,7 +624,6 @@ pub const fn align_of_val(val: &T) -> usize { /// # Examples /// /// ``` -/// #![feature(layout_for_ptr)] /// use std::mem; /// /// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) }); @@ -634,7 +635,8 @@ pub const fn align_of_val(val: &T) -> usize { /// [type-layout]: ../../reference/type-layout.html#r-layout.primitive #[inline] #[must_use] -#[unstable(feature = "layout_for_ptr", issue = "69835")] +#[stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "layout_for_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn align_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::align_of_val(val) } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index c758d5f4b89d6..1b393e6c928e8 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -119,11 +119,23 @@ //! fully contiguous (i.e., has no "holes"), there is no guarantee that this //! will not change in the future. //! +//! An allocation can be either mutable (the common case) or *read-only*. +//! Read-only allocations are implicitly introduced by the compiler for `static` items without +//! interior mutability and for `const` items. Writing or creating a mutable reference to a +//! read-only allocation is undefined behavior, and most atomic operations are not supported +//! for read-only allocations either (see [here][atomic-ro] for exceptions). +//! Additionally, some target-specific intrinsics are not supported on read-only +//! allocations even if their memory write is masked off, such as [`_mm_maskmoveu_si128`]. +//! +//! [atomic-ro]: crate::sync::atomic#atomic-accesses-to-read-only-memory +//! [`_mm_maskmoveu_si128`]: ../../core/arch/x86/fn._mm_maskmoveu_si128.html +//! //! Allocations must behave like "normal" memory: in particular, reads must not have //! side-effects, and writes must become visible to other threads using the usual synchronization //! primitives. //! Allocations must support all atomic operations that are available for the target (as //! determined by the `target_has_atomic*` set of cfg flags). +//! Read-only allocations only have to support the operations [permitted there][atomic-ro]. //! The precise instructions used for atomic operations are generally not guaranteed, so portable //! software should place all Rust allocations in memory regions that support all atomic //! instructions. diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 67e8f5c032b7b..bf5355ffc141d 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -207,6 +207,10 @@ impl NonNull { impl NonNull { /// Creates a new `NonNull`. /// + /// Note that if you have an `&mut`, you can use the safe [`from_mut`] instead. + /// + /// [`from_mut`]: NonNull::from_mut + /// /// # Safety /// /// `ptr` must be non-null. @@ -246,6 +250,10 @@ impl NonNull { /// Creates a new `NonNull` if `ptr` is non-null. /// + /// Note that if you have an `&mut`, you can use [`from_mut`] instead to avoid the `Option`. + /// + /// [`from_mut`]: NonNull::from_mut + /// /// # Panics during const evaluation /// /// This method will panic during const evaluation if the pointer cannot be diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 92fccea38bac9..06c5926ecc2f8 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -82,7 +82,6 @@ #![feature(iter_partition_in_place)] #![feature(iterator_try_collect)] #![feature(iterator_try_reduce)] -#![feature(layout_for_ptr)] #![feature(macro_metavar_expr_concat)] #![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] diff --git a/library/std/src/sys/thread/solid.rs b/library/std/src/sys/thread/solid.rs index 5953c0e7b6129..acea047a66455 100644 --- a/library/std/src/sys/thread/solid.rs +++ b/library/std/src/sys/thread/solid.rs @@ -169,8 +169,7 @@ impl Thread { } } - // Safety: `Box::into_raw` returns a non-null pointer - let p_inner = unsafe { NonNull::new_unchecked(Box::into_raw(inner)) }; + let p_inner = Box::into_non_null(inner); let new_task = ItronError::err_if_negative(unsafe { abi::acre_tsk(&abi::T_CTSK { 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 57d7942fa32f5..b53215355e5b4 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 @@ -189,7 +189,7 @@ fn check_rvalue<'tcx>( Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { Err((span, "casting pointers to ints is unstable in const fn".into())) }, - Rvalue::Cast(CastKind::Transmute, _, _) => Err(( + Rvalue::Cast(CastKind::Transmute | CastKind::BoxDerefTransmute, _, _) => Err(( span, "transmute can attempt to turn pointers into integers, so is unstable in const fn".into(), )), diff --git a/src/tools/enzyme b/src/tools/enzyme index fe8484e4d3da5..a9b96ed28ed25 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit fe8484e4d3da5a4628ea59a66ed2d2e9664b96a1 +Subproject commit a9b96ed28ed25bd9e393d7fd14778acef97505ed diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 48311985ca3b4..9ca3212edef2a 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -601,7 +601,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050). - this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::Dereferenceable)?; + this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::Dereferenceable("pointer"))?; // It is crucial that this gets called on all code paths, to ensure we track tag creation. let log_creation = |this: &MiriInterpCx<'tcx>, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index efeab43d51fa4..185596d7232e9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -244,7 +244,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050). - this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable)?; + this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::Dereferenceable("pointer"))?; // It is crucial that this gets called on all code paths, to ensure we track tag creation. let log_creation = |this: &MiriInterpCx<'tcx>, diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index dda3306d0208d..a366805289227 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -339,7 +339,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { { assert!(init_val != uninit_val); let this = self.eval_context_mut(); - this.check_ptr_access(obj.ptr(), obj.layout.size, CheckInAllocMsg::Dereferenceable)?; + this.check_ptr_access(obj.ptr(), obj.layout.size, CheckInAllocMsg::Dereferenceable("pointer"))?; assert!(init_offset < obj.layout.size); // ensure our 1-byte flag fits let init_field = obj.offset(init_offset, this.machine.layouts.u8, this)?; @@ -389,7 +389,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 'tcx: 'a, { let this = self.eval_context_mut(); - this.check_ptr_access(obj.ptr(), obj.layout.size, CheckInAllocMsg::Dereferenceable)?; + this.check_ptr_access(obj.ptr(), obj.layout.size, CheckInAllocMsg::Dereferenceable("pointer"))?; assert!(init_offset < obj.layout.size); // ensure our 1-byte flag fits let init_field = obj.offset(init_offset, this.machine.layouts.u8, this)?; diff --git a/src/tools/miri/tests/fail/coroutine-pinned-moved.stderr b/src/tools/miri/tests/fail/coroutine-pinned-moved.stderr index 7998db2424b36..664bab4b62f6d 100644 --- a/src/tools/miri/tests/fail/coroutine-pinned-moved.stderr +++ b/src/tools/miri/tests/fail/coroutine-pinned-moved.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: reference not dereferenceable: ALLOC has been freed, so this pointer is dangling --> tests/fail/coroutine-pinned-moved.rs:LL:CC | LL | *num += 1; diff --git a/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr b/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr index 50f041931aea9..3f066b6565880 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/deref-invalid-ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: reference not dereferenceable: reference must be dereferenceable for 4 bytes, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/dangling_pointers/deref-invalid-ptr.rs:LL:CC | LL | let _y = unsafe { *(&*x as *const u32) }; - | ^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | ^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/dangling_pointers/stack_temporary.stderr b/src/tools/miri/tests/fail/dangling_pointers/stack_temporary.stderr index 3faff2248e408..83451d00daecf 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/stack_temporary.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/stack_temporary.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: reference not dereferenceable: ALLOC has been freed, so this pointer is dangling --> tests/fail/dangling_pointers/stack_temporary.rs:LL:CC | LL | let val = *x; diff --git a/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr b/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr index dc3565ce399be..f55c904425510 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/storage_dead_dangling.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: reference not dereferenceable: reference must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/dangling_pointers/storage_dead_dangling.rs:LL:CC | LL | let _x = unsafe { *&mut *(LEAK as *mut i32) }; diff --git a/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr index b0f9ffb451973..6d9cd81f3f802 100644 --- a/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr +++ b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: reference not dereferenceable: reference must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) --> tests/fail/provenance/pointer_partial_overwrite.rs:LL:CC | LL | let x = *p; diff --git a/src/tools/miri/tests/fail/rc_as_ptr.stderr b/src/tools/miri/tests/fail/rc_as_ptr.stderr index 42bf8637e0da8..c86ee3300f03c 100644 --- a/src/tools/miri/tests/fail/rc_as_ptr.stderr +++ b/src/tools/miri/tests/fail/rc_as_ptr.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling +error: Undefined Behavior: box not dereferenceable: ALLOC has been freed, so this pointer is dangling --> tests/fail/rc_as_ptr.rs:LL:CC | LL | assert_eq!(42, **unsafe { &*Weak::as_ptr(&weak) }); diff --git a/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs b/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs index b5a9b2bf18ee3..9aed9f6341b0f 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.rs @@ -19,7 +19,7 @@ fn main() { unsafe { (&mut ptr as *mut _ as *mut *const u8).write(&buf as *const _ as *const u8); } - // Re-borrow that. This should be UB. - let _ptr = &*ptr; //~ERROR: required 256 byte alignment + // Dereference that. This should be UB. + let _ptr = &raw const *ptr; //~ERROR: required 256 byte alignment } } diff --git a/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.stderr b/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.stderr index 7c85a3be5a265..f696f0935eaec 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.stderr +++ b/src/tools/miri/tests/fail/unaligned_pointers/dyn_alignment.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value of type &dyn std::fmt::Debug: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) +error: Undefined Behavior: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) --> tests/fail/unaligned_pointers/dyn_alignment.rs:LL:CC | -LL | let _ptr = &*ptr; - | ^^^^^ Undefined Behavior occurred here +LL | let _ptr = &raw const *ptr; + | ^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/validity/call_null_ref_fn_def.rs b/src/tools/miri/tests/fail/validity/call_null_ref_fn_def.rs new file mode 100644 index 0000000000000..2fb63941284fb --- /dev/null +++ b/src/tools/miri/tests/fail/validity/call_null_ref_fn_def.rs @@ -0,0 +1,7 @@ +fn foo() {} + +fn main() { + let mut f = &foo; + unsafe { (&raw mut f).cast::().write(0) }; + f(); //~ERROR: null reference +} diff --git a/src/tools/miri/tests/fail/validity/call_null_ref_fn_def.stderr b/src/tools/miri/tests/fail/validity/call_null_ref_fn_def.stderr new file mode 100644 index 0000000000000..b5af5c3637fb8 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/call_null_ref_fn_def.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: dereferencing a null reference + --> tests/fail/validity/call_null_ref_fn_def.rs:LL:CC + | +LL | f(); + | ^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_dangling_ref.rs b/src/tools/miri/tests/fail/validity/cast_dangling_ref.rs new file mode 100644 index 0000000000000..aaf4550d29505 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_dangling_ref.rs @@ -0,0 +1,11 @@ +#[repr(C)] +struct S { + a: (), + b: i8, +} + +fn main() { + let mut x = &S { a: (), b: 0 }; + unsafe { (&raw mut x).cast::().write(16) }; + let _val = x as *const _; //~ERROR: must be dereferenceable for 1 byte, but got 0x10[noalloc] which is a dangling pointer +} diff --git a/src/tools/miri/tests/fail/validity/cast_dangling_ref.stderr b/src/tools/miri/tests/fail/validity/cast_dangling_ref.stderr new file mode 100644 index 0000000000000..cd3e5943ab81e --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_dangling_ref.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: reference not dereferenceable: reference must be dereferenceable for 1 byte, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/validity/cast_dangling_ref.rs:LL:CC + | +LL | let _val = x as *const _; + | ^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid.rs new file mode 100644 index 0000000000000..b892c1155cfeb --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid.rs @@ -0,0 +1,36 @@ +//! Even just *casting* a function pointer, withot ever calling it, requires validity. +#![feature(core_intrinsics, custom_mir)] +#![allow(internal_features)] +#![allow(unused_assignments)] + +use core::intrinsics::mir::*; + +// Overwrites `ptr` by invoking the callback, then casts `ptr` to `*mut u8`. +// Needs to use custom MIR to avoid copies that perform their own validation. +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn test(ptr: fn(), overwrite: fn(&mut fn())) { + mir! { + let ptrptr; + let ptr2 : *mut u8; + let _unused; + + { + ptrptr = &mut ptr; + Call(_unused = overwrite(ptrptr), ReturnTo(ret), UnwindContinue()) + } + + ret = { + ptr2 = ptr as *mut u8; //~ERROR: does not point to a function + Return() + } + } +} + +fn f() {} + +fn main() { + test(f, |ptrptr| unsafe { + let ptrptr = std::ptr::from_mut(ptrptr); + ptrptr.cast::<*const ()>().write(&()) + }); +} diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid.stderr new file mode 100644 index 0000000000000..9df62ef619e82 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: using ALLOC as function pointer but it does not point to a function + --> tests/fail/validity/cast_fn_ptr_invalid.rs:LL:CC + | +LL | ptr2 = ptr as *mut u8; + | ^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: stack backtrace: + 0: test + at tests/fail/validity/cast_fn_ptr_invalid.rs:LL:CC + 1: main + at tests/fail/validity/cast_fn_ptr_invalid.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_null_ref.rs b/src/tools/miri/tests/fail/validity/cast_null_ref.rs new file mode 100644 index 0000000000000..5faf5260e7f07 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_null_ref.rs @@ -0,0 +1,5 @@ +fn main() { + let mut x = &(); + unsafe { (&raw mut x).cast::().write(0) }; + let _val = x as *const _; //~ERROR: null reference +} diff --git a/src/tools/miri/tests/fail/validity/cast_null_ref.stderr b/src/tools/miri/tests/fail/validity/cast_null_ref.stderr new file mode 100644 index 0000000000000..5b396d4931e11 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_null_ref.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: dereferencing a null reference + --> tests/fail/validity/cast_null_ref.rs:LL:CC + | +LL | let _val = x as *const _; + | ^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_raw_ptr_invalid_vtable.rs b/src/tools/miri/tests/fail/validity/cast_raw_ptr_invalid_vtable.rs new file mode 100644 index 0000000000000..4e0fe29257e31 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_raw_ptr_invalid_vtable.rs @@ -0,0 +1,47 @@ +#![feature(core_intrinsics, custom_mir)] +#![allow(internal_features)] +#![allow(unused_assignments)] + +use core::intrinsics::mir::*; + +// Overwrites `ptr` by invoking the callback, then casts `ptr` to `*mut u8`. +// Needs to use custom MIR to avoid copies that perform their own validation. +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn test(ptr: *const T, data: U, overwrite: fn(&mut *const T, U)) { + mir! { + let ptrptr; + let ptr2 : *mut u8; // cast drops metadata! + let _unused; + + { + ptrptr = &mut ptr; + Call(_unused = overwrite(ptrptr, data), ReturnTo(ret), UnwindContinue()) + } + + ret = { + ptr2 = CastPtrToPtr(ptr); //~ERROR: vtable for `std::fmt::Debug` but `std::fmt::Display` was expected + Return() + } + } +} + +#[allow(unused)] +struct S { + f: i32, + g: Tail, +} + +fn main() { + let x = S { f: 0, g: 0 }; + let ptr1: *const S = &x; + let ptr2: *const S = &x; + test::, _>( + ptr2, + ptr1, + |ptrptr2, ptr1| unsafe { + // Give ptr2 the vtable from ptr1. + let ptrptr2 = std::ptr::from_mut(ptrptr2); + ptrptr2.copy_from(&raw const ptr1 as *const _, 1) ; + } + ); +} diff --git a/src/tools/miri/tests/fail/validity/cast_raw_ptr_invalid_vtable.stderr b/src/tools/miri/tests/fail/validity/cast_raw_ptr_invalid_vtable.stderr new file mode 100644 index 0000000000000..ba8a1156dfb06 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_raw_ptr_invalid_vtable.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: using vtable for `std::fmt::Debug` but `std::fmt::Display` was expected + --> tests/fail/validity/cast_raw_ptr_invalid_vtable.rs:LL:CC + | +LL | ptr2 = CastPtrToPtr(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: stack backtrace: + 0: test::, *const S> + at tests/fail/validity/cast_raw_ptr_invalid_vtable.rs:LL:CC + 1: main + at tests/fail/validity/cast_raw_ptr_invalid_vtable.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_unaligned_ref.rs b/src/tools/miri/tests/fail/validity/cast_unaligned_ref.rs new file mode 100644 index 0000000000000..97278d9ad6aca --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_unaligned_ref.rs @@ -0,0 +1,8 @@ +#[repr(align(8))] +struct S {} + +fn main() { + let mut x = &S {}; + unsafe { (&raw mut x).cast::().write(1) }; + let _val = x as *const _; //~ERROR: unaligned reference +} diff --git a/src/tools/miri/tests/fail/validity/cast_unaligned_ref.stderr b/src/tools/miri/tests/fail/validity/cast_unaligned_ref.stderr new file mode 100644 index 0000000000000..674a679b326d5 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_unaligned_ref.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) + --> tests/fail/validity/cast_unaligned_ref.rs:LL:CC + | +LL | let _val = x as *const _; + | ^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_dangling_box.rs b/src/tools/miri/tests/fail/validity/deref_dangling_box.rs new file mode 100644 index 0000000000000..9f365c6c915b2 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_dangling_box.rs @@ -0,0 +1,11 @@ +#[repr(C)] +struct S { + a: (), + b: i8, +} + +fn main() { + let mut x = Box::new(S { a: (), b: 0 }); + unsafe { (&raw mut x).cast::().write(16) }; + let _ = x.a; //~ERROR: must be dereferenceable for 1 byte, but got 0x10[noalloc] which is a dangling pointer +} diff --git a/src/tools/miri/tests/fail/validity/deref_dangling_box.stderr b/src/tools/miri/tests/fail/validity/deref_dangling_box.stderr new file mode 100644 index 0000000000000..cedf1e5ef460a --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_dangling_box.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: box not dereferenceable: box must be dereferenceable for 1 byte, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/validity/deref_dangling_box.rs:LL:CC + | +LL | let _ = x.a; + | ^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_dangling_ref.rs b/src/tools/miri/tests/fail/validity/deref_dangling_ref.rs new file mode 100644 index 0000000000000..92abcdb5388ef --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_dangling_ref.rs @@ -0,0 +1,11 @@ +#[repr(C)] +struct S { + a: (), + b: i8, +} + +fn main() { + let mut x = &S { a: (), b: 0 }; + unsafe { (&raw mut x).cast::().write(16) }; + let _ = x.a; //~ERROR: must be dereferenceable for 1 byte, but got 0x10[noalloc] which is a dangling pointer +} diff --git a/src/tools/miri/tests/fail/validity/deref_dangling_ref.stderr b/src/tools/miri/tests/fail/validity/deref_dangling_ref.stderr new file mode 100644 index 0000000000000..f90d50549c3b3 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_dangling_ref.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: reference not dereferenceable: reference must be dereferenceable for 1 byte, but got 0x10[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/validity/deref_dangling_ref.rs:LL:CC + | +LL | let _ = x.a; + | ^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_null_box.rs b/src/tools/miri/tests/fail/validity/deref_null_box.rs new file mode 100644 index 0000000000000..418b5906be02d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_null_box.rs @@ -0,0 +1,5 @@ +fn main() { + let mut x = Box::new(()); + unsafe { (&raw mut x).cast::().write(0) }; + let _ = *x; //~ERROR: null box +} diff --git a/src/tools/miri/tests/fail/validity/deref_null_box.stderr b/src/tools/miri/tests/fail/validity/deref_null_box.stderr new file mode 100644 index 0000000000000..3fcc098160eb2 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_null_box.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: dereferencing a null box + --> tests/fail/validity/deref_null_box.rs:LL:CC + | +LL | let _ = *x; + | ^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_null_ref.rs b/src/tools/miri/tests/fail/validity/deref_null_ref.rs new file mode 100644 index 0000000000000..392fac587ae26 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_null_ref.rs @@ -0,0 +1,5 @@ +fn main() { + let mut x = &(); + unsafe { (&raw mut x).cast::().write(0) }; + let _ = *x; //~ERROR: null reference +} diff --git a/src/tools/miri/tests/fail/validity/deref_null_ref.stderr b/src/tools/miri/tests/fail/validity/deref_null_ref.stderr new file mode 100644 index 0000000000000..7611049cb027d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_null_ref.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: dereferencing a null reference + --> tests/fail/validity/deref_null_ref.rs:LL:CC + | +LL | let _ = *x; + | ^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_raw_ptr_no_vtable.rs b/src/tools/miri/tests/fail/validity/deref_raw_ptr_no_vtable.rs new file mode 100644 index 0000000000000..8f10124ae220d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_raw_ptr_no_vtable.rs @@ -0,0 +1,12 @@ +struct S { + f: i32, + #[allow(unused)] + g: Tail, +} + +fn main() { + let x = S { f: 0, g: 0 }; + let mut ptr: *const S = &x; + unsafe { (&raw mut ptr).cast::().add(1).write(0) }; + let _val = unsafe { &(*ptr).f }; //~ERROR: vtable +} diff --git a/src/tools/miri/tests/fail/validity/deref_raw_ptr_no_vtable.stderr b/src/tools/miri/tests/fail/validity/deref_raw_ptr_no_vtable.stderr new file mode 100644 index 0000000000000..cbbe283f05e9a --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_raw_ptr_no_vtable.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: using null pointer as vtable pointer but it does not point to a vtable + --> tests/fail/validity/deref_raw_ptr_no_vtable.rs:LL:CC + | +LL | let _val = unsafe { &(*ptr).f }; + | ^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_raw_ptr_wrong_vtable.rs b/src/tools/miri/tests/fail/validity/deref_raw_ptr_wrong_vtable.rs new file mode 100644 index 0000000000000..54b248add7566 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_raw_ptr_wrong_vtable.rs @@ -0,0 +1,14 @@ +struct S { + f: i32, + #[allow(unused)] + g: Tail, +} + +fn main() { + let x = S { f: 0, g: 0 }; + let ptr1: *const S = &x; + let mut ptr2: *const S = &x; + // Give ptr2 the vtable from ptr1. + unsafe { (&raw mut ptr2).copy_from(&raw const ptr1 as *const _, 1) }; + let _val = unsafe { &(*ptr2).f }; //~ERROR: vtable +} diff --git a/src/tools/miri/tests/fail/validity/deref_raw_ptr_wrong_vtable.stderr b/src/tools/miri/tests/fail/validity/deref_raw_ptr_wrong_vtable.stderr new file mode 100644 index 0000000000000..69109e5d2040d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_raw_ptr_wrong_vtable.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: using vtable for `std::fmt::Debug` but `std::fmt::Display` was expected + --> tests/fail/validity/deref_raw_ptr_wrong_vtable.rs:LL:CC + | +LL | let _val = unsafe { &(*ptr2).f }; + | ^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_unaligned_box.rs b/src/tools/miri/tests/fail/validity/deref_unaligned_box.rs new file mode 100644 index 0000000000000..2f41e36e25a9b --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_unaligned_box.rs @@ -0,0 +1,10 @@ +#[repr(align(8))] +struct S { + f: (), +} + +fn main() { + let mut x = Box::new(S { f: () }); + unsafe { (&raw mut x).cast::().write(1) }; + let _ = &x.f; //~ERROR: unaligned box +} diff --git a/src/tools/miri/tests/fail/validity/deref_unaligned_box.stderr b/src/tools/miri/tests/fail/validity/deref_unaligned_box.stderr new file mode 100644 index 0000000000000..1a5818334e011 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_unaligned_box.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: encountered an unaligned box (required ALIGN byte alignment but found ALIGN) + --> tests/fail/validity/deref_unaligned_box.rs:LL:CC + | +LL | let _ = &x.f; + | ^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/deref_unaligned_ref.rs b/src/tools/miri/tests/fail/validity/deref_unaligned_ref.rs new file mode 100644 index 0000000000000..837b0f456ca47 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_unaligned_ref.rs @@ -0,0 +1,10 @@ +#[repr(align(8))] +struct S { + f: (), +} + +fn main() { + let mut x = &S { f: () }; + unsafe { (&raw mut x).cast::().write(1) }; + let _ = &x.f; //~ERROR: unaligned reference +} diff --git a/src/tools/miri/tests/fail/validity/deref_unaligned_ref.stderr b/src/tools/miri/tests/fail/validity/deref_unaligned_ref.stderr new file mode 100644 index 0000000000000..9595dd9a2f116 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/deref_unaligned_ref.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN) + --> tests/fail/validity/deref_unaligned_ref.rs:LL:CC + | +LL | let _ = &x.f; + | ^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/ptr_metadata_invalid_vtable.rs b/src/tools/miri/tests/fail/validity/ptr_metadata_invalid_vtable.rs new file mode 100644 index 0000000000000..92522f76b3d8f --- /dev/null +++ b/src/tools/miri/tests/fail/validity/ptr_metadata_invalid_vtable.rs @@ -0,0 +1,36 @@ +#![feature(core_intrinsics, custom_mir)] +#![allow(internal_features)] +#![allow(unused_assignments)] + +use core::intrinsics::mir::*; + +fn main() { + test8345(&1, |ptrptr| unsafe { + let ptrptr = std::ptr::from_mut(ptrptr); + ptrptr.cast::<(usize, usize)>().write((0, 1)) + }); +} + +trait A {} +impl A for T {} + +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn test8345(mut ptr: &dyn A, overwrite: fn(&mut &dyn A)) { + mir! { + let ptrptr; + let _unused; + let idk; + + { + ptrptr = &mut ptr; + // Overwrite `ptr` to make it invalid. + Call(_unused = overwrite(ptrptr), ReturnTo(ret), UnwindContinue()) + } + + ret = { + // Do something with `ptr`. + idk = PtrMetadata(ptr); //~ERROR: null reference + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/validity/ptr_metadata_invalid_vtable.stderr b/src/tools/miri/tests/fail/validity/ptr_metadata_invalid_vtable.stderr new file mode 100644 index 0000000000000..ecd428cd1cb6b --- /dev/null +++ b/src/tools/miri/tests/fail/validity/ptr_metadata_invalid_vtable.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: dereferencing a null reference + --> tests/fail/validity/ptr_metadata_invalid_vtable.rs:LL:CC + | +LL | idk = PtrMetadata(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: stack backtrace: + 0: test8345 + at tests/fail/validity/ptr_metadata_invalid_vtable.rs:LL:CC + 1: main + at tests/fail/validity/ptr_metadata_invalid_vtable.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/intrinsics/intrinsics.rs b/src/tools/miri/tests/pass/intrinsics/intrinsics.rs index d24c64ea099f3..25139922ae774 100644 --- a/src/tools/miri/tests/pass/intrinsics/intrinsics.rs +++ b/src/tools/miri/tests/pass/intrinsics/intrinsics.rs @@ -1,6 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance //@run-native -#![feature(core_intrinsics, layout_for_ptr, ptr_metadata)] +#![feature(core_intrinsics, ptr_metadata)] //! Tests for various intrinsics that do not fit anywhere else. use std::intrinsics; diff --git a/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs b/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs index b396f3fa835cf..6f75c3ce2db58 100644 --- a/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs +++ b/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs @@ -1,4 +1,3 @@ -#![feature(layout_for_ptr)] use std::mem; #[repr(packed, C)] diff --git a/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs b/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs index bdcb87e1a2eee..bfa4d307f8d1a 100644 --- a/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs +++ b/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs @@ -1,4 +1,3 @@ -#![feature(layout_for_ptr)] use std::mem; #[repr(packed(4))] diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2123.rs b/src/tools/miri/tests/pass/issues/issue-miri-2123.rs index e39e5fe454a2c..969bb6f006676 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-2123.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-2123.rs @@ -1,4 +1,4 @@ -#![feature(ptr_metadata, layout_for_ptr)] +#![feature(ptr_metadata)] use std::{mem, ptr}; diff --git a/src/tools/miri/tests/pass/slices.rs b/src/tools/miri/tests/pass/slices.rs index e6f1d0ade354a..c1aecd4cf5a67 100644 --- a/src/tools/miri/tests/pass/slices.rs +++ b/src/tools/miri/tests/pass/slices.rs @@ -3,7 +3,6 @@ //@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes //@compile-flags: -Zmiri-strict-provenance #![feature(slice_partition_dedup)] -#![feature(layout_for_ptr)] use std::{ptr, slice}; diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff index 6ebce526c9833..a6756ba0245c7 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff @@ -12,9 +12,9 @@ bb0: { StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); -- _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); +- _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); -+ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }} as *const Never (Transmute); ++ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }} as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff index 6ebce526c9833..a6756ba0245c7 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff @@ -12,9 +12,9 @@ bb0: { StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); -- _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); +- _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); -+ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }} as *const Never (Transmute); ++ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }} as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff index 0a21dea1335b4..352d9345eef84 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff +++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff @@ -13,7 +13,7 @@ StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); + _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff index 0a21dea1335b4..352d9345eef84 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff +++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff @@ -13,7 +13,7 @@ StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); + _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff index 9e717a74776d1..8b5ad1519d27c 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff @@ -73,7 +73,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); + _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff index f0b1feca4b28e..14943534b98be 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff @@ -53,7 +53,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); + _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff index dc5f656f17a59..f8d47dcae5b27 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff @@ -73,7 +73,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); + _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff index f0b1feca4b28e..14943534b98be 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff @@ -53,7 +53,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); + _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff index 73cd8e4fdfd81..ceacf606f3553 100644 --- a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff @@ -23,7 +23,7 @@ + StorageLive(_7); + StorageLive(_5); + _7 = no_retag copy (((*_3).0: std::ptr::Unique>).0: std::ptr::NonNull>); -+ _6 = copy _7 as *const dyn std::ops::FnMut (Transmute); ++ _6 = copy _7 as *const dyn std::ops::FnMut (BoxDerefTransmute); + _5 = &mut (*_6); + _0 = as FnMut>::call_mut(move _5, move _4) -> [return: bb2, unwind unreachable]; } diff --git a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff index 469d2ef000c5c..862174fd94bff 100644 --- a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff @@ -23,7 +23,7 @@ + StorageLive(_7); + StorageLive(_5); + _7 = no_retag copy (((*_3).0: std::ptr::Unique>).0: std::ptr::NonNull>); -+ _6 = copy _7 as *const dyn std::ops::FnMut (Transmute); ++ _6 = copy _7 as *const dyn std::ops::FnMut (BoxDerefTransmute); + _5 = &mut (*_6); + _0 = as FnMut>::call_mut(move _5, move _4) -> [return: bb4, unwind: bb2]; } diff --git a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff index 1046a86fe9f6d..0dc8adb257423 100644 --- a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff @@ -24,7 +24,7 @@ + StorageLive(_7); + StorageLive(_5); + _7 = no_retag copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); -+ _6 = copy _7 as *const dyn std::ops::Fn(i32) (Transmute); ++ _6 = copy _7 as *const dyn std::ops::Fn(i32) (BoxDerefTransmute); + _5 = &(*_6); + _2 = >::call(move _5, move _4) -> [return: bb2, unwind unreachable]; } diff --git a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff index bb24c02f3578b..1b320f9200405 100644 --- a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff @@ -24,7 +24,7 @@ + StorageLive(_7); + StorageLive(_5); + _7 = no_retag copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); -+ _6 = copy _7 as *const dyn std::ops::Fn(i32) (Transmute); ++ _6 = copy _7 as *const dyn std::ops::Fn(i32) (BoxDerefTransmute); + _5 = &(*_6); + _2 = >::call(move _5, move _4) -> [return: bb4, unwind: bb2]; } diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir index 0445a476c5cc9..f4972c7d1437e 100644 --- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir +++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir @@ -20,7 +20,7 @@ fn b(_1: &mut Box) -> &mut T { StorageLive(_5); StorageLive(_6); _6 = no_retag copy (((*_4).0: std::ptr::Unique).0: std::ptr::NonNull); - _5 = copy _6 as *const T (Transmute); + _5 = copy _6 as *const T (BoxDerefTransmute); _3 = &mut (*_5); StorageDead(_6); StorageDead(_5); diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir index 38076dc14b6a1..d5a0450af828e 100644 --- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir +++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir @@ -18,7 +18,7 @@ fn d(_1: &Box) -> &T { StorageLive(_4); StorageLive(_5); _5 = no_retag copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); - _4 = copy _5 as *const T (Transmute); + _4 = copy _5 as *const T (BoxDerefTransmute); _2 = &(*_4); StorageDead(_5); StorageDead(_4); diff --git a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff index 644d6d320de04..8ca4ca123c829 100644 --- a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff +++ b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff @@ -12,7 +12,7 @@ StorageLive(_2); StorageLive(_3); _3 = move _1; - _4 = copy ((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>) as *const [i32] (Transmute); + _4 = copy ((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>) as *const [i32] (BoxDerefTransmute); _2 = callee(move (*_4)) -> [return: bb1, unwind: bb3]; } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff index 4f8b7c4160f99..adf61031b3699 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff @@ -17,7 +17,7 @@ } bb1: { - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); + _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); PlaceMention((*_2)); unreachable; } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff index 4f8b7c4160f99..adf61031b3699 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff @@ -17,7 +17,7 @@ } bb1: { - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); + _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); PlaceMention((*_2)); unreachable; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir index fc254be264748..549af7af4d888 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir @@ -65,7 +65,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } } } - scope 37 (inlined without_provenance_mut::) { + scope 37 (inlined std::ptr::without_provenance_mut::) { } } scope 32 (inlined std::ptr::const_ptr::::addr) { @@ -109,7 +109,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { scope 7 { } scope 11 (inlined std::ptr::without_provenance::) { - scope 12 (inlined without_provenance_mut::) { + scope 12 (inlined std::ptr::without_provenance_mut::) { } } scope 13 (inlined NonNull::::as_ptr) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir index 5f8fefc57c412..2e662fc36ac67 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir @@ -34,7 +34,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } } } - scope 25 (inlined without_provenance_mut::) { + scope 25 (inlined std::ptr::without_provenance_mut::) { } } scope 20 (inlined std::ptr::const_ptr::::addr) { @@ -77,7 +77,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 7 { } scope 11 (inlined std::ptr::without_provenance::) { - scope 12 (inlined without_provenance_mut::) { + scope 12 (inlined std::ptr::without_provenance_mut::) { } } scope 13 (inlined NonNull::::as_ptr) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir index bd0e3a025f425..c2c521729b75c 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir @@ -103,7 +103,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 7 { } scope 11 (inlined std::ptr::without_provenance::) { - scope 12 (inlined without_provenance_mut::) { + scope 12 (inlined std::ptr::without_provenance_mut::) { } } scope 13 (inlined NonNull::::as_ptr) { diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.runtime-optimized.after.panic-abort.mir index 7596384ac4a89..889d7c1e69afb 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.runtime-optimized.after.panic-abort.mir @@ -21,7 +21,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { } } } - scope 10 (inlined without_provenance_mut::) { + scope 10 (inlined std::ptr::without_provenance_mut::) { } } scope 5 (inlined std::ptr::const_ptr::::addr) { diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr index d1e3f884d02b3..3f35b9d99d1e5 100644 --- a/tests/ui/async-await/async-fn/impl-header.stderr +++ b/tests/ui/async-await/async-fn/impl-header.stderr @@ -28,7 +28,7 @@ error[E0046]: not all trait items implemented, missing: `call` LL | impl async Fn<()> for F {} | ^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation | - = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` + = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` (unstable, requires feature `fn_traits`) error[E0277]: expected an `FnMut()` closure, found `F` --> $DIR/impl-header.rs:5:23 diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs index fcb0dccf750e3..7c2d86bff75b2 100644 --- a/tests/ui/const-ptr/forbidden_slices.rs +++ b/tests/ui/const-ptr/forbidden_slices.rs @@ -20,7 +20,7 @@ pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; // Out of bounds pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) }; -//~^ ERROR: dangling reference (going beyond the bounds of its allocation) +//~^ ERROR: reference must be dereferenceable for 8 bytes // Reading uninitialized data pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; //~ ERROR: uninitialized memory @@ -39,14 +39,14 @@ pub static S7: &[u16] = unsafe { // Unaligned read pub static S8: &[u64] = unsafe { - //~^ ERROR: dangling reference (going beyond the bounds of its allocation) let ptr = (&D4 as *const [u32; 2] as *const u32).byte_add(1).cast::(); from_raw_parts(ptr, 1) + //~^ ERROR: reference must be dereferenceable for 8 bytes }; pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; -//~^ ERROR encountered a null reference +//~^ ERROR: null reference pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore //~^ ERROR 0 < pointee_size && pointee_size <= isize::MAX as usize pub static R2: &[u32] = unsafe { @@ -70,9 +70,9 @@ pub static R6: &[bool] = unsafe { from_ptr_range(ptr..ptr.add(4)) }; pub static R7: &[u16] = unsafe { - //~^ ERROR: unaligned reference (required 2 byte alignment but found 1) let ptr = (&D2 as *const Struct as *const u16).byte_add(1); from_ptr_range(ptr..ptr.add(4)) + //~^ ERROR: unaligned reference (required 2 byte alignment but found 1) }; pub static R8: &[u64] = unsafe { let ptr = (&D4 as *const [u32; 2] as *const u32).byte_add(1).cast::(); diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index e23b4e5b1aa75..fb5b9c9764076 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -1,35 +1,20 @@ -error[E0080]: constructing invalid value of type &[u32]: encountered a null reference - --> $DIR/forbidden_slices.rs:16:1 +error[E0080]: dereferencing a null reference + --> $DIR/forbidden_slices.rs:16:34 | LL | pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; - | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `S0` failed here -error[E0080]: constructing invalid value of type &[()]: encountered a null reference - --> $DIR/forbidden_slices.rs:18:1 +error[E0080]: dereferencing a null reference + --> $DIR/forbidden_slices.rs:18:33 | LL | pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; - | ^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `S1` failed here -error[E0080]: constructing invalid value of type &[u32]: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/forbidden_slices.rs:22:1 +error[E0080]: reference not dereferenceable: reference must be dereferenceable for 8 bytes, but got ALLOC$ID which is only 4 bytes from the end of the allocation + --> $DIR/forbidden_slices.rs:22:34 | LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) }; - | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ HEX_DUMP - } + | ^^^^^^^^^^^^^^^^^^^^^^ evaluation of `S2` failed here error[E0080]: constructing invalid value of type &[u8]: at .[0], encountered uninitialized memory, but expected an integer --> $DIR/forbidden_slices.rs:26:1 @@ -77,27 +62,17 @@ LL | pub static S7: &[u16] = unsafe { ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: constructing invalid value of type &[u64]: encountered a dangling reference (going beyond the bounds of its allocation) - --> $DIR/forbidden_slices.rs:41:1 +error[E0080]: reference not dereferenceable: reference must be dereferenceable for 8 bytes, but got ALLOC$ID+0x1 which is only 7 bytes from the end of the allocation + --> $DIR/forbidden_slices.rs:44:5 | -LL | pub static S8: &[u64] = unsafe { - | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ HEX_DUMP - } +LL | from_raw_parts(ptr, 1) + | ^^^^^^^^^^^^^^^^^^^^^^ evaluation of `S8` failed here -error[E0080]: constructing invalid value of type &[u32]: encountered a null reference - --> $DIR/forbidden_slices.rs:48:1 +error[E0080]: dereferencing a null reference + --> $DIR/forbidden_slices.rs:48:34 | LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `R0` failed here error[E0080]: evaluation panicked: assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize --> $DIR/forbidden_slices.rs:50:33 @@ -146,16 +121,11 @@ LL | pub static R6: &[bool] = unsafe { ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: constructing invalid value of type &[u16]: encountered an unaligned reference (required 2 byte alignment but found 1) - --> $DIR/forbidden_slices.rs:72:1 - | -LL | pub static R7: &[u16] = unsafe { - | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value +error[E0080]: encountered an unaligned reference (required 2 byte alignment but found 1) + --> $DIR/forbidden_slices.rs:74:5 | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ HEX_DUMP - } +LL | from_ptr_range(ptr..ptr.add(4)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `R7` failed here error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC$ID+0x1 which is only 7 bytes from the end of the allocation --> $DIR/forbidden_slices.rs:79:25 diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs index 80cf3ffef11a5..5609fb56a1322 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs @@ -10,10 +10,10 @@ use std::intrinsics; const _X: &'static u8 = unsafe { - //~^ ERROR: dangling reference (use-after-free) let ptr = intrinsics::const_allocate(4, 4); intrinsics::const_deallocate(ptr, 4, 4); &*ptr + //~^ ERROR: this pointer is dangling }; const _Y: u8 = unsafe { diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr index e9b7f99ee6d99..01fe36c0a537a 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr @@ -1,15 +1,10 @@ -error[E0080]: constructing invalid value of type &u8: encountered a dangling reference (use-after-free) - --> $DIR/dealloc_intrinsic_dangling.rs:12:1 +error[E0080]: reference not dereferenceable: ALLOC$ID has been freed, so this pointer is dangling + --> $DIR/dealloc_intrinsic_dangling.rs:15:5 | -LL | const _X: &'static u8 = unsafe { - | ^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ │ ╾─╼ - } +LL | &*ptr + | ^^^^^ evaluation of `_X` failed here -error[E0080]: memory access failed: ALLOC$ID has been freed, so this pointer is dangling +error[E0080]: reference not dereferenceable: ALLOC$ID has been freed, so this pointer is dangling --> $DIR/dealloc_intrinsic_dangling.rs:23:5 | LL | *reference diff --git a/tests/ui/consts/const-eval/issue-49296.stderr b/tests/ui/consts/const-eval/issue-49296.stderr index 64e892e61af47..90e8176d79c59 100644 --- a/tests/ui/consts/const-eval/issue-49296.stderr +++ b/tests/ui/consts/const-eval/issue-49296.stderr @@ -1,4 +1,4 @@ -error[E0080]: memory access failed: ALLOC$ID has been freed, so this pointer is dangling +error[E0080]: reference not dereferenceable: ALLOC$ID has been freed, so this pointer is dangling --> $DIR/issue-49296.rs:9:16 | LL | const X: u64 = *wat(42); diff --git a/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr b/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr index 8dbb05c15725a..c9f1e5f1639bb 100644 --- a/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr +++ b/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr @@ -1,8 +1,11 @@ -error[E0080]: memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) - --> $DIR/nonnull_as_ref_ub.rs:4:29 +error[E0080]: reference not dereferenceable: reference must be dereferenceable for 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + --> $DIR/nonnull_as_ref_ub.rs:4:39 | LL | const _: () = assert!(42 == *unsafe { NON_NULL.as_ref() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + | ^^^^^^^^^^^^^^^^^ evaluation of `_` failed inside this call + | +note: inside `NonNull::::as_ref::<'_>` + --> $SRC_DIR/core/src/ptr/non_null.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr b/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr index 1efd30818b2e5..70ca9dbcd1037 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.32bit.stderr @@ -1,24 +1,14 @@ -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-incorrect-vtable.rs:18:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-incorrect-vtable.rs:19:14 | -LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ - } +LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `INVALID_VTABLE_ALIGNMENT` failed here -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-incorrect-vtable.rs:22:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-incorrect-vtable.rs:23:14 | -LL | const INVALID_VTABLE_SIZE: &dyn Trait = - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──╼╾──╼ - } +LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `INVALID_VTABLE_SIZE` failed here error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:31:1 diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr b/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr index bc26c93513964..b4e1be920b8f3 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.64bit.stderr @@ -1,24 +1,14 @@ -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-incorrect-vtable.rs:18:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-incorrect-vtable.rs:19:14 | -LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait = - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ - } +LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `INVALID_VTABLE_ALIGNMENT` failed here -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-incorrect-vtable.rs:22:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-incorrect-vtable.rs:23:14 | -LL | const INVALID_VTABLE_SIZE: &dyn Trait = - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾──────╼╾──────╼ - } +LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `INVALID_VTABLE_SIZE` failed here error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-incorrect-vtable.rs:31:1 diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.rs b/tests/ui/consts/const-eval/ub-incorrect-vtable.rs index 4185b0261b296..b7a49f5fe78e7 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.rs +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.rs @@ -17,11 +17,11 @@ trait Trait {} const INVALID_VTABLE_ALIGNMENT: &dyn Trait = unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; -//~^^ ERROR vtable +//~^ ERROR vtable const INVALID_VTABLE_SIZE: &dyn Trait = unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; -//~^^ ERROR vtable +//~^ ERROR vtable #[repr(transparent)] struct W(T); diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs index 679de8b0cf101..4bb897ab11bd4 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.rs +++ b/tests/ui/consts/const-eval/ub-nonnull.rs @@ -19,9 +19,9 @@ const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; //~^ ERROR invalid value const OUT_OF_BOUNDS_PTR: NonNull = { unsafe { - let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle + let ptr: *const [u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle // Use address-of-element for pointer arithmetic. This could wrap around to null! - let out_of_bounds_ptr = &ptr[255]; //~ ERROR in-bounds pointer arithmetic failed + let out_of_bounds_ptr = &(*ptr)[255]; //~ ERROR in-bounds pointer arithmetic failed mem::transmute(out_of_bounds_ptr) } }; diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr index 81f2ff3ca5fa4..d34425ea79ad9 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.stderr +++ b/tests/ui/consts/const-eval/ub-nonnull.stderr @@ -12,8 +12,8 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC$ID which is only 1 byte from the end of the allocation --> $DIR/ub-nonnull.rs:24:29 | -LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^^ evaluation of `OUT_OF_BOUNDS_PTR` failed here +LL | let out_of_bounds_ptr = &(*ptr)[255]; + | ^^^^^^^^^^^^ evaluation of `OUT_OF_BOUNDS_PTR` failed here error[E0080]: constructing invalid value of type NonZero: at .0.0, encountered 0, but expected something greater or equal to 1 --> $DIR/ub-nonnull.rs:28:1 diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.rs b/tests/ui/consts/const-eval/ub-wide-ptr.rs index 327a5689de062..64b13f91e839b 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.rs +++ b/tests/ui/consts/const-eval/ub-wide-ptr.rs @@ -140,11 +140,11 @@ const DYN_METADATA: ptr::DynMetadata = ptr::metadata::(ptr:: static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe { mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) - //~^^ ERROR null pointer + //~^ ERROR null pointer }; static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe { mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - //~^^ ERROR vtable + //~^ ERROR vtable }; fn main() {} diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.stderr b/tests/ui/consts/const-eval/ub-wide-ptr.stderr index b442a43c6276f..10c6dafeac7cd 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-wide-ptr.stderr @@ -226,38 +226,23 @@ LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u ╾ALLOC$ID╼ HEX_DUMP } -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:119:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-wide-ptr.rs:119:57 | LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ - } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `TRAIT_OBJ_UNALIGNED_VTABLE` failed here -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:121:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-wide-ptr.rs:121:57 | LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ - } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `TRAIT_OBJ_BAD_DROP_FN_NULL` failed here -error[E0080]: constructing invalid value of type &dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:123:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-wide-ptr.rs:123:56 | LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ - } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `TRAIT_OBJ_BAD_DROP_FN_INT` failed here error[E0080]: constructing invalid value of type W<&dyn Trait>: at .0, encountered ALLOC$ID, but expected a vtable pointer --> $DIR/ub-wide-ptr.rs:125:1 @@ -303,27 +288,17 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ } -error[E0080]: constructing invalid value of type *const dyn Trait: encountered null pointer, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:141:1 - | -LL | static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value +error[E0080]: using null pointer as vtable pointer but it does not point to a vtable + --> $DIR/ub-wide-ptr.rs:142:5 | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ HEX_DUMP - } +LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF` failed here -error[E0080]: constructing invalid value of type *const dyn Trait: encountered ALLOC$ID, but expected a vtable pointer - --> $DIR/ub-wide-ptr.rs:145:1 +error[E0080]: using ALLOC$ID as vtable pointer but it does not point to a vtable + --> $DIR/ub-wide-ptr.rs:146:5 | -LL | static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value - | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - ╾ALLOC$ID╼ ╾ALLOC$ID╼ │ ╾─╼ - } +LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF` failed here error: aborting due to 29 previous errors diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs index ebf1f88eb8e72..3438790232fe5 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs @@ -84,8 +84,8 @@ fn dangling() { // Undefined behaviour (integer as pointer), who doesn't love tests like this. Some(&mut *(42 as *mut i32)) } } - const INT2PTR: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference - static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference + const INT2PTR: Option<&mut i32> = helper_int2ptr(); //~ ERROR reference not dereferenceable + static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); //~ ERROR reference not dereferenceable const fn helper_dangling() -> Option<&'static mut i32> { unsafe { // Undefined behaviour (dangling pointer), who doesn't love tests like this. diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr index ad19f78ef831b..d631dd3376086 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -120,27 +120,29 @@ LL | const RAW_MUT_COERCE_C: SyncPtr = SyncPtr { x: &mut 0 }; = note: to avoid accidentally creating global mutable state, such temporaries must be immutable = help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut` -error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (0x2a[noalloc] has no provenance) - --> $DIR/mut_ref_in_final.rs:87:5 +error[E0080]: reference not dereferenceable: reference must be dereferenceable for 4 bytes, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) + --> $DIR/mut_ref_in_final.rs:87:39 | LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | ^^^^^^^^^^^^^^^^ evaluation of `dangling::INT2PTR` failed inside this call | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } +note: inside `helper_int2ptr` + --> $DIR/mut_ref_in_final.rs:85:14 + | +LL | Some(&mut *(42 as *mut i32)) + | ^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here -error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (0x2a[noalloc] has no provenance) - --> $DIR/mut_ref_in_final.rs:88:5 +error[E0080]: reference not dereferenceable: reference must be dereferenceable for 4 bytes, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) + --> $DIR/mut_ref_in_final.rs:88:47 | LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | ^^^^^^^^^^^^^^^^ evaluation of `dangling::INT2PTR_STATIC` failed inside this call | - = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } +note: inside `helper_int2ptr` + --> $DIR/mut_ref_in_final.rs:85:14 + | +LL | Some(&mut *(42 as *mut i32)) + | ^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here error[E0080]: constructing invalid value of type Option<&mut i32>: at ..0, encountered a dangling reference (use-after-free) --> $DIR/mut_ref_in_final.rs:94:5 diff --git a/tests/ui/consts/const-size_of_val-align_of_val.rs b/tests/ui/consts/const-size_of_val-align_of_val.rs index d4b5a90351797..2ec5efd26710d 100644 --- a/tests/ui/consts/const-size_of_val-align_of_val.rs +++ b/tests/ui/consts/const-size_of_val-align_of_val.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(layout_for_ptr)] - use std::{mem, ptr}; struct Foo(#[allow(dead_code)] u32); diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr index 268527c0e5bf8..a809e7c265d22 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr @@ -17,11 +17,11 @@ LL | std::intrinsics::raw_eq(&(&0), &(&1)) = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported -error[E0080]: accessing memory with alignment 1, but alignment 4 is required - --> $DIR/intrinsic-raw_eq-const-bad.rs:17:5 +error[E0080]: encountered an unaligned reference (required 4 byte alignment but found 1) + --> $DIR/intrinsic-raw_eq-const-bad.rs:17:29 | LL | std::intrinsics::raw_eq(aref, aref) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_NOT_ALIGNED` failed here + | ^^^^ evaluation of `RAW_EQ_NOT_ALIGNED` failed here error: aborting due to 3 previous errors diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index afb627de1c1bb..c05cea0dbe533 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -38,7 +38,7 @@ error[E0046]: not all trait items implemented, missing: `call` LL | impl Fn(&isize) for Error { | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation | - = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` + = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` (unstable, requires feature `fn_traits`) error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-95023.rs:3:21 diff --git a/tests/ui/packed/issue-118537-field-offset-ice.rs b/tests/ui/packed/issue-118537-field-offset-ice.rs index 83bace96aac2f..e398f18f88e85 100644 --- a/tests/ui/packed/issue-118537-field-offset-ice.rs +++ b/tests/ui/packed/issue-118537-field-offset-ice.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(layout_for_ptr)] use std::mem; #[repr(packed(4))] diff --git a/tests/ui/packed/issue-118537-field-offset.rs b/tests/ui/packed/issue-118537-field-offset.rs index 906b3a9f976ec..d1245056db421 100644 --- a/tests/ui/packed/issue-118537-field-offset.rs +++ b/tests/ui/packed/issue-118537-field-offset.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(layout_for_ptr)] use std::mem; #[repr(packed, C)] diff --git a/tests/ui/panic-handler/panic-handler-with-track-caller.stderr b/tests/ui/panic-handler/panic-handler-with-track-caller.stderr index 605567acdb58b..9c922f4704652 100644 --- a/tests/ui/panic-handler/panic-handler-with-track-caller.stderr +++ b/tests/ui/panic-handler/panic-handler-with-track-caller.stderr @@ -1,11 +1,13 @@ error: `#[panic_handler]` function is not allowed to have `#[track_caller]` --> $DIR/panic-handler-with-track-caller.rs:10:1 | -LL | #[track_caller] - | ^^^^^^^^^^^^^^^ +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ LL | -LL | fn panic(info: &PanicInfo) -> ! { - | ------------------------------- `#[panic_handler]` function is not allowed to have `#[track_caller]` +LL | / fn panic(info: &PanicInfo) -> ! { +LL | | unimplemented!(); +LL | | } + | |_- `#[panic_handler]` function is not allowed to have `#[track_caller]` error: aborting due to 1 previous error diff --git a/tests/ui/traits/default-method/auxiliary/mioou.rs b/tests/ui/traits/default-method/auxiliary/mioou.rs new file mode 100644 index 0000000000000..f0cb013ad3348 --- /dev/null +++ b/tests/ui/traits/default-method/auxiliary/mioou.rs @@ -0,0 +1,31 @@ +#![feature(rustc_attrs)] +#![feature(staged_api)] +#![stable(feature="s", since="1.0.0")] + +#[rustc_must_implement_one_of(a1, b1)] +#[stable(feature="s", since="1.0.0")] +pub trait Trait1 { + #[stable(feature="s", since="1.0.0")] + fn a1(&self) -> u64 { + self.a1() + 1 + } + + #[stable(feature="s", since="1.0.0")] + fn b1(&self) -> u64 { + self.b1() + 1 + } +} + +#[rustc_must_implement_one_of(a2, b2)] +#[stable(feature="s", since="1.0.0")] +pub trait Trait2 { + #[stable(feature="s", since="1.0.0")] + fn a2(&self) -> u64 { + self.b2() + 1 + } + + #[unstable(feature="trait2_b2", issue="none")] + fn b2(&self) -> u64 { + self.a2() + 1 + } +} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs new file mode 100644 index 0000000000000..86e03e19e7835 --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs @@ -0,0 +1,16 @@ +//@ edition:2024 +//@ aux-crate:mioou=mioou.rs + +use mioou::*; + +struct A; + +impl Trait1 for A { +//~^ ERROR not all trait items implemented, missing one of: `a1`, `b1` +} + +impl Trait2 for A { +//~^ ERROR not all trait items implemented, missing one of: `a2`, `b2` +} + +fn main() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr new file mode 100644 index 0000000000000..6338fd1e45f27 --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr @@ -0,0 +1,21 @@ +error[E0046]: not all trait items implemented, missing one of: `a1`, `b1` + --> $DIR/rustc_must_implement_one_of-unstable.rs:8:1 + | +LL | impl Trait1 for A { + | ^^^^^^^^^^^^^^^^^ missing one of `a1`, `b1` in implementation + | + = help: implement the missing item: `fn a1(&self) -> u64 { todo!() }` + = help: implement the missing item: `fn b1(&self) -> u64 { todo!() }` + +error[E0046]: not all trait items implemented, missing one of: `a2`, `b2` + --> $DIR/rustc_must_implement_one_of-unstable.rs:12:1 + | +LL | impl Trait2 for A { + | ^^^^^^^^^^^^^^^^^ missing one of `a2`, `b2` in implementation + | + = help: implement the missing item: `fn a2(&self) -> u64 { todo!() }` + = help: implement the missing item: `fn b2(&self) -> u64 { todo!() }` (unstable, requires feature `trait2_b2`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index b8b9ea57612b4..0238e4ae18685 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -30,7 +30,7 @@ error[E0046]: not all trait items implemented, missing: `call` LL | impl Fn(&isize) for Error { | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation | - = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` + = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` (unstable, requires feature `fn_traits`) error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-87558.rs:3:21