mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #132005 - matthiaskrgr:rollup-ced4upi, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #130350 (stabilize Strict Provenance and Exposed Provenance APIs) - #131737 (linkchecker: add a reminder on broken links to add new/renamed pages to `SUMMARY.md` for mdBooks) - #131991 (test: Add test for trait in FQS cast, issue #98565) - #131997 (Make `rustc_abi` compile on stable again) - #131999 (Improve test coverage for `unit_bindings` lint) - #132001 (fix coherence error for very large tuples™) - #132003 (update ABI compatibility docs for new option-like rules) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
31e102c509
@ -3,18 +3,23 @@ mod abi {
|
||||
pub(crate) use crate::Variants;
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
|
||||
#[cfg(feature = "nightly")]
|
||||
use crate::{Abi, FieldsShape, TyAbiInterface, TyAndLayout};
|
||||
use crate::{Align, HasDataLayout, Size};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum RegKind {
|
||||
Integer,
|
||||
Float,
|
||||
Vector,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Reg {
|
||||
pub kind: RegKind,
|
||||
pub size: Size,
|
||||
@ -108,15 +113,8 @@ impl HomogeneousAggregate {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
|
||||
pub fn is_aggregate(&self) -> bool {
|
||||
match self.abi {
|
||||
Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Homogeneous` if this layout is an aggregate containing fields of
|
||||
/// only a single type (e.g., `(u32, u32)`). Such aggregates are often
|
||||
/// special-cased in ABIs.
|
||||
|
@ -11,8 +11,10 @@ use crate::{
|
||||
Variants, WrappingRange,
|
||||
};
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
mod ty;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
|
||||
// A variant is absent if it's uninhabited and only has ZST fields.
|
||||
|
@ -29,14 +29,14 @@ mod layout;
|
||||
mod tests;
|
||||
|
||||
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
|
||||
pub use layout::{
|
||||
FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface,
|
||||
TyAndLayout, VariantIdx,
|
||||
};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
pub use layout::{LayoutCalculator, LayoutCalculatorError};
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
#[cfg(feature = "nightly")]
|
||||
pub trait HashStableContext {}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
||||
@ -1644,6 +1644,14 @@ pub struct LayoutS<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
}
|
||||
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> {
|
||||
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
|
||||
pub fn is_aggregate(&self) -> bool {
|
||||
match self.abi {
|
||||
Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false,
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
|
||||
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
|
||||
let size = scalar.size(cx);
|
||||
|
@ -23,7 +23,6 @@
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#![feature(let_chains)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(try_blocks)]
|
||||
#![warn(unreachable_pub)]
|
||||
|
@ -361,12 +361,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
|
||||
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
|
||||
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
|
||||
(Pointer(..), Int(..)) => bx.ptrtoint(imm, to_backend_ty),
|
||||
(Pointer(..), Int(..)) => {
|
||||
// FIXME: this exposes the provenance, which shouldn't be necessary.
|
||||
bx.ptrtoint(imm, to_backend_ty)
|
||||
}
|
||||
(Float(_), Pointer(..)) => {
|
||||
let int_imm = bx.bitcast(imm, bx.cx().type_isize());
|
||||
bx.ptradd(bx.const_null(bx.type_ptr()), int_imm)
|
||||
}
|
||||
(Pointer(..), Float(_)) => {
|
||||
// FIXME: this exposes the provenance, which shouldn't be necessary.
|
||||
let int_imm = bx.ptrtoint(imm, bx.cx().type_isize());
|
||||
bx.bitcast(int_imm, to_backend_ty)
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(unqualified_local_imports)]
|
||||
|
@ -33,7 +33,6 @@
|
||||
#![feature(ptr_alignment_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(test)]
|
||||
#![feature(thread_id_value)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
@ -595,7 +595,7 @@ declare_features! (
|
||||
/// Allows attributes on expressions and non-item statements.
|
||||
(unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
|
||||
/// Allows lints part of the strict provenance effort.
|
||||
(unstable, strict_provenance, "1.61.0", Some(95228)),
|
||||
(unstable, strict_provenance_lints, "1.61.0", Some(130351)),
|
||||
/// Allows string patterns to dereference values to match them.
|
||||
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
|
||||
/// Allows the use of `#[target_feature]` on safe functions.
|
||||
|
@ -2667,7 +2667,6 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(strict_provenance)]
|
||||
/// #![warn(fuzzy_provenance_casts)]
|
||||
///
|
||||
/// fn main() {
|
||||
@ -2701,7 +2700,7 @@ declare_lint! {
|
||||
pub FUZZY_PROVENANCE_CASTS,
|
||||
Allow,
|
||||
"a fuzzy integer to pointer cast is used",
|
||||
@feature_gate = strict_provenance;
|
||||
@feature_gate = strict_provenance_lints;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
@ -2711,7 +2710,6 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(strict_provenance)]
|
||||
/// #![warn(lossy_provenance_casts)]
|
||||
///
|
||||
/// fn main() {
|
||||
@ -2747,7 +2745,7 @@ declare_lint! {
|
||||
pub LOSSY_PROVENANCE_CASTS,
|
||||
Allow,
|
||||
"a lossy pointer to integer cast is used",
|
||||
@feature_gate = strict_provenance;
|
||||
@feature_gate = strict_provenance_lints;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
@ -56,7 +56,6 @@
|
||||
#![feature(ptr_alignment_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(trait_upcasting)]
|
||||
#![feature(trusted_len)]
|
||||
#![feature(try_blocks)]
|
||||
|
@ -16,7 +16,7 @@ use rustc_type_ir::fold::TypeFoldable;
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::relate::solver_relating::RelateExt;
|
||||
use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner};
|
||||
use tracing::{instrument, trace};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use crate::canonicalizer::{CanonicalizeMode, Canonicalizer};
|
||||
use crate::delegate::SolverDelegate;
|
||||
@ -165,12 +165,22 @@ where
|
||||
// HACK: We bail with overflow if the response would have too many non-region
|
||||
// inference variables. This tends to only happen if we encounter a lot of
|
||||
// ambiguous alias types which get replaced with fresh inference variables
|
||||
// during generalization. This prevents a hang in nalgebra.
|
||||
let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region()).count();
|
||||
if num_non_region_vars > self.cx().recursion_limit() {
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
|
||||
suggest_increasing_limit: true,
|
||||
}));
|
||||
// during generalization. This prevents hangs caused by an exponential blowup,
|
||||
// see tests/ui/traits/next-solver/coherence-alias-hang.rs.
|
||||
//
|
||||
// We don't do so for `NormalizesTo` goals as we erased the expected term and
|
||||
// bailing with overflow here would prevent us from detecting a type-mismatch,
|
||||
// causing a coherence error in diesel, see #131969. We still bail with verflow
|
||||
// when later returning from the parent AliasRelate goal.
|
||||
if !self.is_normalizes_to_goal {
|
||||
let num_non_region_vars =
|
||||
canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count();
|
||||
if num_non_region_vars > self.cx().recursion_limit() {
|
||||
debug!(?num_non_region_vars, "too many inference variables -> overflow");
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
|
||||
suggest_increasing_limit: true,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(canonical)
|
||||
|
@ -1913,7 +1913,7 @@ symbols! {
|
||||
str_trim,
|
||||
str_trim_end,
|
||||
str_trim_start,
|
||||
strict_provenance,
|
||||
strict_provenance_lints,
|
||||
string_as_mut_str,
|
||||
string_as_str,
|
||||
string_deref_patterns,
|
||||
|
@ -298,6 +298,7 @@ fn equate_impl_headers<'tcx>(
|
||||
}
|
||||
|
||||
/// The result of [fn impl_intersection_has_impossible_obligation].
|
||||
#[derive(Debug)]
|
||||
enum IntersectionHasImpossibleObligations<'tcx> {
|
||||
Yes,
|
||||
No {
|
||||
@ -328,6 +329,7 @@ enum IntersectionHasImpossibleObligations<'tcx> {
|
||||
/// of the two impls above to be empty.
|
||||
///
|
||||
/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
|
||||
#[instrument(level = "debug", skip(selcx), ret)]
|
||||
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
|
||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
obligations: &'a [PredicateObligation<'tcx>],
|
||||
|
@ -4,7 +4,8 @@
|
||||
#![feature(iter_next_chunk)]
|
||||
#![feature(repr_simd)]
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(strict_provenance)]
|
||||
#![cfg_attr(bootstrap, feature(strict_provenance))]
|
||||
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
|
||||
#![feature(test)]
|
||||
#![deny(fuzzy_provenance_casts)]
|
||||
|
||||
|
@ -147,7 +147,6 @@
|
||||
#![feature(slice_range)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(trusted_fused)]
|
||||
#![feature(trusted_len)]
|
||||
#![feature(trusted_random_access)]
|
||||
@ -162,6 +161,8 @@
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(strict_provenance))]
|
||||
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
|
||||
#![cfg_attr(not(test), feature(coroutine_trait))]
|
||||
#![cfg_attr(test, feature(panic_update_hook))]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
|
@ -32,7 +32,8 @@
|
||||
#![feature(panic_update_hook)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(thin_box)]
|
||||
#![feature(strict_provenance)]
|
||||
#![cfg_attr(bootstrap, feature(strict_provenance))]
|
||||
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
|
||||
#![feature(drain_keep_rest)]
|
||||
#![feature(local_waker)]
|
||||
#![feature(vec_pop_if)]
|
||||
|
@ -2794,7 +2794,6 @@ where
|
||||
/// #![feature(is_val_statically_known)]
|
||||
/// #![feature(core_intrinsics)]
|
||||
/// # #![allow(internal_features)]
|
||||
/// #![feature(strict_provenance)]
|
||||
/// use std::intrinsics::is_val_statically_known;
|
||||
///
|
||||
/// fn foo(x: &i32) -> bool {
|
||||
|
@ -163,7 +163,6 @@
|
||||
#![feature(str_internals)]
|
||||
#![feature(str_split_inclusive_remainder)]
|
||||
#![feature(str_split_remainder)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(ub_checks)]
|
||||
#![feature(unchecked_neg)]
|
||||
#![feature(unchecked_shifts)]
|
||||
@ -174,6 +173,8 @@
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(strict_provenance))]
|
||||
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
|
||||
#![feature(abi_unadjusted)]
|
||||
#![feature(adt_const_params)]
|
||||
#![feature(allow_internal_unsafe)]
|
||||
|
@ -1784,9 +1784,11 @@ mod prim_ref {}
|
||||
/// unique field that doesn't have size 0 and alignment 1 (if there is such a field).
|
||||
/// - `i32` is ABI-compatible with `NonZero<i32>`, and similar for all other integer types.
|
||||
/// - If `T` is guaranteed to be subject to the [null pointer
|
||||
/// optimization](option/index.html#representation), then `T` and `Option<T>` are ABI-compatible.
|
||||
/// Furthermore, if `U` satisfies the requirements [outlined here](result/index.html#representation),
|
||||
/// then `T` and `Result<T, U>` and `Result<U, T>` are all ABI-compatible.
|
||||
/// optimization](option/index.html#representation), and `E` is an enum satisfying the following
|
||||
/// requirements, then `T` and `E` are ABI-compatible. Such an enum `E` is called "option-like".
|
||||
/// - The enum `E` has exactly two variants.
|
||||
/// - One variant has exactly one field, of type `T`.
|
||||
/// - All fields of the other variant are zero-sized with 1-byte alignment.
|
||||
///
|
||||
/// Furthermore, ABI compatibility satisfies the following general properties:
|
||||
///
|
||||
|
@ -137,10 +137,11 @@ impl<T: ?Sized> *const T {
|
||||
|
||||
/// Gets the "address" portion of the pointer.
|
||||
///
|
||||
/// This is similar to `self as usize`, which semantically discards *provenance* and
|
||||
/// *address-space* information. However, unlike `self as usize`, casting the returned address
|
||||
/// back to a pointer yields a [pointer without provenance][without_provenance], which is undefined behavior to dereference. To
|
||||
/// properly restore the lost information and obtain a dereferenceable pointer, use
|
||||
/// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of
|
||||
/// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that
|
||||
/// casting the returned address back to a pointer yields a [pointer without
|
||||
/// provenance][without_provenance], which is undefined behavior to dereference. To properly
|
||||
/// restore the lost information and obtain a dereferenceable pointer, use
|
||||
/// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
|
||||
///
|
||||
/// If using those APIs is not possible because there is no way to preserve a pointer with the
|
||||
@ -155,90 +156,81 @@ impl<T: ?Sized> *const T {
|
||||
/// perform a change of representation to produce a value containing only the address
|
||||
/// portion of the pointer. What that means is up to the platform to define.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
|
||||
/// might change in the future (including possibly weakening this so it becomes wholly
|
||||
/// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn addr(self) -> usize {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
// A pointer-to-integer transmute currently has exactly the right semantics: it returns the
|
||||
// address without exposing the provenance. Note that this is *not* a stable guarantee about
|
||||
// transmute semantics, it relies on sysroot crates having special status.
|
||||
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
|
||||
// provenance).
|
||||
unsafe { mem::transmute(self.cast::<()>()) }
|
||||
}
|
||||
|
||||
/// Exposes the "provenance" part of the pointer for future use in
|
||||
/// [`with_exposed_provenance`][] and returns the "address" portion.
|
||||
/// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in
|
||||
/// [`with_exposed_provenance`] and returns the "address" portion.
|
||||
///
|
||||
/// This is equivalent to `self as usize`, which semantically discards *provenance* and
|
||||
/// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
|
||||
/// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
|
||||
/// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its
|
||||
/// provenance. (Reconstructing address space information, if required, is your responsibility.)
|
||||
/// This is equivalent to `self as usize`, which semantically discards provenance information.
|
||||
/// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the
|
||||
/// provenance as 'exposed', so on platforms that support it you can later call
|
||||
/// [`with_exposed_provenance`] to reconstitute the original pointer including its provenance.
|
||||
///
|
||||
/// Using this method means that code is *not* following [Strict
|
||||
/// Provenance][super#strict-provenance] rules. Supporting
|
||||
/// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by
|
||||
/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
|
||||
/// use [`addr`][pointer::addr] wherever possible.
|
||||
/// Due to its inherent ambiguity, [`with_exposed_provenance`] may not be supported by tools
|
||||
/// that help you to stay conformant with the Rust memory model. It is recommended to use
|
||||
/// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr]
|
||||
/// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`.
|
||||
///
|
||||
/// On most platforms this will produce a value with the same bytes as the original pointer,
|
||||
/// because all the bytes are dedicated to describing the address. Platforms which need to store
|
||||
/// additional information in the pointer may not support this operation, since the 'expose'
|
||||
/// side-effect which is required for [`with_exposed_provenance`][] to work is typically not
|
||||
/// side-effect which is required for [`with_exposed_provenance`] to work is typically not
|
||||
/// available.
|
||||
///
|
||||
/// It is unclear whether this method can be given a satisfying unambiguous specification. This
|
||||
/// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance].
|
||||
/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
|
||||
///
|
||||
/// [`with_exposed_provenance`]: with_exposed_provenance
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "exposed_provenance", issue = "95228")]
|
||||
#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn expose_provenance(self) -> usize {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
self.cast::<()>() as usize
|
||||
}
|
||||
|
||||
/// Creates a new pointer with the given address.
|
||||
/// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
|
||||
/// `self`.
|
||||
///
|
||||
/// This performs the same operation as an `addr as ptr` cast, but copies
|
||||
/// the *address-space* and *provenance* of `self` to the new pointer.
|
||||
/// This allows us to dynamically preserve and propagate this important
|
||||
/// information in a way that is otherwise impossible with a unary cast.
|
||||
/// This is similar to a `addr as *const T` cast, but copies
|
||||
/// the *provenance* of `self` to the new pointer.
|
||||
/// This avoids the inherent ambiguity of the unary cast.
|
||||
///
|
||||
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
|
||||
/// `self` to the given address, and therefore has all the same capabilities and restrictions.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn with_addr(self, addr: usize) -> Self {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
//
|
||||
// In the mean-time, this operation is defined to be "as if" it was
|
||||
// a wrapping_offset, so we can emulate it as such. This should properly
|
||||
// restore pointer provenance even under today's compiler.
|
||||
// This should probably be an intrinsic to avoid doing any sort of arithmetic, but
|
||||
// meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's
|
||||
// provenance.
|
||||
let self_addr = self.addr() as isize;
|
||||
let dest_addr = addr as isize;
|
||||
let offset = dest_addr.wrapping_sub(self_addr);
|
||||
|
||||
// This is the canonical desugaring of this operation
|
||||
self.wrapping_byte_offset(offset)
|
||||
}
|
||||
|
||||
/// Creates a new pointer by mapping `self`'s address to a new one.
|
||||
/// Creates a new pointer by mapping `self`'s address to a new one, preserving the
|
||||
/// [provenance][crate::ptr#provenance] of `self`.
|
||||
///
|
||||
/// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
|
||||
self.with_addr(f(self.addr()))
|
||||
}
|
||||
@ -379,7 +371,7 @@ impl<T: ?Sized> *const T {
|
||||
/// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
|
||||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
@ -560,7 +552,7 @@ impl<T: ?Sized> *const T {
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ptr_mask, strict_provenance)]
|
||||
/// #![feature(ptr_mask)]
|
||||
/// let v = 17_u32;
|
||||
/// let ptr: *const u32 = &v;
|
||||
///
|
||||
@ -611,7 +603,7 @@ impl<T: ?Sized> *const T {
|
||||
/// * `self` and `origin` must either
|
||||
///
|
||||
/// * point to the same address, or
|
||||
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
|
||||
/// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
|
||||
/// the two pointers must be in bounds of that object. (See below for an example.)
|
||||
///
|
||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||
@ -871,7 +863,7 @@ impl<T: ?Sized> *const T {
|
||||
/// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
|
||||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
@ -978,7 +970,7 @@ impl<T: ?Sized> *const T {
|
||||
/// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
|
||||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
|
@ -18,10 +18,11 @@
|
||||
//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
|
||||
//! The following points are only concerned with non-zero-sized accesses.
|
||||
//! * A [null] pointer is *never* valid.
|
||||
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
|
||||
//! be *dereferenceable*: the memory range of the given size starting at the pointer must all be
|
||||
//! within the bounds of a single allocated object. Note that in Rust,
|
||||
//! every (stack-allocated) variable is considered a separate allocated object.
|
||||
//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be
|
||||
//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated
|
||||
//! object] it is derived from; a pointer is dereferenceable if the memory range of the given size
|
||||
//! starting at the pointer is entirely contained within the bounds of that allocated object. Note
|
||||
//! that in Rust, every (stack-allocated) variable is considered a separate allocated object.
|
||||
//! * All accesses performed by functions in this module are *non-atomic* in the sense
|
||||
//! of [atomic operations] used to synchronize between threads. This means it is
|
||||
//! undefined behavior to perform two concurrent accesses to the same location from different
|
||||
@ -130,123 +131,130 @@
|
||||
//!
|
||||
//! [`null()`]: null
|
||||
//!
|
||||
//! # Strict Provenance
|
||||
//!
|
||||
//! **The following text is non-normative, insufficiently formal, and is an extremely strict
|
||||
//! interpretation of provenance. It's ok if your code doesn't strictly conform to it.**
|
||||
//!
|
||||
//! [Strict Provenance][] is an experimental set of APIs that help tools that try
|
||||
//! to validate the memory-safety of your program's execution. Notably this includes [Miri][]
|
||||
//! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate
|
||||
//! Rust's memory model.
|
||||
//!
|
||||
//! Provenance must exist in some form for any programming
|
||||
//! language compiled for modern computer architectures, but specifying a model for provenance
|
||||
//! in a way that is useful to both compilers and programmers is an ongoing challenge.
|
||||
//! The [Strict Provenance][] experiment seeks to explore the question: *what if we just said you
|
||||
//! couldn't do all the nasty operations that make provenance so messy?*
|
||||
//!
|
||||
//! What APIs would have to be removed? What APIs would have to be added? How much would code
|
||||
//! have to change, and is it worse or better now? Would any patterns become truly inexpressible?
|
||||
//! Could we carve out special exceptions for those patterns? Should we?
|
||||
//!
|
||||
//! A secondary goal of this project is to see if we can disambiguate the many functions of
|
||||
//! pointer<->integer casts enough for the definition of `usize` to be loosened so that it
|
||||
//! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue
|
||||
//! to conflate these notions). This would potentially make it possible to more efficiently
|
||||
//! target platforms where pointers are larger than offsets, such as CHERI and maybe some
|
||||
//! segmented architectures.
|
||||
//!
|
||||
//! ## Provenance
|
||||
//!
|
||||
//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.**
|
||||
//! # Provenance
|
||||
//!
|
||||
//! Pointers are not *simply* an "integer" or "address". For instance, it's uncontroversial
|
||||
//! to say that a Use After Free is clearly Undefined Behaviour, even if you "get lucky"
|
||||
//! and the freed memory gets reallocated before your read/write (in fact this is the
|
||||
//! worst-case scenario, UAFs would be much less concerning if this didn't happen!).
|
||||
//! To rationalize this claim, pointers need to somehow be *more* than just their addresses:
|
||||
//! they must have provenance.
|
||||
//! As another example, consider that [`wrapping_offset`] is documented to "remember"
|
||||
//! the allocated object that the original pointer points to, even if it is offset far
|
||||
//! outside the memory range occupied by that allocated object.
|
||||
//! To rationalize claims like this, pointers need to somehow be *more* than just their addresses:
|
||||
//! they must have **provenance**.
|
||||
//!
|
||||
//! When an allocation is created, that allocation has a unique Original Pointer. For alloc
|
||||
//! APIs this is literally the pointer the call returns, and for local variables and statics,
|
||||
//! this is the name of the variable/static. This is mildly overloading the term "pointer"
|
||||
//! for the sake of brevity/exposition.
|
||||
//! A pointer value in Rust semantically contains the following information:
|
||||
//!
|
||||
//! The Original Pointer for an allocation is guaranteed to have unique access to the entire
|
||||
//! allocation and *only* that allocation. In this sense, an allocation can be thought of
|
||||
//! as a "sandbox" that cannot be broken into or out of. *Provenance* is the permission
|
||||
//! to access an allocation's sandbox and has both a *spatial* and *temporal* component:
|
||||
//!
|
||||
//! * Spatial: A range of bytes that the pointer is allowed to access.
|
||||
//! * Temporal: The lifetime (of the allocation) that access to these bytes is tied to.
|
||||
//!
|
||||
//! Spatial provenance makes sure you don't go beyond your sandbox, while temporal provenance
|
||||
//! makes sure that you can't "get lucky" after your permission to access some memory
|
||||
//! has been revoked (either through deallocations or borrows expiring).
|
||||
//!
|
||||
//! Provenance is implicitly shared with all pointers transitively derived from
|
||||
//! The Original Pointer through operations like [`offset`], borrowing, and pointer casts.
|
||||
//! Some operations may *shrink* the derived provenance, limiting how much memory it can
|
||||
//! access or how long it's valid for (i.e. borrowing a subfield and subslicing).
|
||||
//!
|
||||
//! Shrinking provenance cannot be undone: even if you "know" there is a larger allocation, you
|
||||
//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine"
|
||||
//! two contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
|
||||
//!
|
||||
//! A reference to a value always has provenance over exactly the memory that field occupies.
|
||||
//! A reference to a slice always has provenance over exactly the range that slice describes.
|
||||
//!
|
||||
//! If an allocation is deallocated, all pointers with provenance to that allocation become
|
||||
//! invalidated, and effectively lose their provenance.
|
||||
//!
|
||||
//! The strict provenance experiment is mostly only interested in exploring stricter *spatial*
|
||||
//! provenance. In this sense it can be thought of as a subset of the more ambitious and
|
||||
//! formal [Stacked Borrows][] research project, which is what tools like [Miri][] are based on.
|
||||
//! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed
|
||||
//! to do and when they become invalidated. This necessarily involves much more complex
|
||||
//! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code
|
||||
//! for the strict provenance experiment will also greatly help Stacked Borrows.
|
||||
//!
|
||||
//!
|
||||
//! ## Pointer Vs Addresses
|
||||
//!
|
||||
//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.**
|
||||
//!
|
||||
//! One of the largest historical issues with trying to define provenance is that programmers
|
||||
//! freely convert between pointers and integers. Once you allow for this, it generally becomes
|
||||
//! impossible to accurately track and preserve provenance information, and you need to appeal
|
||||
//! to very complex and unreliable heuristics. But of course, converting between pointers and
|
||||
//! integers is very useful, so what can we do?
|
||||
//!
|
||||
//! Also did you know WASM is actually a "Harvard Architecture"? As in function pointers are
|
||||
//! handled completely differently from data pointers? And we kind of just shipped Rust on WASM
|
||||
//! without really addressing the fact that we let you freely convert between function pointers
|
||||
//! and data pointers, because it mostly Just Works? Let's just put that on the "pointer casts
|
||||
//! are dubious" pile.
|
||||
//!
|
||||
//! Strict Provenance attempts to square these circles by decoupling Rust's traditional conflation
|
||||
//! of pointers and `usize` (and `isize`), and defining a pointer to semantically contain the
|
||||
//! following information:
|
||||
//!
|
||||
//! * The **address-space** it is part of (e.g. "data" vs "code" in WASM).
|
||||
//! * The **address** it points to, which can be represented by a `usize`.
|
||||
//! * The **provenance** it has, defining the memory it has permission to access.
|
||||
//! Provenance can be absent, in which case the pointer does not have permission to access any memory.
|
||||
//! * The **provenance** it has, defining the memory it has permission to access. Provenance can be
|
||||
//! absent, in which case the pointer does not have permission to access any memory.
|
||||
//!
|
||||
//! Under Strict Provenance, a `usize` *cannot* accurately represent a pointer, and converting from
|
||||
//! a pointer to a `usize` is generally an operation which *only* extracts the address. It is
|
||||
//! therefore *impossible* to construct a valid pointer from a `usize` because there is no way
|
||||
//! to restore the address-space and provenance. In other words, pointer-integer-pointer
|
||||
//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable).
|
||||
//! The exact structure of provenance is not yet specified, but the permission defined by a
|
||||
//! pointer's provenance have a *spatial* component, a *temporal* component, and a *mutability*
|
||||
//! component:
|
||||
//!
|
||||
//! The key insight to making this model *at all* viable is the [`with_addr`][] method:
|
||||
//! * Spatial: The set of memory addresses that the pointer is allowed to access.
|
||||
//! * Temporal: The timespan during which the pointer is allowed to access those memory addresses.
|
||||
//! * Mutability: Whether the pointer may only access the memory for reads, or also access it for
|
||||
//! writes. Note that this can interact with the other components, e.g. a pointer might permit
|
||||
//! mutation only for a subset of addresses, or only for a subset of its maximal timespan.
|
||||
//!
|
||||
//! When an [allocated object] is created, it has a unique Original Pointer. For alloc
|
||||
//! APIs this is literally the pointer the call returns, and for local variables and statics,
|
||||
//! this is the name of the variable/static. (This is mildly overloading the term "pointer"
|
||||
//! for the sake of brevity/exposition.)
|
||||
//!
|
||||
//! The Original Pointer for an allocated object has provenance that constrains the *spatial*
|
||||
//! permissions of this pointer to the memory range of the allocation, and the *temporal*
|
||||
//! permissions to the lifetime of the allocation. Provenance is implicitly inherited by all
|
||||
//! pointers transitively derived from the Original Pointer through operations like [`offset`],
|
||||
//! borrowing, and pointer casts. Some operations may *shrink* the permissions of the derived
|
||||
//! provenance, limiting how much memory it can access or how long it's valid for (i.e. borrowing a
|
||||
//! subfield and subslicing can shrink the spatial component of provenance, and all borrowing can
|
||||
//! shrink the temporal component of provenance). However, no operation can ever *grow* the
|
||||
//! permissions of the derived provenance: even if you "know" there is a larger allocation, you
|
||||
//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine" two
|
||||
//! contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`).
|
||||
//!
|
||||
//! A reference to a place always has provenance over at least the memory that place occupies.
|
||||
//! A reference to a slice always has provenance over at least the range that slice describes.
|
||||
//! Whether and when exactly the provenance of a reference gets "shrunk" to *exactly* fit
|
||||
//! the memory it points to is not yet determined.
|
||||
//!
|
||||
//! A *shared* reference only ever has provenance that permits reading from memory,
|
||||
//! and never permits writes, except inside [`UnsafeCell`].
|
||||
//!
|
||||
//! Provenance can affect whether a program has undefined behavior:
|
||||
//!
|
||||
//! * It is undefined behavior to access memory through a pointer that does not have provenance over
|
||||
//! that memory. Note that a pointer "at the end" of its provenance is not actually outside its
|
||||
//! provenance, it just has 0 bytes it can load/store. Zero-sized accesses do not require any
|
||||
//! provenance since they access an empty range of memory.
|
||||
//!
|
||||
//! * It is undefined behavior to [`offset`] a pointer across a memory range that is not contained
|
||||
//! in the allocated object it is derived from, or to [`offset_from`] two pointers not derived
|
||||
//! from the same allocated object. Provenance is used to say what exactly "derived from" even
|
||||
//! means: the lineage of a pointer is traced back to the Original Pointer it descends from, and
|
||||
//! that identifies the relevant allocated object. In particular, it's always UB to offset a
|
||||
//! pointer derived from something that is now deallocated, except if the offset is 0.
|
||||
//!
|
||||
//! But it *is* still sound to:
|
||||
//!
|
||||
//! * Create a pointer without provenance from just an address (see [`ptr::dangling`]). Such a
|
||||
//! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be
|
||||
//! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be
|
||||
//! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for
|
||||
//! fun" as long as you don't use operations on it which require it to be valid (non-zero-sized
|
||||
//! offset, read, write, etc).
|
||||
//!
|
||||
//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
|
||||
//! i.e. the usual "ZSTs are fake, do what you want" rules apply.
|
||||
//!
|
||||
//! * [`wrapping_offset`] a pointer outside its provenance. This includes pointers
|
||||
//! which have "no" provenance. In particular, this makes it sound to do pointer tagging tricks.
|
||||
//!
|
||||
//! * Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses
|
||||
//! *are* just integers, so there is always a coherent answer, even if the pointers are dangling
|
||||
//! or from different provenances. Note that if you get "lucky" and notice that a pointer at the
|
||||
//! end of one allocated object is the "same" address as the start of another allocated object,
|
||||
//! anything you do with that fact is *probably* going to be gibberish. The scope of that
|
||||
//! gibberish is kept under control by the fact that the two pointers *still* aren't allowed to
|
||||
//! access the other's allocation (bytes), because they still have different provenance.
|
||||
//!
|
||||
//! Note that the full definition of provenance in Rust is not decided yet, as this interacts
|
||||
//! with the as-yet undecided [aliasing] rules.
|
||||
//!
|
||||
//! ## Pointers Vs Integers
|
||||
//!
|
||||
//! From this discussion, it becomes very clear that a `usize` *cannot* accurately represent a pointer,
|
||||
//! and converting from a pointer to a `usize` is generally an operation which *only* extracts the
|
||||
//! address. Converting this address back into pointer requires somehow answering the question:
|
||||
//! which provenance should the resulting pointer have?
|
||||
//!
|
||||
//! Rust provides two ways of dealing with this situation: *Strict Provenance* and *Exposed Provenance*.
|
||||
//!
|
||||
//! Note that a pointer *can* represent a `usize` (via [`without_provenance`]), so the right type to
|
||||
//! use in situations where a value is "sometimes a pointer and sometimes a bare `usize`" is a
|
||||
//! pointer type.
|
||||
//!
|
||||
//! ## Strict Provenance
|
||||
//!
|
||||
//! "Strict Provenance" refers to a set of APIs designed to make working with provenance more
|
||||
//! explicit. They are intended as substitutes for casting a pointer to an integer and back.
|
||||
//!
|
||||
//! Entirely avoiding integer-to-pointer casts successfully side-steps the inherent ambiguity of
|
||||
//! that operation. This benefits compiler optimizations, and it is pretty much a requirement for
|
||||
//! using tools like [Miri] and architectures like [CHERI] that aim to detect and diagnose pointer
|
||||
//! misuse.
|
||||
//!
|
||||
//! The key insight to making programming without integer-to-pointer casts *at all* viable is the
|
||||
//! [`with_addr`] method:
|
||||
//!
|
||||
//! ```text
|
||||
//! /// Creates a new pointer with the given address.
|
||||
//! ///
|
||||
//! /// This performs the same operation as an `addr as ptr` cast, but copies
|
||||
//! /// the *address-space* and *provenance* of `self` to the new pointer.
|
||||
//! /// the *provenance* of `self` to the new pointer.
|
||||
//! /// This allows us to dynamically preserve and propagate this important
|
||||
//! /// information in a way that is otherwise impossible with a unary cast.
|
||||
//! ///
|
||||
@ -257,23 +265,21 @@
|
||||
//!
|
||||
//! So you're still able to drop down to the address representation and do whatever
|
||||
//! clever bit tricks you want *as long as* you're able to keep around a pointer
|
||||
//! into the allocation you care about that can "reconstitute" the other parts of the pointer.
|
||||
//! into the allocation you care about that can "reconstitute" the provenance.
|
||||
//! Usually this is very easy, because you only are taking a pointer, messing with the address,
|
||||
//! and then immediately converting back to a pointer. To make this use case more ergonomic,
|
||||
//! we provide the [`map_addr`][] method.
|
||||
//! we provide the [`map_addr`] method.
|
||||
//!
|
||||
//! To help make it clear that code is "following" Strict Provenance semantics, we also provide an
|
||||
//! [`addr`][] method which promises that the returned address is not part of a
|
||||
//! pointer-usize-pointer roundtrip. In the future we may provide a lint for pointer<->integer
|
||||
//! [`addr`] method which promises that the returned address is not part of a
|
||||
//! pointer-integer-pointer roundtrip. In the future we may provide a lint for pointer<->integer
|
||||
//! casts to help you audit if your code conforms to strict provenance.
|
||||
//!
|
||||
//!
|
||||
//! ## Using Strict Provenance
|
||||
//! ### Using Strict Provenance
|
||||
//!
|
||||
//! Most code needs no changes to conform to strict provenance, as the only really concerning
|
||||
//! operation that *wasn't* obviously already Undefined Behaviour is casts from usize to a
|
||||
//! pointer. For code which *does* cast a `usize` to a pointer, the scope of the change depends
|
||||
//! on exactly what you're doing.
|
||||
//! operation is casts from usize to a pointer. For code which *does* cast a `usize` to a pointer,
|
||||
//! the scope of the change depends on exactly what you're doing.
|
||||
//!
|
||||
//! In general, you just need to make sure that if you want to convert a `usize` address to a
|
||||
//! pointer and then use that pointer to read/write memory, you need to keep around a pointer
|
||||
@ -284,8 +290,6 @@
|
||||
//! represent the tagged pointer as an actual pointer and not a `usize`*. For instance:
|
||||
//!
|
||||
//! ```
|
||||
//! #![feature(strict_provenance)]
|
||||
//!
|
||||
//! unsafe {
|
||||
//! // A flag we want to pack into our pointer
|
||||
//! static HAS_DATA: usize = 0x1;
|
||||
@ -314,122 +318,65 @@
|
||||
//! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers,
|
||||
//! we would like to know why, and what needs to be done to fix it.)
|
||||
//!
|
||||
//! Something more complicated and just generally *evil* like an XOR-List requires more significant
|
||||
//! changes like allocating all nodes in a pre-allocated Vec or Arena and using a pointer
|
||||
//! to the whole allocation to reconstitute the XORed addresses.
|
||||
//!
|
||||
//! Situations where a valid pointer *must* be created from just an address, such as baremetal code
|
||||
//! accessing a memory-mapped interface at a fixed address, are an open question on how to support.
|
||||
//! These situations *will* still be allowed, but we might require some kind of "I know what I'm
|
||||
//! doing" annotation to explain the situation to the compiler. It's also possible they need no
|
||||
//! special attention at all, because they're generally accessing memory outside the scope of
|
||||
//! "the abstract machine", or already using "I know what I'm doing" annotations like "volatile".
|
||||
//!
|
||||
//! Under [Strict Provenance] it is Undefined Behaviour to:
|
||||
//!
|
||||
//! * Access memory through a pointer that does not have provenance over that memory.
|
||||
//!
|
||||
//! * [`offset`] a pointer to or from an address it doesn't have provenance over.
|
||||
//! This means it's always UB to offset a pointer derived from something deallocated,
|
||||
//! even if the offset is 0. Note that a pointer "one past the end" of its provenance
|
||||
//! is not actually outside its provenance, it just has 0 bytes it can load/store.
|
||||
//!
|
||||
//! But it *is* still sound to:
|
||||
//!
|
||||
//! * Create a pointer without provenance from just an address (see [`ptr::dangling`][]). Such a
|
||||
//! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be
|
||||
//! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be
|
||||
//! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for
|
||||
//! fun" as long as you don't use operations on it which require it to be valid (non-zero-sized
|
||||
//! offset, read, write, etc).
|
||||
//!
|
||||
//! * Forge an allocation of size zero at any sufficiently aligned non-null address.
|
||||
//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
|
||||
//! for actual forgery (integers cast to pointers). If you borrow some struct's field
|
||||
//! that *happens* to be zero-sized, the resulting pointer will have provenance tied to
|
||||
//! that allocation, and it will still get invalidated if the allocation gets deallocated.
|
||||
//! In the future we may introduce an API to make such a forged allocation explicit.
|
||||
//!
|
||||
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers
|
||||
//! which have "no" provenance. Unfortunately there may be practical limits on this for a
|
||||
//! particular platform, and it's an open question as to how to specify this (if at all).
|
||||
//! Notably, [CHERI][] relies on a compression scheme that can't handle a
|
||||
//! pointer getting offset "too far" out of bounds. If this happens, the address
|
||||
//! returned by `addr` will be the value you expect, but the provenance will get invalidated
|
||||
//! and using it to read/write will fault. The details of this are architecture-specific
|
||||
//! and based on alignment, but the buffer on either side of the pointer's range is pretty
|
||||
//! generous (think kilobytes, not bytes).
|
||||
//!
|
||||
//! * Compare arbitrary pointers by address. Addresses *are* just integers and so there is
|
||||
//! always a coherent answer, even if the pointers are dangling or from different
|
||||
//! address-spaces/provenances. Of course, comparing addresses from different address-spaces
|
||||
//! is generally going to be *meaningless*, but so is comparing Kilograms to Meters, and Rust
|
||||
//! doesn't prevent that either. Similarly, if you get "lucky" and notice that a pointer
|
||||
//! one-past-the-end is the "same" address as the start of an unrelated allocation, anything
|
||||
//! you do with that fact is *probably* going to be gibberish. The scope of that gibberish
|
||||
//! is kept under control by the fact that the two pointers *still* aren't allowed to access
|
||||
//! the other's allocation (bytes), because they still have different provenance.
|
||||
//!
|
||||
//! * Perform pointer tagging tricks. This falls out of [`wrapping_offset`] but is worth
|
||||
//! mentioning in more detail because of the limitations of [CHERI][]. Low-bit tagging
|
||||
//! is very robust, and often doesn't even go out of bounds because types ensure
|
||||
//! size >= align (and over-aligning actually gives CHERI more flexibility). Anything
|
||||
//! more complex than this rapidly enters "extremely platform-specific" territory as
|
||||
//! certain things may or may not be allowed based on specific supported operations.
|
||||
//! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits
|
||||
//! that and should support it.
|
||||
//! accessing a memory-mapped interface at a fixed address, cannot currently be handled with strict
|
||||
//! provenance APIs and should use [exposed provenance](#exposed-provenance).
|
||||
//!
|
||||
//! ## Exposed Provenance
|
||||
//!
|
||||
//! **This section is *non-normative* and is an extension to the [Strict Provenance] experiment.**
|
||||
//!
|
||||
//! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance].
|
||||
//! As discussed above, integer-to-pointer casts are not possible with Strict Provenance APIs.
|
||||
//! This is by design: the goal of Strict Provenance is to provide a clear specification that we are
|
||||
//! confident can be formalized unambiguously and can be subject to precise formal reasoning.
|
||||
//! confident can be formalized unambiguously and can be subject to precise formal reasoning.
|
||||
//! Integer-to-pointer casts do not (currently) have such a clear specification.
|
||||
//!
|
||||
//! However, there exist situations where pointer-usize-pointer roundtrips cannot be avoided, or
|
||||
//! However, there exist situations where integer-to-pointer casts cannot be avoided, or
|
||||
//! where avoiding them would require major refactoring. Legacy platform APIs also regularly assume
|
||||
//! that `usize` can capture all the information that makes up a pointer. The goal of Strict
|
||||
//! Provenance is not to rule out such code; the goal is to put all the *other* pointer-manipulating
|
||||
//! code onto a more solid foundation. Strict Provenance is about improving the situation where
|
||||
//! possible (all the code that can be written with Strict Provenance) without making things worse
|
||||
//! for situations where Strict Provenance is insufficient.
|
||||
//! that `usize` can capture all the information that makes up a pointer.
|
||||
//! Bare-metal platforms can also require the synthesis of a pointer "out of thin air" without
|
||||
//! anywhere to obtain proper provenance from.
|
||||
//!
|
||||
//! For these situations, there is a highly experimental extension to Strict Provenance called
|
||||
//! *Exposed Provenance*. This extension permits pointer-usize-pointer roundtrips. However, its
|
||||
//! semantics are on much less solid footing than Strict Provenance, and at this point it is not yet
|
||||
//! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance.
|
||||
//! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI].
|
||||
//! Rust's model for dealing with integer-to-pointer casts is called *Exposed Provenance*. However,
|
||||
//! the semantics of Exposed Provenance are on much less solid footing than Strict Provenance, and
|
||||
//! at this point it is not yet clear whether a satisfying unambiguous semantics can be defined for
|
||||
//! Exposed Provenance. (If that sounds bad, be reassured that other popular languages that provide
|
||||
//! integer-to-pointer casts are not faring any better.) Furthermore, Exposed Provenance will not
|
||||
//! work (well) with tools like [Miri] and [CHERI].
|
||||
//!
|
||||
//! Exposed Provenance is provided by the [`expose_provenance`] and [`with_exposed_provenance`] methods,
|
||||
//! which are meant to replace `as` casts between pointers and integers. [`expose_provenance`] is a lot like
|
||||
//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed'
|
||||
//! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but
|
||||
//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`]
|
||||
//! can be used to construct a pointer with one of these previously 'exposed' provenances.
|
||||
//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is
|
||||
//! no indication of what the correct provenance for the returned pointer is -- and that is exactly
|
||||
//! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no
|
||||
//! algorithm that decides which provenance will be used. You can think of this as "guessing" the
|
||||
//! right provenance, and the guess will be "maximally in your favor", in the sense that if there is
|
||||
//! any way to avoid undefined behavior, then that is the guess that will be taken. However, if
|
||||
//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will
|
||||
//! be used, the program has undefined behavior.
|
||||
//! which are equivalent to `as` casts between pointers and integers.
|
||||
//! - [`expose_provenance`] is a lot like [`addr`], but additionally adds the provenance of the
|
||||
//! pointer to a global list of 'exposed' provenances. (This list is purely conceptual, it exists
|
||||
//! for the purpose of specifying Rust but is not materialized in actual executions, except in
|
||||
//! tools like [Miri].)
|
||||
//! Memory which is outside the control of the Rust abstract machine (MMIO registers, for example)
|
||||
//! is always considered to be exposed, so long as this memory is disjoint from memory that will
|
||||
//! be used by the abstract machine such as the stack, heap, and statics.
|
||||
//! - [`with_exposed_provenance`] can be used to construct a pointer with one of these previously
|
||||
//! 'exposed' provenances. [`with_exposed_provenance`] takes only `addr: usize` as arguments, so
|
||||
//! unlike in [`with_addr`] there is no indication of what the correct provenance for the returned
|
||||
//! pointer is -- and that is exactly what makes integer-to-pointer casts so tricky to rigorously
|
||||
//! specify! The compiler will do its best to pick the right provenance for you, but currently we
|
||||
//! cannot provide any guarantees about which provenance the resulting pointer will have. Only one
|
||||
//! thing is clear: if there is *no* previously 'exposed' provenance that justifies the way the
|
||||
//! returned pointer will be used, the program has undefined behavior.
|
||||
//!
|
||||
//! Using [`expose_provenance`] or [`with_exposed_provenance`] (or the `as` casts) means that code is
|
||||
//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to
|
||||
//! determine how far one can get in Rust without the use of [`expose_provenance`] and
|
||||
//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only.
|
||||
//! Maximizing the amount of such code is a major win for avoiding specification complexity and to
|
||||
//! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the
|
||||
//! confidence in (unsafe) Rust code.
|
||||
//! If at all possible, we encourage code to be ported to [Strict Provenance] APIs, thus avoiding
|
||||
//! the need for Exposed Provenance. Maximizing the amount of such code is a major win for avoiding
|
||||
//! specification complexity and to facilitate adoption of tools like [CHERI] and [Miri] that can be
|
||||
//! a big help in increasing the confidence in (unsafe) Rust code. However, we acknowledge that this
|
||||
//! is not always possible, and offer Exposed Provenance as a way to explicit "opt out" of the
|
||||
//! well-defined semantics of Strict Provenance, and "opt in" to the unclear semantics of
|
||||
//! integer-to-pointer casts.
|
||||
//!
|
||||
//! [aliasing]: ../../nomicon/aliasing.html
|
||||
//! [allocated object]: #allocated-object
|
||||
//! [provenance]: #provenance
|
||||
//! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
|
||||
//! [ub]: ../../reference/behavior-considered-undefined.html
|
||||
//! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts
|
||||
//! [atomic operations]: crate::sync::atomic
|
||||
//! [`offset`]: pointer::offset
|
||||
//! [`offset_from`]: pointer::offset_from
|
||||
//! [`wrapping_offset`]: pointer::wrapping_offset
|
||||
//! [`with_addr`]: pointer::with_addr
|
||||
//! [`map_addr`]: pointer::map_addr
|
||||
@ -439,8 +386,8 @@
|
||||
//! [`with_exposed_provenance`]: with_exposed_provenance
|
||||
//! [Miri]: https://github.com/rust-lang/miri
|
||||
//! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
|
||||
//! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228
|
||||
//! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/
|
||||
//! [Strict Provenance]: #strict-provenance
|
||||
//! [`UnsafeCell`]: core::cell::UnsafeCell
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
// There are many unsafe functions taking pointers that don't dereference them.
|
||||
@ -629,7 +576,7 @@ pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
|
||||
from_raw_parts_mut(without_provenance_mut::<()>(0), ())
|
||||
}
|
||||
|
||||
/// Creates a pointer with the given address and no provenance.
|
||||
/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance].
|
||||
///
|
||||
/// This is equivalent to `ptr::null().with_addr(addr)`.
|
||||
///
|
||||
@ -641,16 +588,15 @@ pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
|
||||
/// This is different from `addr as *const T`, which creates a pointer that picks up a previously
|
||||
/// exposed provenance. See [`with_exposed_provenance`] for more details on that operation.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn without_provenance<T>(addr: usize) -> *const T {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
// We use transmute rather than a cast so tools like Miri can tell that this
|
||||
// is *not* the same as with_exposed_provenance.
|
||||
// An int-to-pointer transmute currently has exactly the intended semantics: it creates a
|
||||
// pointer without provenance. Note that this is *not* a stable guarantee about transmute
|
||||
// semantics, it relies on sysroot crates having special status.
|
||||
// SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
|
||||
// pointer).
|
||||
unsafe { mem::transmute(addr) }
|
||||
@ -668,12 +614,12 @@ pub const fn without_provenance<T>(addr: usize) -> *const T {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn dangling<T>() -> *const T {
|
||||
without_provenance(mem::align_of::<T>())
|
||||
}
|
||||
|
||||
/// Creates a pointer with the given address and no provenance.
|
||||
/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance].
|
||||
///
|
||||
/// This is equivalent to `ptr::null_mut().with_addr(addr)`.
|
||||
///
|
||||
@ -685,16 +631,15 @@ pub const fn dangling<T>() -> *const T {
|
||||
/// This is different from `addr as *mut T`, which creates a pointer that picks up a previously
|
||||
/// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
// We use transmute rather than a cast so tools like Miri can tell that this
|
||||
// is *not* the same as with_exposed_provenance.
|
||||
// An int-to-pointer transmute currently has exactly the intended semantics: it creates a
|
||||
// pointer without provenance. Note that this is *not* a stable guarantee about transmute
|
||||
// semantics, it relies on sysroot crates having special status.
|
||||
// SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
|
||||
// pointer).
|
||||
unsafe { mem::transmute(addr) }
|
||||
@ -712,96 +657,88 @@ pub const fn without_provenance_mut<T>(addr: usize) -> *mut T {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const fn dangling_mut<T>() -> *mut T {
|
||||
without_provenance_mut(mem::align_of::<T>())
|
||||
}
|
||||
|
||||
/// Converts an address back to a pointer, picking up a previously 'exposed' provenance.
|
||||
/// Converts an address back to a pointer, picking up some previously 'exposed'
|
||||
/// [provenance][crate::ptr#provenance].
|
||||
///
|
||||
/// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the
|
||||
/// returned pointer is that of *any* pointer that was previously exposed by passing it to
|
||||
/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory which is
|
||||
/// outside the control of the Rust abstract machine (MMIO registers, for example) is always
|
||||
/// considered to be exposed, so long as this memory is disjoint from memory that will be used by
|
||||
/// the abstract machine such as the stack, heap, and statics.
|
||||
/// This is fully equivalent to `addr as *const T`. The provenance of the returned pointer is that
|
||||
/// of *some* pointer that was previously exposed by passing it to
|
||||
/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory
|
||||
/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is
|
||||
/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint
|
||||
/// from memory that will be used by the abstract machine such as the stack, heap, and statics.
|
||||
///
|
||||
/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
|
||||
/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
|
||||
/// and references that have been invalidated due to aliasing accesses cannot be used anymore,
|
||||
/// even if they have been exposed!
|
||||
/// The exact provenance that gets picked is not specified. The compiler will do its best to pick
|
||||
/// the "right" provenance for you (whatever that may be), but currently we cannot provide any
|
||||
/// guarantees about which provenance the resulting pointer will have -- and therefore there
|
||||
/// is no definite specification for which memory the resulting pointer may access.
|
||||
///
|
||||
/// Note that there is no algorithm that decides which provenance will be used. You can think of this
|
||||
/// as "guessing" the right provenance, and the guess will be "maximally in your favor", in the sense
|
||||
/// that if there is any way to avoid undefined behavior (while upholding all aliasing requirements),
|
||||
/// then that is the guess that will be taken.
|
||||
/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer
|
||||
/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply:
|
||||
/// pointers and references that have been invalidated due to aliasing accesses cannot be used
|
||||
/// anymore, even if they have been exposed!
|
||||
///
|
||||
/// On platforms with multiple address spaces, it is your responsibility to ensure that the
|
||||
/// address makes sense in the address space that this pointer will be used with.
|
||||
///
|
||||
/// Using this function means that code is *not* following [Strict
|
||||
/// Provenance][self#strict-provenance] rules. "Guessing" a
|
||||
/// suitable provenance complicates specification and reasoning and may not be supported by
|
||||
/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
|
||||
/// use [`with_addr`][pointer::with_addr] wherever possible.
|
||||
/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to
|
||||
/// stay conformant with the Rust memory model. It is recommended to use [Strict
|
||||
/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever
|
||||
/// possible.
|
||||
///
|
||||
/// On most platforms this will produce a value with the same bytes as the address. Platforms
|
||||
/// which need to store additional information in a pointer may not support this operation,
|
||||
/// since it is generally not possible to actually *compute* which provenance the returned
|
||||
/// pointer has to pick up.
|
||||
///
|
||||
/// It is unclear whether this function can be given a satisfying unambiguous specification. This
|
||||
/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance].
|
||||
/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "exposed_provenance", issue = "95228")]
|
||||
#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
|
||||
pub fn with_exposed_provenance<T>(addr: usize) -> *const T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
pub fn with_exposed_provenance<T>(addr: usize) -> *const T {
|
||||
addr as *const T
|
||||
}
|
||||
|
||||
/// Converts an address back to a mutable pointer, picking up a previously 'exposed' provenance.
|
||||
/// Converts an address back to a mutable pointer, picking up some previously 'exposed'
|
||||
/// [provenance][crate::ptr#provenance].
|
||||
///
|
||||
/// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the
|
||||
/// returned pointer is that of *any* pointer that was previously passed to
|
||||
/// [`expose_provenance`][pointer::expose_provenance] or a `ptr as usize` cast. If there is no previously
|
||||
/// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined
|
||||
/// behavior. Note that there is no algorithm that decides which provenance will be used. You can
|
||||
/// think of this as "guessing" the right provenance, and the guess will be "maximally in your
|
||||
/// favor", in the sense that if there is any way to avoid undefined behavior, then that is the
|
||||
/// guess that will be taken.
|
||||
/// This is fully equivalent to `addr as *mut T`. The provenance of the returned pointer is that
|
||||
/// of *some* pointer that was previously exposed by passing it to
|
||||
/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory
|
||||
/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is
|
||||
/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint
|
||||
/// from memory that will be used by the abstract machine such as the stack, heap, and statics.
|
||||
///
|
||||
/// On platforms with multiple address spaces, it is your responsibility to ensure that the
|
||||
/// address makes sense in the address space that this pointer will be used with.
|
||||
/// The exact provenance that gets picked is not specified. The compiler will do its best to pick
|
||||
/// the "right" provenance for you (whatever that may be), but currently we cannot provide any
|
||||
/// guarantees about which provenance the resulting pointer will have -- and therefore there
|
||||
/// is no definite specification for which memory the resulting pointer may access.
|
||||
///
|
||||
/// Using this function means that code is *not* following [Strict
|
||||
/// Provenance][self#strict-provenance] rules. "Guessing" a
|
||||
/// suitable provenance complicates specification and reasoning and may not be supported by
|
||||
/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
|
||||
/// use [`with_addr`][pointer::with_addr] wherever possible.
|
||||
/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer
|
||||
/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply:
|
||||
/// pointers and references that have been invalidated due to aliasing accesses cannot be used
|
||||
/// anymore, even if they have been exposed!
|
||||
///
|
||||
/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to
|
||||
/// stay conformant with the Rust memory model. It is recommended to use [Strict
|
||||
/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever
|
||||
/// possible.
|
||||
///
|
||||
/// On most platforms this will produce a value with the same bytes as the address. Platforms
|
||||
/// which need to store additional information in a pointer may not support this operation,
|
||||
/// since it is generally not possible to actually *compute* which provenance the returned
|
||||
/// pointer has to pick up.
|
||||
///
|
||||
/// It is unclear whether this function can be given a satisfying unambiguous specification. This
|
||||
/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance].
|
||||
/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "exposed_provenance", issue = "95228")]
|
||||
#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
|
||||
pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
|
||||
addr as *mut T
|
||||
}
|
||||
|
||||
|
@ -124,12 +124,12 @@ impl<T: ?Sized> *mut T {
|
||||
|
||||
/// Gets the "address" portion of the pointer.
|
||||
///
|
||||
/// This is similar to `self as usize`, which semantically discards *provenance* and
|
||||
/// *address-space* information. However, unlike `self as usize`, casting the returned address
|
||||
/// back to a pointer yields a [pointer without provenance][without_provenance_mut], which is undefined
|
||||
/// behavior to dereference. To properly restore the lost information and obtain a
|
||||
/// dereferenceable pointer, use [`with_addr`][pointer::with_addr] or
|
||||
/// [`map_addr`][pointer::map_addr].
|
||||
/// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of
|
||||
/// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that
|
||||
/// casting the returned address back to a pointer yields a [pointer without
|
||||
/// provenance][without_provenance_mut], which is undefined behavior to dereference. To properly
|
||||
/// restore the lost information and obtain a dereferenceable pointer, use
|
||||
/// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
|
||||
///
|
||||
/// If using those APIs is not possible because there is no way to preserve a pointer with the
|
||||
/// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts
|
||||
@ -143,89 +143,80 @@ impl<T: ?Sized> *mut T {
|
||||
/// perform a change of representation to produce a value containing only the address
|
||||
/// portion of the pointer. What that means is up to the platform to define.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
|
||||
/// might change in the future (including possibly weakening this so it becomes wholly
|
||||
/// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn addr(self) -> usize {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
// A pointer-to-integer transmute currently has exactly the right semantics: it returns the
|
||||
// address without exposing the provenance. Note that this is *not* a stable guarantee about
|
||||
// transmute semantics, it relies on sysroot crates having special status.
|
||||
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
|
||||
// provenance).
|
||||
unsafe { mem::transmute(self.cast::<()>()) }
|
||||
}
|
||||
|
||||
/// Exposes the "provenance" part of the pointer for future use in
|
||||
/// [`with_exposed_provenance`][] and returns the "address" portion.
|
||||
/// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in
|
||||
/// [`with_exposed_provenance_mut`] and returns the "address" portion.
|
||||
///
|
||||
/// This is equivalent to `self as usize`, which semantically discards *provenance* and
|
||||
/// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
|
||||
/// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
|
||||
/// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its
|
||||
/// provenance. (Reconstructing address space information, if required, is your responsibility.)
|
||||
/// This is equivalent to `self as usize`, which semantically discards provenance information.
|
||||
/// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the
|
||||
/// provenance as 'exposed', so on platforms that support it you can later call
|
||||
/// [`with_exposed_provenance_mut`] to reconstitute the original pointer including its provenance.
|
||||
///
|
||||
/// Using this method means that code is *not* following [Strict
|
||||
/// Provenance][super#strict-provenance] rules. Supporting
|
||||
/// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported
|
||||
/// by tools that help you to stay conformant with the Rust memory model, so it is recommended
|
||||
/// to use [`addr`][pointer::addr] wherever possible.
|
||||
/// Due to its inherent ambiguity, [`with_exposed_provenance_mut`] may not be supported by tools
|
||||
/// that help you to stay conformant with the Rust memory model. It is recommended to use
|
||||
/// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr]
|
||||
/// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`.
|
||||
///
|
||||
/// On most platforms this will produce a value with the same bytes as the original pointer,
|
||||
/// because all the bytes are dedicated to describing the address. Platforms which need to store
|
||||
/// additional information in the pointer may not support this operation, since the 'expose'
|
||||
/// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not
|
||||
/// side-effect which is required for [`with_exposed_provenance_mut`] to work is typically not
|
||||
/// available.
|
||||
///
|
||||
/// It is unclear whether this method can be given a satisfying unambiguous specification. This
|
||||
/// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance].
|
||||
/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API.
|
||||
///
|
||||
/// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "exposed_provenance", issue = "95228")]
|
||||
#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn expose_provenance(self) -> usize {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
self.cast::<()>() as usize
|
||||
}
|
||||
|
||||
/// Creates a new pointer with the given address.
|
||||
/// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
|
||||
/// `self`.
|
||||
///
|
||||
/// This performs the same operation as an `addr as ptr` cast, but copies
|
||||
/// the *address-space* and *provenance* of `self` to the new pointer.
|
||||
/// This allows us to dynamically preserve and propagate this important
|
||||
/// information in a way that is otherwise impossible with a unary cast.
|
||||
/// This is similar to a `addr as *mut T` cast, but copies
|
||||
/// the *provenance* of `self` to the new pointer.
|
||||
/// This avoids the inherent ambiguity of the unary cast.
|
||||
///
|
||||
/// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset
|
||||
/// `self` to the given address, and therefore has all the same capabilities and restrictions.
|
||||
///
|
||||
/// This API and its claimed semantics are an extension to the Strict Provenance experiment,
|
||||
/// see the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn with_addr(self, addr: usize) -> Self {
|
||||
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
|
||||
//
|
||||
// In the mean-time, this operation is defined to be "as if" it was
|
||||
// a wrapping_offset, so we can emulate it as such. This should properly
|
||||
// restore pointer provenance even under today's compiler.
|
||||
// This should probably be an intrinsic to avoid doing any sort of arithmetic, but
|
||||
// meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's
|
||||
// provenance.
|
||||
let self_addr = self.addr() as isize;
|
||||
let dest_addr = addr as isize;
|
||||
let offset = dest_addr.wrapping_sub(self_addr);
|
||||
|
||||
// This is the canonical desugaring of this operation
|
||||
self.wrapping_byte_offset(offset)
|
||||
}
|
||||
|
||||
/// Creates a new pointer by mapping `self`'s address to a new one.
|
||||
/// Creates a new pointer by mapping `self`'s address to a new one, preserving the original
|
||||
/// pointer's [provenance][crate::ptr#provenance].
|
||||
///
|
||||
/// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details.
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [module documentation][crate::ptr] for details.
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
|
||||
self.with_addr(f(self.addr()))
|
||||
}
|
||||
@ -376,7 +367,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
|
||||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
@ -558,7 +549,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ptr_mask, strict_provenance)]
|
||||
/// #![feature(ptr_mask)]
|
||||
/// let mut v = 17_u32;
|
||||
/// let ptr: *mut u32 = &mut v;
|
||||
///
|
||||
@ -777,7 +768,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// * `self` and `origin` must either
|
||||
///
|
||||
/// * point to the same address, or
|
||||
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
|
||||
/// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between
|
||||
/// the two pointers must be in bounds of that object. (See below for an example.)
|
||||
///
|
||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||
@ -954,7 +945,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
|
||||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
@ -1061,7 +1052,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// * The offset in bytes, `count * size_of::<T>()`, computed on mathematical integers (without
|
||||
/// "wrapping around"), must fit in an `isize`.
|
||||
///
|
||||
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||
/// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some
|
||||
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||
/// of the address space.
|
||||
|
@ -283,40 +283,39 @@ impl<T: ?Sized> NonNull<T> {
|
||||
///
|
||||
/// For more details see the equivalent method on a raw pointer, [`pointer::addr`].
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [`ptr` module documentation][crate::ptr].
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn addr(self) -> NonZero<usize> {
|
||||
// SAFETY: The pointer is guaranteed by the type to be non-null,
|
||||
// meaning that the address will be non-zero.
|
||||
unsafe { NonZero::new_unchecked(self.pointer.addr()) }
|
||||
}
|
||||
|
||||
/// Creates a new pointer with the given address.
|
||||
/// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of
|
||||
/// `self`.
|
||||
///
|
||||
/// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`].
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [`ptr` module documentation][crate::ptr].
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn with_addr(self, addr: NonZero<usize>) -> Self {
|
||||
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
|
||||
unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
|
||||
}
|
||||
|
||||
/// Creates a new pointer by mapping `self`'s address to a new one.
|
||||
/// Creates a new pointer by mapping `self`'s address to a new one, preserving the
|
||||
/// [provenance][crate::ptr#provenance] of `self`.
|
||||
///
|
||||
/// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`].
|
||||
///
|
||||
/// This API and its claimed semantics are part of the Strict Provenance experiment,
|
||||
/// see the [`ptr` module documentation][crate::ptr].
|
||||
/// This is a [Strict Provenance][crate::ptr#strict-provenance] API.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "strict_provenance", issue = "95228")]
|
||||
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn map_addr(self, f: impl FnOnce(NonZero<usize>) -> NonZero<usize>) -> Self {
|
||||
self.with_addr(f(self.addr()))
|
||||
}
|
||||
@ -749,7 +748,6 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// *Incorrect* usage:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// #![feature(strict_provenance)]
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// let ptr1 = NonNull::new(Box::into_raw(Box::new(0u8))).unwrap();
|
||||
|
@ -1758,7 +1758,7 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
|
||||
/// #![feature(strict_provenance_atomic_ptr)]
|
||||
/// use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let atom = AtomicPtr::<i64>::new(core::ptr::null_mut());
|
||||
@ -1838,7 +1838,7 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
|
||||
/// #![feature(strict_provenance_atomic_ptr)]
|
||||
/// use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let atom = AtomicPtr::<i64>::new(core::ptr::null_mut());
|
||||
@ -1874,7 +1874,7 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
|
||||
/// #![feature(strict_provenance_atomic_ptr)]
|
||||
/// use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let atom = AtomicPtr::<i64>::new(core::ptr::without_provenance_mut(1));
|
||||
@ -1919,7 +1919,7 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
|
||||
/// #![feature(strict_provenance_atomic_ptr)]
|
||||
/// use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let pointer = &mut 3i64 as *mut i64;
|
||||
@ -1970,7 +1970,7 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
|
||||
/// #![feature(strict_provenance_atomic_ptr)]
|
||||
/// use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let pointer = &mut 3i64 as *mut i64;
|
||||
@ -2020,7 +2020,7 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(strict_provenance_atomic_ptr, strict_provenance)]
|
||||
/// #![feature(strict_provenance_atomic_ptr)]
|
||||
/// use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
///
|
||||
/// let pointer = &mut 3i64 as *mut i64;
|
||||
|
@ -1,4 +1,6 @@
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(strict_provenance))]
|
||||
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
|
||||
#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
|
||||
#![cfg_attr(test, feature(cfg_match))]
|
||||
#![feature(alloc_layout_extra)]
|
||||
@ -85,7 +87,6 @@
|
||||
#![feature(std_internals)]
|
||||
#![feature(step_trait)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(strict_provenance_atomic_ptr)]
|
||||
#![feature(test)]
|
||||
#![feature(trait_upcasting)]
|
||||
|
@ -19,8 +19,6 @@
|
||||
#![feature(panic_unwind)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(exposed_provenance)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![panic_runtime]
|
||||
#![feature(panic_runtime)]
|
||||
|
@ -9,7 +9,6 @@
|
||||
repr_simd,
|
||||
simd_ffi,
|
||||
staged_api,
|
||||
strict_provenance,
|
||||
prelude_import,
|
||||
ptr_metadata
|
||||
)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(portable_simd, strict_provenance, exposed_provenance)]
|
||||
#![feature(portable_simd)]
|
||||
|
||||
use core_simd::simd::{
|
||||
ptr::{SimdConstPtr, SimdMutPtr},
|
||||
|
@ -32,7 +32,6 @@
|
||||
#![feature(restricted_std)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(strict_provenance)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(internal_features)]
|
||||
#![deny(ffi_unwind_calls)]
|
||||
|
@ -279,6 +279,8 @@
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(strict_provenance))]
|
||||
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(allocator_internals)]
|
||||
#![feature(allow_internal_unsafe)]
|
||||
@ -336,7 +338,6 @@
|
||||
#![feature(error_iter)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(exclusive_wrapper)]
|
||||
#![feature(exposed_provenance)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(float_gamma)]
|
||||
#![feature(float_minimum_maximum)]
|
||||
@ -362,7 +363,6 @@
|
||||
#![feature(slice_range)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(strict_provenance_atomic_ptr)]
|
||||
#![feature(ub_checks)]
|
||||
// tidy-alphabetical-end
|
||||
|
@ -2,7 +2,6 @@
|
||||
#![unstable(feature = "panic_unwind", issue = "32837")]
|
||||
#![feature(link_cfg)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(strict_provenance)]
|
||||
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
|
||||
#![cfg_attr(
|
||||
all(target_family = "wasm", not(target_os = "emscripten")),
|
||||
|
@ -1,18 +1,17 @@
|
||||
# `strict_provenance`
|
||||
# `strict_provenance_lints`
|
||||
|
||||
The tracking issue for this feature is: [#95228]
|
||||
|
||||
[#95228]: https://github.com/rust-lang/rust/issues/95228
|
||||
-----
|
||||
|
||||
The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
|
||||
The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints.
|
||||
These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model.
|
||||
The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`).
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(strict_provenance_lints)]
|
||||
#![warn(fuzzy_provenance_casts)]
|
||||
|
||||
fn main() {
|
@ -100,6 +100,7 @@ fn main() {
|
||||
links_ignored_external: 0,
|
||||
links_ignored_exception: 0,
|
||||
intra_doc_exceptions: 0,
|
||||
has_broken_urls: false,
|
||||
};
|
||||
checker.walk(&docs, &mut report);
|
||||
report.report();
|
||||
@ -116,6 +117,8 @@ struct Checker {
|
||||
|
||||
struct Report {
|
||||
errors: u32,
|
||||
// Used to provide help message to remind the user to register a page in `SUMMARY.md`.
|
||||
has_broken_urls: bool,
|
||||
start: Instant,
|
||||
html_files: u32,
|
||||
html_redirects: u32,
|
||||
@ -274,6 +277,7 @@ impl Checker {
|
||||
report.links_ignored_exception += 1;
|
||||
} else {
|
||||
report.errors += 1;
|
||||
report.has_broken_urls = true;
|
||||
println!("{}:{}: broken link - `{}`", pretty_path, i, target_pretty_path);
|
||||
}
|
||||
return;
|
||||
@ -438,6 +442,13 @@ impl Report {
|
||||
println!("number of links ignored due to exceptions: {}", self.links_ignored_exception);
|
||||
println!("number of intra doc links ignored: {}", self.intra_doc_exceptions);
|
||||
println!("errors found: {}", self.errors);
|
||||
|
||||
if self.has_broken_urls {
|
||||
eprintln!(
|
||||
"NOTE: if you are adding or renaming a markdown file in a mdBook, don't forget to \
|
||||
register the page in SUMMARY.md"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,6 @@
|
||||
#![feature(let_chains)]
|
||||
#![feature(trait_upcasting)]
|
||||
#![feature(strict_overflow_ops)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(exposed_provenance)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(unqualified_local_imports)]
|
||||
// Configure clippy and other lints
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(strict_provenance)]
|
||||
use std::ptr;
|
||||
|
||||
fn direct_raw(x: *const (i32, i32)) -> *const i32 {
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Should be caught even without retagging
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
#![feature(strict_provenance)]
|
||||
use std::ptr::{self, addr_of_mut};
|
||||
|
||||
// Deref'ing a dangling raw pointer is fine, but for a dangling box it is not.
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Should be caught even without retagging
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
#![feature(strict_provenance)]
|
||||
use std::ptr::{self, addr_of_mut};
|
||||
|
||||
// Deref'ing a dangling raw pointer is fine, but for a dangling reference it is not.
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(strict_provenance)]
|
||||
use core::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(strict_provenance)]
|
||||
use std::mem;
|
||||
|
||||
#[repr(C, usize)]
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(strict_provenance, exposed_provenance)]
|
||||
|
||||
fn main() {
|
||||
let x: i32 = 3;
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(strict_provenance, exposed_provenance)]
|
||||
|
||||
// Ensure that a `ptr::without_provenance` ptr is truly invalid.
|
||||
fn main() {
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-strict-provenance
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
fn main() {
|
||||
let x = 22;
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-strict-provenance
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
fn main() {
|
||||
let addr = &0 as *const i32 as usize;
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
// If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail.
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
//@compile-flags: -Zmiri-symbolic-alignment-check
|
||||
//@revisions: call_unaligned_ptr read_unaligned_ptr
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
#[path = "../../utils/mod.rs"]
|
||||
mod utils;
|
||||
|
@ -1,7 +1,6 @@
|
||||
//@compile-flags: -Zmiri-disable-validation
|
||||
//@error-in-other-file: memory is uninitialized at [0x4..0x8]
|
||||
//@normalize-stderr-test: "a[0-9]+" -> "ALLOC"
|
||||
#![feature(strict_provenance)]
|
||||
#![allow(dropping_copy_types)]
|
||||
|
||||
// Test printing allocations that contain single-byte provenance.
|
||||
|
@ -3,7 +3,6 @@
|
||||
//@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
|
||||
#![feature(io_error_more)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::mem::{size_of, size_of_val};
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
//@only-target: linux
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
use std::convert::TryInto;
|
||||
|
||||
fn main() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(strict_provenance, pointer_is_aligned_to)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
fn test_memcpy() {
|
||||
|
@ -2,7 +2,6 @@
|
||||
//@compile-flags: -Zmiri-disable-isolation
|
||||
#![feature(io_error_more)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
//@ignore-target: windows # No mmap on Windows
|
||||
//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::io::Error;
|
||||
use std::{ptr, slice};
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-symbolic-alignment-check
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
//@compile-flags: -Zmiri-strict-provenance
|
||||
|
||||
#![feature(strict_provenance, strict_provenance_atomic_ptr)]
|
||||
#![feature(strict_provenance_atomic_ptr)]
|
||||
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
|
||||
#![allow(static_mut_refs)]
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
//@revisions: stack tree
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
#![feature(allocator_api)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::alloc::{AllocError, Allocator, Layout};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Regression test for <https://github.com/rust-lang/miri/issues/3450>:
|
||||
//! When the address gets reused, there should be a happens-before relation.
|
||||
//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=1.0
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
|
||||
use std::cell::SyncUnsafeCell;
|
||||
|
@ -7,7 +7,6 @@
|
||||
// MIR inlining will put every evaluation of the const we're repeatedly evaluating into the same
|
||||
// stack frame, breaking this test.
|
||||
//@compile-flags: -Zinline-mir=no
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
const EVALS: usize = 256;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(custom_mir, core_intrinsics, strict_provenance)]
|
||||
#![feature(custom_mir, core_intrinsics)]
|
||||
use std::intrinsics::mir::*;
|
||||
|
||||
// The `Drop` terminator on a type with no drop glue should be a NOP.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//@revisions: stack tree
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
#![feature(extern_types, strict_provenance)]
|
||||
#![feature(extern_types)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
//@revisions: stack tree
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
#![feature(strict_provenance)]
|
||||
use std::{mem, ptr};
|
||||
|
||||
const PTR_SIZE: usize = mem::size_of::<&i32>();
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Tree Borrows doesn't support int2ptr casts, but let's make sure we don't immediately crash either.
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
//@[stack]compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(strict_provenance, exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(strict_provenance)]
|
||||
use std::mem;
|
||||
use std::ptr::{self, addr_of};
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![feature(ptr_mask)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
fn main() {
|
||||
let v: u32 = 0xABCDABCD;
|
||||
|
@ -4,7 +4,6 @@
|
||||
#![feature(slice_as_chunks)]
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(layout_for_ptr)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::{ptr, slice};
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(exposed_provenance)]
|
||||
use std::ptr;
|
||||
|
||||
// Just to make sure that casting a ref to raw, to int and back to raw
|
||||
|
@ -2,7 +2,6 @@
|
||||
// printing, not how it interacts with the GC.
|
||||
//@compile-flags: -Zmiri-permissive-provenance -Zmiri-provenance-gc=0
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
use std::alloc::{self, Layout};
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
//@revisions: stack tree
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
#![feature(strict_provenance)]
|
||||
use std::{mem, ptr};
|
||||
|
||||
fn t1() {
|
||||
|
@ -1,5 +1,4 @@
|
||||
// Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(never_type)]
|
||||
|
||||
use std::ptr;
|
||||
|
@ -1,5 +1,4 @@
|
||||
//! Tests specific for <https://github.com/rust-lang/rust/issues/117945>: zero-sized operations.
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
//@ compile-flags: -O -Cno-prepopulate-passes
|
||||
#![crate_type = "lib"]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(strict_provenance_atomic_ptr)]
|
||||
|
||||
use std::ptr::without_provenance_mut;
|
||||
|
@ -1,7 +1,6 @@
|
||||
//@ compile-flags: -O -C debug-assertions=yes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test(src: *const u8, dst: *const u8) -> usize {
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
// Regression for <https://github.com/rust-lang/rust/issues/127089>
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
struct Foo<T>(std::marker::PhantomData<T>);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
|
@ -31,6 +31,10 @@ fn main() {
|
||||
"rustc_pattern_analysis",
|
||||
"-p",
|
||||
"rustc_lexer",
|
||||
"-p",
|
||||
"rustc_abi",
|
||||
"-p",
|
||||
"rustc_parse_format",
|
||||
])
|
||||
.run();
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
//@ compile-flags: -Copt-level=2
|
||||
//@ run-pass
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn f() -> usize {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[inline(never)]
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[inline(never)]
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
ptr,
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Derived from https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
|
||||
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -2,8 +2,6 @@
|
||||
//@ compile-flags: -Copt-level=2
|
||||
//@ run-pass
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn f() -> usize {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[inline(never)]
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[inline(never)]
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
ptr,
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
// Derived from https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
|
@ -1,24 +1,24 @@
|
||||
warning: unknown lint: `fuzzy_provenance_casts`
|
||||
--> $DIR/feature-gate-strict_provenance.rs:3:1
|
||||
--> $DIR/feature-gate-strict_provenance_lints.rs:3:1
|
||||
|
|
||||
LL | #![deny(fuzzy_provenance_casts)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `fuzzy_provenance_casts` lint is unstable
|
||||
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
|
||||
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
|
||||
= note: see issue #130351 <https://github.com/rust-lang/rust/issues/130351> for more information
|
||||
= help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
= note: `#[warn(unknown_lints)]` on by default
|
||||
|
||||
warning: unknown lint: `lossy_provenance_casts`
|
||||
--> $DIR/feature-gate-strict_provenance.rs:5:1
|
||||
--> $DIR/feature-gate-strict_provenance_lints.rs:5:1
|
||||
|
|
||||
LL | #![deny(lossy_provenance_casts)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `lossy_provenance_casts` lint is unstable
|
||||
= note: see issue #95228 <https://github.com/rust-lang/rust/issues/95228> for more information
|
||||
= help: add `#![feature(strict_provenance)]` to the crate attributes to enable
|
||||
= note: see issue #130351 <https://github.com/rust-lang/rust/issues/130351> for more information
|
||||
= help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
warning: 2 warnings emitted
|
@ -1,4 +1,4 @@
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(strict_provenance_lints)]
|
||||
#![deny(fuzzy_provenance_casts)]
|
||||
|
||||
fn main() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(strict_provenance_lints)]
|
||||
#![deny(lossy_provenance_casts)]
|
||||
|
||||
fn main() {
|
||||
|
40
tests/ui/lint/unit_bindings.deny_level.stderr
Normal file
40
tests/ui/lint/unit_bindings.deny_level.stderr
Normal file
@ -0,0 +1,40 @@
|
||||
error: binding has unit type `()`
|
||||
--> $DIR/unit_bindings.rs:50:5
|
||||
|
|
||||
LL | let _ = expr;
|
||||
| ^^^^-^^^^^^^^
|
||||
| |
|
||||
| this pattern is inferred to be the unit type `()`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/unit_bindings.rs:22:30
|
||||
|
|
||||
LL | #![cfg_attr(deny_level, deny(unit_bindings))]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: binding has unit type `()`
|
||||
--> $DIR/unit_bindings.rs:51:5
|
||||
|
|
||||
LL | let pat = expr;
|
||||
| ^^^^---^^^^^^^^
|
||||
| |
|
||||
| this pattern is inferred to be the unit type `()`
|
||||
|
||||
error: binding has unit type `()`
|
||||
--> $DIR/unit_bindings.rs:52:5
|
||||
|
|
||||
LL | let _pat = expr;
|
||||
| ^^^^----^^^^^^^^
|
||||
| |
|
||||
| this pattern is inferred to be the unit type `()`
|
||||
|
||||
error: binding has unit type `()`
|
||||
--> $DIR/unit_bindings.rs:55:5
|
||||
|
|
||||
LL | let list = v.sort();
|
||||
| ^^^^----^^^^^^^^^^^^
|
||||
| |
|
||||
| this pattern is inferred to be the unit type `()`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
60
tests/ui/lint/unit_bindings.rs
Normal file
60
tests/ui/lint/unit_bindings.rs
Normal file
@ -0,0 +1,60 @@
|
||||
//! Basic checks for `unit_bindings` lint.
|
||||
//!
|
||||
//! The `unit_bindings` lint tries to detect cases like `let list = list.sort()`. The lint will
|
||||
//! trigger on bindings that have the unit `()` type **except** if:
|
||||
//!
|
||||
//! - The user wrote `()` on either side, i.e.
|
||||
//! - `let () = <expr>;` or `let <expr> = ();`
|
||||
//! - `let _ = ();`
|
||||
//! - The binding occurs within macro expansions, e.g. `foo!();`.
|
||||
//! - The user explicitly provided type annotations, e.g. `let x: () = <expr>`.
|
||||
//!
|
||||
//! Examples where the lint *should* fire on include:
|
||||
//!
|
||||
//! - `let _ = <expr>;`
|
||||
//! - `let pat = <expr>;`
|
||||
//! - `let _pat = <expr>;`
|
||||
|
||||
//@ revisions: default_level deny_level
|
||||
//@[default_level] check-pass (`unit_bindings` is currently allow-by-default)
|
||||
|
||||
#![allow(unused)]
|
||||
#![cfg_attr(deny_level, deny(unit_bindings))]
|
||||
|
||||
// The `list` binding below should trigger the lint if it's not contained in a macro expansion.
|
||||
macro_rules! expands_to_sus {
|
||||
() => {
|
||||
let mut v = [1, 2, 3];
|
||||
let list = v.sort();
|
||||
}
|
||||
}
|
||||
|
||||
// No warning for `y` and `z` because it is provided as type parameter.
|
||||
fn ty_param_check<T: Copy>(x: T) {
|
||||
let y = x;
|
||||
let z: T = x;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// No warning if user explicitly wrote `()` on either side.
|
||||
let expr = ();
|
||||
let () = expr;
|
||||
let _ = ();
|
||||
// No warning if user explicitly annotates the unit type on the binding.
|
||||
let pat: () = expr;
|
||||
// No warning for let bindings with unit type in macro expansions.
|
||||
expands_to_sus!();
|
||||
// No warning for unit bindings in generic fns.
|
||||
ty_param_check(());
|
||||
|
||||
let _ = expr; //[deny_level]~ ERROR binding has unit type
|
||||
let pat = expr; //[deny_level]~ ERROR binding has unit type
|
||||
let _pat = expr; //[deny_level]~ ERROR binding has unit type
|
||||
|
||||
let mut v = [1, 2, 3];
|
||||
let list = v.sort(); //[deny_level]~ ERROR binding has unit type
|
||||
|
||||
// Limitation: the lint currently does not fire on nested unit LHS bindings, i.e.
|
||||
// this will not currently trigger the lint.
|
||||
let (nested, _) = (expr, 0i32);
|
||||
}
|
@ -7,8 +7,6 @@
|
||||
// that will fail on dereferencing of a pointer to u64 which is not 8-byte-aligned but is
|
||||
// 4-byte-aligned.
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
fn main() {
|
||||
let mut x = [0u64; 2];
|
||||
let ptr = x.as_mut_ptr();
|
||||
|
@ -1,8 +1,6 @@
|
||||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
#[repr(packed)]
|
||||
struct Misaligner {
|
||||
_head: u8,
|
||||
|
@ -5,7 +5,6 @@
|
||||
#![allow(dead_code)]
|
||||
#![feature(never_type)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::mem::size_of;
|
||||
use std::num::NonZero;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user