Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b372dce
allocations: document that they can be read-only
RalfJung Jul 18, 2026
2a8cada
mention target-specific limitations can exist
RalfJung Jul 22, 2026
f9db7f4
When issuing suggestions for missing trait items, label unstable items
joshtriplett Jul 31, 2026
c4058be
Add test for suggestions on `must_implement_one_of`
joshtriplett Jul 31, 2026
0f35baa
Update Enzyme to resolve two of the open bugs
ZuseZ4 Jul 31, 2026
20eb58f
Remove final use of sealed traits from stdlib
jhpratt Aug 1, 2026
d9c81f7
stabilize size_of_val_raw, align_of_val_raw, Layout::for_value_raw
RalfJung Jun 7, 2026
fb485fa
make it explicit that the extern type behavior is unstable
RalfJung Jun 17, 2026
9e7711f
don't repeat the safety invariant
RalfJung Jul 8, 2026
14d28b1
miri: ensure validity of references and pointers we dereference
RalfJung Jul 27, 2026
fcf68f2
introduce a new CastKind for ElaborateBoxDerefs
RalfJung Jul 29, 2026
b3724b6
also validate the inputs of casts
RalfJung Jul 30, 2026
907a562
also catch invalid pointers coming into PtrMetadata
RalfJung Jul 30, 2026
bb24b38
Box::leak: tell people to avoid unleaking
RalfJung Aug 1, 2026
c2d84bc
Replace unsafe usage of `NonNull::new_unchecked` with `Box::into_non_…
ArhanChaudhary Aug 1, 2026
6d6e8d3
Make the noundef-on-Cast size guard explicit
Vastargazing Aug 1, 2026
596b026
Move `check_track_caller` into the attribute parser
rperier Aug 1, 2026
f21db8d
Rollup merge of #157572 - RalfJung:layout-of-raw, r=saethlin
JonathanBrouwer Aug 1, 2026
ab287d1
Rollup merge of #160012 - RalfJung:deref-validity, r=oli-obk
JonathanBrouwer Aug 1, 2026
51dc9af
Rollup merge of #160294 - ZuseZ4:update-enzyme-july-26, r=jieyouxu
JonathanBrouwer Aug 1, 2026
58b86ea
Rollup merge of #159503 - RalfJung:allocations, r=Mark-Simulacrum
JonathanBrouwer Aug 1, 2026
f94ba6a
Rollup merge of #160250 - joshtriplett:must-implement-one-of-no-unsta…
JonathanBrouwer Aug 1, 2026
2fbcb36
Rollup merge of #160251 - ArhanChaudhary:nonnull-from-mut, r=jhpratt,…
JonathanBrouwer Aug 1, 2026
808b469
Rollup merge of #160311 - jhpratt:sealed-traits, r=joboet
JonathanBrouwer Aug 1, 2026
5213a3d
Rollup merge of #160313 - Vastargazing:abi-cast-noundef-size-guard, r…
JonathanBrouwer Aug 1, 2026
8171cef
Rollup merge of #160323 - RalfJung:no-unleak, r=nia-e
JonathanBrouwer Aug 1, 2026
a77b919
Rollup merge of #160328 - rperier:move_check_track_caller_into_attrib…
JonathanBrouwer Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
};
Expand Down
80 changes: 76 additions & 4 deletions compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 => {
Expand Down Expand Up @@ -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...
Expand All @@ -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<T>` 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)?;
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,14 +1070,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>,
) -> 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)?;
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/interpret/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) => {
Expand Down
Loading
Loading