Skip to content
Merged
13 changes: 9 additions & 4 deletions compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_hir::find_attr;
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
use rustc_middle::{bug, mir, span_bug};
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
use rustc_target::callconv::{ArgAbi, FnAbi};
use tracing::field::Empty;
use tracing::{info, instrument, trace};

Expand Down Expand Up @@ -284,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
'tcx: 'y,
{
assert_eq!(callee_ty, callee_abi.layout.ty);
if callee_abi.mode == PassMode::Ignore {
if callee_abi.is_ignore() {
// This one is skipped. Still must be made live though!
if !already_live {
self.storage_live(callee_arg.as_local().unwrap())?;
Expand Down Expand Up @@ -450,7 +450,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let mut caller_args = args
.iter()
.zip(caller_fn_abi.args.iter())
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
.filter(|arg_and_abi| !arg_and_abi.1.is_ignore());

// Now we have to spread them out across the callee's locals,
// taking into account the `spread_arg`. If we could write
Expand Down Expand Up @@ -480,7 +480,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {

// Consume the remaining arguments by putting them into the variable argument
// list.
let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?;
let varargs = self.allocate_varargs(
&mut caller_args,
// "Ignored" arguments aren't actually passed, so the callee should also
// ignore them. (`pass_argument` does this for regular arguments.)
(&mut callee_args_abis).filter(|(_, abi)| !abi.is_ignore()),
)?;
// When the frame is dropped, these variable arguments are deallocated.
self.frame_mut().va_list = varargs.clone();
let key = self.va_list_ptr(varargs.into());
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_const_eval/src/interpret/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,8 @@ impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// of variadic arguments. Return a list of the places that hold those arguments.
pub(crate) fn allocate_varargs<I, J>(
&mut self,
caller_args: &mut I,
callee_abis: &mut J,
caller_args: I,
mut callee_abis: J,
) -> InterpResult<'tcx, Vec<MPlaceTy<'tcx, M::Provenance>>>
where
I: Iterator<Item = (&'a FnArg<'tcx, M::Provenance>, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
Expand Down
69 changes: 45 additions & 24 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::borrow::Cow;
use std::fmt::Write;
use std::hash::Hash;
use std::mem;
use std::num::NonZero;

use either::{Left, Right};
Expand Down Expand Up @@ -288,6 +289,8 @@ struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> {
/// If this is `Some`, then `reset_provenance_and_padding` must be true (but not vice versa:
/// we might not track data vs padding bytes if the operand isn't stored in memory anyway).
data_bytes: Option<RangeSet>,
/// True if we are inside of `MaybeDangling`. This disables pointer access checks.
may_dangle: bool,
}

impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
Expand Down Expand Up @@ -489,7 +492,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
if place.layout.is_unsized() {
self.check_wide_ptr_meta(place.meta(), place.layout)?;
}
// Make sure this is dereferenceable and all.

// Determine size and alignment of pointee.
let size_and_align = try_validation!(
self.ecx.size_and_align_of_val(&place),
self.path,
Expand All @@ -503,27 +507,33 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
// alignment and size determined by the layout (size will be 0,
// alignment should take attributes into account).
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
try_validation!(
self.ecx.check_ptr_access(
place.ptr(),
size,
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
),
self.path,
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
ptr_kind,
// FIXME this says "null pointer" when null but we need translate
pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i))
},
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
ptr_kind
},
Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
ptr_kind,
},
);

if !self.may_dangle {
// Make sure this is dereferenceable and all.

// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
// Call `check_ptr_access` to avoid checking alignment here.
try_validation!(
self.ecx.check_ptr_access(
place.ptr(),
size,
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
),
self.path,
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
ptr_kind,
pointer: format!("{}", Pointer::<Option<AllocId>>::without_provenance(i))
},
Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
ptr_kind
},
Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
ptr_kind,
},
);
}

try_validation!(
self.ecx.check_ptr_align(
place.ptr(),
Expand All @@ -536,8 +546,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
found_bytes: has.bytes()
},
);
// Make sure this is non-null. We checked dereferenceability above, but if `size` is zero
// that does not imply non-null.

// Make sure this is non-null. This is obviously needed when `may_dangle` is set,
// but even if we did check dereferenceability above that would still allow null
// pointers if `size` is zero.
let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx);
if self.ecx.scalar_may_be_null(scalar)? {
let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..));
Expand Down Expand Up @@ -1265,6 +1277,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
ty::PatternKind::Or(_patterns) => {}
}
}
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
let old_may_dangle = mem::replace(&mut self.may_dangle, true);

let inner = self.ecx.project_field(val, FieldIdx::ZERO)?;
self.visit_value(&inner)?;

self.may_dangle = old_may_dangle;
}
_ => {
// default handler
try_validation!(
Expand Down Expand Up @@ -1350,6 +1370,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ecx,
reset_provenance_and_padding,
data_bytes: reset_padding.then_some(RangeSet(Vec::new())),
may_dangle: false,
};
v.visit_value(val)?;
v.reset_padding(val)?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ declare_features! (
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
/// Allows deref patterns.
(incomplete, deref_patterns, "1.79.0", Some(87121)),
(unstable, deref_patterns, "1.79.0", Some(87121)),
/// Allows deriving the From trait on single-field structs.
(unstable, derive_from, "1.91.0", Some(144889)),
/// Allows giving non-const impls custom diagnostic messages if attempted to be used as const
Expand Down
29 changes: 13 additions & 16 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1792,27 +1792,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

fn check_expr_tuple(
&self,
elts: &'tcx [hir::Expr<'tcx>],
elements: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let flds = expected.only_has_type(self).and_then(|ty| {
let ty = self.try_structurally_resolve_type(expr.span, ty);
match ty.kind() {
ty::Tuple(flds) => Some(&flds[..]),
_ => None,
}
let mut expectations = expected
.only_has_type(self)
.and_then(|ty| self.try_structurally_resolve_type(expr.span, ty).opt_tuple_fields())
.unwrap_or_default()
.iter();

let elements = elements.iter().map(|e| {
let ty = expectations.next().unwrap_or_else(|| self.next_ty_var(e.span));
self.check_expr_coercible_to_type(e, ty, None);
ty
});

let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds {
Some(fs) if i < fs.len() => {
let ety = fs[i];
self.check_expr_coercible_to_type(e, ety, None);
ety
}
_ => self.check_expr_with_expectation(e, NoExpectation),
});
let tuple = Ty::new_tup_from_iter(self.tcx, elt_ts_iter);
let tuple = Ty::new_tup_from_iter(self.tcx, elements);

if let Err(guar) = tuple.error_reported() {
Ty::new_error(self.tcx, guar)
} else {
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1592,7 +1592,8 @@ impl<'tcx> Ty<'tcx> {
}
}

/// Iterates over tuple fields.
/// Returns a list of tuple type arguments.
///
/// Panics when called on anything but a tuple.
#[inline]
pub fn tuple_fields(self) -> &'tcx List<Ty<'tcx>> {
Expand All @@ -1602,6 +1603,15 @@ impl<'tcx> Ty<'tcx> {
}
}

/// Returns a list of tuple type arguments, or `None` if `self` isn't a tuple.
#[inline]
pub fn opt_tuple_fields(self) -> Option<&'tcx List<Ty<'tcx>>> {
match self.kind() {
Tuple(args) => Some(args),
_ => None,
}
}

/// If the type contains variants, returns the valid range of variant indices.
//
// FIXME: This requires the optimized MIR in the case of coroutines.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ The tracking issue for this feature is: [#87121]

------------------------

> **Note**: This feature is incomplete. In the future, it is meant to supersede
> [`box_patterns`].
> **Note**: This feature supersedes [`box_patterns`].

This feature permits pattern matching on [smart pointers in the standard library] through their
`Deref` target types, either implicitly or with explicit `deref!(_)` patterns (the syntax of which
is currently a placeholder).

```rust
#![feature(deref_patterns)]
#![allow(incomplete_features)]

let mut v = vec![Box::new(Some(0))];

Expand Down Expand Up @@ -58,7 +56,6 @@ Like [`box_patterns`], deref patterns may move out of boxes:

```rust
# #![feature(deref_patterns)]
# #![allow(incomplete_features)]
struct NoCopy;
let deref!(x) = Box::new(NoCopy);
drop::<NoCopy>(x);
Expand All @@ -69,7 +66,6 @@ allowing then to be used in deref patterns:

```rust
# #![feature(deref_patterns)]
# #![allow(incomplete_features)]
match ("test".to_string(), Box::from("test"), b"test".to_vec()) {
("test", "test", b"test") => {}
_ => panic!(),
Expand Down
4 changes: 4 additions & 0 deletions src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
RetagInfo { cause: self.retag_cause, in_field: self.in_field },
)?;
self.ecx.write_immediate(*val, place)?;

interp_ok(())
}
}
Expand Down Expand Up @@ -964,6 +965,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// even if field retagging is not enabled. *shrug*)
self.walk_value(place)?;
}
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
// Skip traversing for everything inside of `MaybeDangling`
}
_ => {
// Not a reference/pointer/box. Recurse.
let in_field = mem::replace(&mut self.in_field, true); // remember and restore old value
Expand Down
3 changes: 3 additions & 0 deletions src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// even if field retagging is not enabled. *shrug*)
self.walk_value(place)?;
}
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
// Skip traversing for everything inside of `MaybeDangling`
}
_ => {
// Not a reference/pointer/box. Recurse.
self.walk_value(place)?;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Test that an unaligned `MaybeDangling<&u8>` is still detected as UB.
//
//@compile-flags: -Zmiri-disable-stacked-borrows
#![feature(maybe_dangling)]

use std::mem::{MaybeDangling, transmute};

fn main() {
let a = [1u16, 0u16];
unsafe {
let unaligned = MaybeDangling::new(a.as_ptr().byte_add(1));
transmute::<MaybeDangling<*const u16>, MaybeDangling<&u16>>(unaligned)
//~^ ERROR: Undefined Behavior: constructing invalid value: encountered an unaligned reference
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
--> tests/fail/unaligned_pointers/maybe_dangling_unalighed.rs:LL:CC
|
LL | transmute::<MaybeDangling<*const u16>, MaybeDangling<&u16>>(unaligned)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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

13 changes: 13 additions & 0 deletions src/tools/miri/tests/fail/validity/maybe_dangling_null.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Test that a null `MaybeDangling<&u8>` is still detected as UB.
//
//@compile-flags: -Zmiri-disable-stacked-borrows
#![feature(maybe_dangling)]

use std::mem::{MaybeDangling, transmute};
use std::ptr::null;

fn main() {
let null = MaybeDangling::new(null());
unsafe { transmute::<MaybeDangling<*const u8>, MaybeDangling<&u8>>(null) };
//~^ ERROR: Undefined Behavior: constructing invalid value: encountered a null reference
}
13 changes: 13 additions & 0 deletions src/tools/miri/tests/fail/validity/maybe_dangling_null.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: Undefined Behavior: constructing invalid value: encountered a null reference
--> tests/fail/validity/maybe_dangling_null.rs:LL:CC
|
LL | unsafe { transmute::<MaybeDangling<*const u8>, MaybeDangling<&u8>>(null) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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

Loading
Loading