Merge from rustc
This commit is contained in:
commit
bca5e33727
162 changed files with 2920 additions and 1112 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -315,6 +315,10 @@ jobs:
|
|||
- name: x86_64-gnu-distcheck
|
||||
os: ubuntu-20.04-8core-32gb
|
||||
env: {}
|
||||
- name: x86_64-gnu-llvm-18
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
os: ubuntu-20.04-8core-32gb
|
||||
- name: x86_64-gnu-llvm-17
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
|
|
|||
38
Cargo.lock
38
Cargo.lock
|
|
@ -784,12 +784,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.0.0"
|
||||
|
|
@ -1035,10 +1029,8 @@ version = "0.99.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
|
|
@ -3498,7 +3490,7 @@ dependencies = [
|
|||
name = "rustc_ast_passes"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr",
|
||||
|
|
@ -3518,7 +3510,7 @@ dependencies = [
|
|||
name = "rustc_ast_pretty"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_ast",
|
||||
"rustc_lexer",
|
||||
"rustc_span",
|
||||
|
|
@ -3559,7 +3551,7 @@ name = "rustc_borrowck"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"polonius-engine",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -3612,7 +3604,7 @@ name = "rustc_codegen_llvm"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"libc",
|
||||
"measureme",
|
||||
"object",
|
||||
|
|
@ -3648,7 +3640,7 @@ dependencies = [
|
|||
"ar_archive_writer",
|
||||
"bitflags 2.4.2",
|
||||
"cc",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"object",
|
||||
|
|
@ -3930,7 +3922,7 @@ dependencies = [
|
|||
name = "rustc_hir_analysis"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
|
@ -3969,7 +3961,7 @@ dependencies = [
|
|||
name = "rustc_hir_typeck"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_ast",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr",
|
||||
|
|
@ -4134,7 +4126,6 @@ dependencies = [
|
|||
"rustc_target",
|
||||
"rustc_trait_selection",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
"unicode-security",
|
||||
]
|
||||
|
|
@ -4218,7 +4209,6 @@ name = "rustc_middle"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"derive_more",
|
||||
"either",
|
||||
"field-offset",
|
||||
"gsgdt",
|
||||
|
|
@ -4256,7 +4246,7 @@ name = "rustc_mir_build"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
|
|
@ -4303,7 +4293,7 @@ name = "rustc_mir_transform"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
|
@ -4383,7 +4373,6 @@ dependencies = [
|
|||
name = "rustc_passes"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr",
|
||||
|
|
@ -4573,6 +4562,7 @@ name = "rustc_span"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"md-5",
|
||||
"rustc_arena",
|
||||
"rustc_data_structures",
|
||||
|
|
@ -4633,7 +4623,7 @@ name = "rustc_trait_selection"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_ast",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr",
|
||||
|
|
@ -4673,7 +4663,7 @@ dependencies = [
|
|||
name = "rustc_transmute"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
|
|
@ -4689,7 +4679,7 @@ dependencies = [
|
|||
name = "rustc_ty_utils"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -4739,7 +4729,7 @@ dependencies = [
|
|||
"askama",
|
||||
"expect-test",
|
||||
"indexmap",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"minifier",
|
||||
"once_cell",
|
||||
"regex",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl VisitorResult for () {
|
|||
type Residual = !;
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
type Residual = core::ops::Infallible;
|
||||
type Residual = core::convert::Infallible;
|
||||
|
||||
fn output() -> Self {}
|
||||
fn from_residual(_: Self::Residual) -> Self {}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
either = "1.5.0"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
polonius-engine = "0.13.0"
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
|
|||
|
|
@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
ConstraintCategory::SizedBound,
|
||||
);
|
||||
}
|
||||
&Rvalue::NullaryOp(NullOp::DebugAssertions, _) => {}
|
||||
&Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {}
|
||||
|
||||
Rvalue::ShallowInitBox(operand, ty) => {
|
||||
self.check_operand(operand, location);
|
||||
|
|
|
|||
|
|
@ -779,7 +779,7 @@ fn codegen_stmt<'tcx>(
|
|||
NullOp::OffsetOf(fields) => {
|
||||
layout.offset_of_subfield(fx, fields.iter()).bytes()
|
||||
}
|
||||
NullOp::DebugAssertions => {
|
||||
NullOp::UbCheck(_) => {
|
||||
let val = fx.tcx.sess.opts.debug_assertions;
|
||||
let val = CValue::by_val(
|
||||
fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ test = false
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
libc = "0.2"
|
||||
measureme = "11"
|
||||
object = { version = "0.32.0", default-features = false, features = ["std", "read"] }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
ar_archive_writer = "0.1.5"
|
||||
bitflags = "2.4.1"
|
||||
cc = "1.0.90"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
jobserver = "0.1.28"
|
||||
pathdiff = "0.2.0"
|
||||
regex = "1.4"
|
||||
|
|
|
|||
|
|
@ -685,7 +685,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes();
|
||||
bx.cx().const_usize(val)
|
||||
}
|
||||
mir::NullOp::DebugAssertions => {
|
||||
mir::NullOp::UbCheck(_) => {
|
||||
// In codegen, we want to check for language UB and library UB
|
||||
let val = bx.tcx().sess.opts.debug_assertions;
|
||||
bx.cx().const_bool(val)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,10 +258,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let val = layout.offset_of_subfield(self, fields.iter()).bytes();
|
||||
Scalar::from_target_usize(val, self)
|
||||
}
|
||||
mir::NullOp::DebugAssertions => {
|
||||
// The checks hidden behind this are always better done by the interpreter
|
||||
// itself, because it knows the runtime state better.
|
||||
Scalar::from_bool(false)
|
||||
mir::NullOp::UbCheck(kind) => {
|
||||
// We want to enable checks for library UB, because the interpreter doesn't
|
||||
// know about those on its own.
|
||||
// But we want to disable checks for language UB, because the interpreter
|
||||
// has its own better checks for that.
|
||||
let should_check = match kind {
|
||||
mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions,
|
||||
mir::UbKind::LanguageUb => false,
|
||||
};
|
||||
Scalar::from_bool(should_check)
|
||||
}
|
||||
};
|
||||
self.write_scalar(val, &dest)?;
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ use rustc_middle::mir::interpret::{
|
|||
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
|
||||
ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
|
||||
};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{
|
||||
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||
|
|
@ -783,7 +783,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
fn visit_box(
|
||||
&mut self,
|
||||
_box_ty: Ty<'tcx>,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_safe_pointer(op, PointerKind::Box)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::interpret::InterpResult;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
|
||||
|
||||
|
|
@ -47,10 +47,10 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
|||
Ok(())
|
||||
}
|
||||
/// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
|
||||
/// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
|
||||
/// pointee type is the actual `T`.
|
||||
/// The type of `v` will be a raw pointer to `T`, but this is a field of `Box<T>` and the
|
||||
/// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself.
|
||||
#[inline(always)]
|
||||
fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> {
|
||||
fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
|||
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
|
||||
let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr
|
||||
// ... whose only field finally is a raw ptr we can dereference.
|
||||
self.visit_box(&raw_ptr)?;
|
||||
self.visit_box(ty, &raw_ptr)?;
|
||||
|
||||
// The second `Box` field is the allocator, which we recursively check for validity
|
||||
// like in regular structs.
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
Rvalue::Cast(_, _, _) => {}
|
||||
|
||||
Rvalue::NullaryOp(
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions,
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_),
|
||||
_,
|
||||
) => {}
|
||||
Rvalue::ShallowInitBox(_, _) => {}
|
||||
|
|
|
|||
|
|
@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
Rvalue::Repeat(_, _)
|
||||
| Rvalue::ThreadLocalRef(_)
|
||||
| Rvalue::AddressOf(_, _)
|
||||
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::DebugAssertions, _)
|
||||
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _)
|
||||
| Rvalue::Discriminant(_) => {}
|
||||
}
|
||||
self.super_rvalue(rvalue, location);
|
||||
|
|
|
|||
|
|
@ -676,6 +676,7 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy
|
|||
metadata_loader,
|
||||
&mut v,
|
||||
&sess.opts.unstable_opts.ls,
|
||||
sess.cfg_version,
|
||||
)
|
||||
.unwrap();
|
||||
safe_println!("{}", String::from_utf8(v).unwrap());
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
|||
| sym::variant_count
|
||||
| sym::is_val_statically_known
|
||||
| sym::ptr_mask
|
||||
| sym::debug_assertions
|
||||
| sym::check_language_ub
|
||||
| sym::check_library_ub
|
||||
| sym::fadd_algebraic
|
||||
| sym::fsub_algebraic
|
||||
| sym::fmul_algebraic
|
||||
|
|
@ -584,7 +585,7 @@ pub fn check_intrinsic_type(
|
|||
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
|
||||
}
|
||||
|
||||
sym::debug_assertions => (0, 1, Vec::new(), tcx.types.bool),
|
||||
sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool),
|
||||
|
||||
sym::simd_eq
|
||||
| sym::simd_ne
|
||||
|
|
@ -657,6 +658,10 @@ pub fn check_intrinsic_type(
|
|||
sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
|
||||
sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
|
||||
|
||||
sym::retag_box_to_raw => {
|
||||
(2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0)))
|
||||
}
|
||||
|
||||
other => {
|
||||
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_ir = { path = "../rustc_ast_ir" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use rustc_target::spec::abi::Abi;
|
|||
use rustc_trait_selection::traits;
|
||||
use rustc_trait_selection::traits::error_reporting::ArgKind;
|
||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||
use std::cmp;
|
||||
use rustc_type_ir::ClosureKind;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
|
|
@ -437,10 +437,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(found_kind) = found_kind {
|
||||
expected_kind = Some(
|
||||
expected_kind
|
||||
.map_or_else(|| found_kind, |current| cmp::min(current, found_kind)),
|
||||
);
|
||||
// always use the closure kind that is more permissive.
|
||||
match (expected_kind, found_kind) {
|
||||
(None, _) => expected_kind = Some(found_kind),
|
||||
(Some(ClosureKind::FnMut), ClosureKind::Fn) => {
|
||||
expected_kind = Some(ClosureKind::Fn)
|
||||
}
|
||||
(Some(ClosureKind::FnOnce), ClosureKind::Fn | ClosureKind::FnMut) => {
|
||||
expected_kind = Some(found_kind)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,10 +101,11 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
|
|||
threads: usize,
|
||||
f: F,
|
||||
) -> R {
|
||||
use rustc_data_structures::{jobserver, sync::FromDyn};
|
||||
use rustc_data_structures::{defer, jobserver, sync::FromDyn};
|
||||
use rustc_middle::ty::tls;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_query_system::query::{deadlock, QueryContext};
|
||||
use rustc_query_system::query::{break_query_cycles, QueryContext};
|
||||
use std::process;
|
||||
|
||||
let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
|
||||
|
||||
|
|
@ -128,7 +129,19 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
|
|||
let query_map =
|
||||
FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs()));
|
||||
let registry = rayon_core::Registry::current();
|
||||
thread::spawn(move || deadlock(query_map.into_inner(), ®istry));
|
||||
thread::Builder::new()
|
||||
.name("rustc query cycle handler".to_string())
|
||||
.spawn(move || {
|
||||
let on_panic = defer(|| {
|
||||
eprintln!("query cycle handler thread panicked, aborting process");
|
||||
// We need to abort here as we failed to resolve the deadlock,
|
||||
// otherwise the compiler could just hang,
|
||||
process::abort();
|
||||
});
|
||||
break_query_cycles(query_map.into_inner(), ®istry);
|
||||
on_panic.disable();
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
if let Some(size) = get_stack_size() {
|
||||
builder = builder.stack_size(size);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ rustc_span = { path = "../rustc_span" }
|
|||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
tracing = "0.1"
|
||||
unicode-security = "0.1.0"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind};
|
|||
use rustc_span::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
|
||||
use crate::{LateContext, LateLintPass, LintContext};
|
||||
|
||||
|
|
@ -85,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
|||
if let Some(def_id) = oexpn.macro_def_id
|
||||
&& let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind
|
||||
&& def_id.krate != LOCAL_CRATE
|
||||
&& std::env::var_os("CARGO").is_some()
|
||||
&& rustc_session::utils::was_invoked_from_cargo()
|
||||
{
|
||||
Some(NonLocalDefinitionsCargoUpdateNote {
|
||||
macro_kind: macro_kind.descr(),
|
||||
|
|
@ -114,25 +112,25 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
|||
// is using local items and so we don't lint on it.
|
||||
|
||||
// We also ignore anon-const in item by including the anon-const
|
||||
// parent as well; and since it's quite uncommon, we use smallvec
|
||||
// to avoid unnecessary heap allocations.
|
||||
let local_parents: SmallVec<[DefId; 1]> = if parent_def_kind == DefKind::Const
|
||||
// parent as well.
|
||||
let parent_parent = if parent_def_kind == DefKind::Const
|
||||
&& parent_opt_item_name == Some(kw::Underscore)
|
||||
{
|
||||
smallvec![parent, cx.tcx.parent(parent)]
|
||||
Some(cx.tcx.parent(parent))
|
||||
} else {
|
||||
smallvec![parent]
|
||||
None
|
||||
};
|
||||
|
||||
let self_ty_has_local_parent = match impl_.self_ty.kind {
|
||||
TyKind::Path(QPath::Resolved(_, ty_path)) => {
|
||||
path_has_local_parent(ty_path, cx, &*local_parents)
|
||||
path_has_local_parent(ty_path, cx, parent, parent_parent)
|
||||
}
|
||||
TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
|
||||
path_has_local_parent(
|
||||
principle_poly_trait_ref.trait_ref.path,
|
||||
cx,
|
||||
&*local_parents,
|
||||
parent,
|
||||
parent_parent,
|
||||
)
|
||||
}
|
||||
TyKind::TraitObject([], _, _)
|
||||
|
|
@ -154,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
|||
|
||||
let of_trait_has_local_parent = impl_
|
||||
.of_trait
|
||||
.map(|of_trait| path_has_local_parent(of_trait.path, cx, &*local_parents))
|
||||
.map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent))
|
||||
.unwrap_or(false);
|
||||
|
||||
// If none of them have a local parent (LOGICAL NOR) this means that
|
||||
|
|
@ -218,6 +216,16 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
|||
/// std::convert::PartialEq<Foo<Bar>>
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
fn path_has_local_parent(path: &Path<'_>, cx: &LateContext<'_>, local_parents: &[DefId]) -> bool {
|
||||
path.res.opt_def_id().is_some_and(|did| local_parents.contains(&cx.tcx.parent(did)))
|
||||
fn path_has_local_parent(
|
||||
path: &Path<'_>,
|
||||
cx: &LateContext<'_>,
|
||||
impl_parent: DefId,
|
||||
impl_parent_parent: Option<DefId>,
|
||||
) -> bool {
|
||||
path.res.opt_def_id().is_some_and(|did| {
|
||||
did.is_local() && {
|
||||
let res_parent = cx.tcx.parent(did);
|
||||
res_parent == impl_parent || Some(res_parent) == impl_parent_parent
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ declare_lint_pass! {
|
|||
MISSING_FRAGMENT_SPECIFIER,
|
||||
MUST_NOT_SUSPEND,
|
||||
NAMED_ARGUMENTS_USED_POSITIONALLY,
|
||||
NON_CONTIGUOUS_RANGE_ENDPOINTS,
|
||||
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
||||
ORDER_DEPENDENT_TRAIT_OBJECTS,
|
||||
OVERLAPPING_RANGE_ENDPOINTS,
|
||||
|
|
@ -812,6 +813,36 @@ declare_lint! {
|
|||
"detects range patterns with overlapping endpoints"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `non_contiguous_range_endpoints` lint detects likely off-by-one errors when using
|
||||
/// exclusive [range patterns].
|
||||
///
|
||||
/// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(exclusive_range_pattern)]
|
||||
/// let x = 123u32;
|
||||
/// match x {
|
||||
/// 0..100 => { println!("small"); }
|
||||
/// 101..1000 => { println!("large"); }
|
||||
/// _ => { println!("larger"); }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// It is likely a mistake to have range patterns in a match expression that miss out a single
|
||||
/// number. Check that the beginning and end values are what you expect, and keep in mind that
|
||||
/// with `..=` the right bound is inclusive, and with `..` it is exclusive.
|
||||
pub NON_CONTIGUOUS_RANGE_ENDPOINTS,
|
||||
Warn,
|
||||
"detects off-by-one errors with exclusive range patterns"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `bindings_with_variant_name` lint detects pattern bindings with
|
||||
/// the same name as one of the matched variants.
|
||||
|
|
|
|||
|
|
@ -569,31 +569,47 @@ impl<'a> CrateLocator<'a> {
|
|||
debug!("skipping empty file");
|
||||
continue;
|
||||
}
|
||||
let (hash, metadata) =
|
||||
match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) {
|
||||
Ok(blob) => {
|
||||
if let Some(h) = self.crate_matches(&blob, &lib) {
|
||||
(h, blob)
|
||||
} else {
|
||||
info!("metadata mismatch");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(MetadataError::LoadFailure(err)) => {
|
||||
info!("no metadata found: {}", err);
|
||||
// The file was present and created by the same compiler version, but we
|
||||
// couldn't load it for some reason. Give a hard error instead of silently
|
||||
// ignoring it, but only if we would have given an error anyway.
|
||||
self.crate_rejections
|
||||
.via_invalid
|
||||
.push(CrateMismatch { path: lib, got: err });
|
||||
let (hash, metadata) = match get_metadata_section(
|
||||
self.target,
|
||||
flavor,
|
||||
&lib,
|
||||
self.metadata_loader,
|
||||
self.cfg_version,
|
||||
) {
|
||||
Ok(blob) => {
|
||||
if let Some(h) = self.crate_matches(&blob, &lib) {
|
||||
(h, blob)
|
||||
} else {
|
||||
info!("metadata mismatch");
|
||||
continue;
|
||||
}
|
||||
Err(err @ MetadataError::NotPresent(_)) => {
|
||||
info!("no metadata found: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(MetadataError::VersionMismatch { expected_version, found_version }) => {
|
||||
// The file was present and created by the same compiler version, but we
|
||||
// couldn't load it for some reason. Give a hard error instead of silently
|
||||
// ignoring it, but only if we would have given an error anyway.
|
||||
info!(
|
||||
"Rejecting via version: expected {} got {}",
|
||||
expected_version, found_version
|
||||
);
|
||||
self.crate_rejections
|
||||
.via_version
|
||||
.push(CrateMismatch { path: lib, got: found_version });
|
||||
continue;
|
||||
}
|
||||
Err(MetadataError::LoadFailure(err)) => {
|
||||
info!("no metadata found: {}", err);
|
||||
// The file was present and created by the same compiler version, but we
|
||||
// couldn't load it for some reason. Give a hard error instead of silently
|
||||
// ignoring it, but only if we would have given an error anyway.
|
||||
self.crate_rejections.via_invalid.push(CrateMismatch { path: lib, got: err });
|
||||
continue;
|
||||
}
|
||||
Err(err @ MetadataError::NotPresent(_)) => {
|
||||
info!("no metadata found: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// If we see multiple hashes, emit an error about duplicate candidates.
|
||||
if slot.as_ref().is_some_and(|s| s.0 != hash) {
|
||||
if let Some(candidates) = err_data {
|
||||
|
|
@ -648,16 +664,6 @@ impl<'a> CrateLocator<'a> {
|
|||
}
|
||||
|
||||
fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> {
|
||||
let rustc_version = rustc_version(self.cfg_version);
|
||||
let found_version = metadata.get_rustc_version();
|
||||
if found_version != rustc_version {
|
||||
info!("Rejecting via version: expected {} got {}", rustc_version, found_version);
|
||||
self.crate_rejections
|
||||
.via_version
|
||||
.push(CrateMismatch { path: libpath.to_path_buf(), got: found_version });
|
||||
return None;
|
||||
}
|
||||
|
||||
let header = metadata.get_header();
|
||||
if header.is_proc_macro_crate != self.is_proc_macro {
|
||||
info!(
|
||||
|
|
@ -770,6 +776,7 @@ fn get_metadata_section<'p>(
|
|||
flavor: CrateFlavor,
|
||||
filename: &'p Path,
|
||||
loader: &dyn MetadataLoader,
|
||||
cfg_version: &'static str,
|
||||
) -> Result<MetadataBlob, MetadataError<'p>> {
|
||||
if !filename.exists() {
|
||||
return Err(MetadataError::NotPresent(filename));
|
||||
|
|
@ -847,13 +854,18 @@ fn get_metadata_section<'p>(
|
|||
}
|
||||
};
|
||||
let blob = MetadataBlob(raw_bytes);
|
||||
if blob.is_compatible() {
|
||||
Ok(blob)
|
||||
} else {
|
||||
Err(MetadataError::LoadFailure(format!(
|
||||
match blob.check_compatibility(cfg_version) {
|
||||
Ok(()) => Ok(blob),
|
||||
Err(None) => Err(MetadataError::LoadFailure(format!(
|
||||
"invalid metadata version found: {}",
|
||||
filename.display()
|
||||
)))
|
||||
))),
|
||||
Err(Some(found_version)) => {
|
||||
return Err(MetadataError::VersionMismatch {
|
||||
expected_version: rustc_version(cfg_version),
|
||||
found_version,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -864,9 +876,10 @@ pub fn list_file_metadata(
|
|||
metadata_loader: &dyn MetadataLoader,
|
||||
out: &mut dyn Write,
|
||||
ls_kinds: &[String],
|
||||
cfg_version: &'static str,
|
||||
) -> IoResult<()> {
|
||||
let flavor = get_flavor_from_path(path);
|
||||
match get_metadata_section(target, flavor, path, metadata_loader) {
|
||||
match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) {
|
||||
Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds),
|
||||
Err(msg) => write!(out, "{msg}\n"),
|
||||
}
|
||||
|
|
@ -932,6 +945,8 @@ enum MetadataError<'a> {
|
|||
NotPresent(&'a Path),
|
||||
/// The file was present and invalid.
|
||||
LoadFailure(String),
|
||||
/// The file was present, but compiled with a different rustc version.
|
||||
VersionMismatch { expected_version: String, found_version: String },
|
||||
}
|
||||
|
||||
impl fmt::Display for MetadataError<'_> {
|
||||
|
|
@ -941,6 +956,12 @@ impl fmt::Display for MetadataError<'_> {
|
|||
f.write_str(&format!("no such file: '{}'", filename.display()))
|
||||
}
|
||||
MetadataError::LoadFailure(msg) => f.write_str(msg),
|
||||
MetadataError::VersionMismatch { expected_version, found_version } => {
|
||||
f.write_str(&format!(
|
||||
"rustc version mismatch. expected {}, found {}",
|
||||
expected_version, found_version,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -684,13 +684,25 @@ impl<'a, 'tcx, I: Idx, T> Decodable<DecodeContext<'a, 'tcx>> for LazyTable<I, T>
|
|||
implement_ty_decoder!(DecodeContext<'a, 'tcx>);
|
||||
|
||||
impl MetadataBlob {
|
||||
pub(crate) fn is_compatible(&self) -> bool {
|
||||
self.blob().starts_with(METADATA_HEADER)
|
||||
}
|
||||
pub(crate) fn check_compatibility(
|
||||
&self,
|
||||
cfg_version: &'static str,
|
||||
) -> Result<(), Option<String>> {
|
||||
if !self.blob().starts_with(METADATA_HEADER) {
|
||||
if self.blob().starts_with(b"rust") {
|
||||
return Err(Some("<unknown rustc version>".to_owned()));
|
||||
}
|
||||
return Err(None);
|
||||
}
|
||||
|
||||
pub(crate) fn get_rustc_version(&self) -> String {
|
||||
LazyValue::<String>::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap())
|
||||
.decode(self)
|
||||
let found_version =
|
||||
LazyValue::<String>::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap())
|
||||
.decode(self);
|
||||
if rustc_version(cfg_version) != found_version {
|
||||
return Err(Some(found_version));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn root_pos(&self) -> NonZero<usize> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
derive_more = "0.99.17"
|
||||
either = "1.5.0"
|
||||
field-offset = "0.3.5"
|
||||
gsgdt = "0.1.2"
|
||||
|
|
|
|||
|
|
@ -915,7 +915,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
|
||||
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
|
||||
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
||||
NullOp::DebugAssertions => write!(fmt, "cfg!(debug_assertions)"),
|
||||
NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"),
|
||||
}
|
||||
}
|
||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||
|
|
|
|||
|
|
@ -1366,8 +1366,16 @@ pub enum NullOp<'tcx> {
|
|||
AlignOf,
|
||||
/// Returns the offset of a field
|
||||
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
|
||||
/// cfg!(debug_assertions), but expanded in codegen
|
||||
DebugAssertions,
|
||||
/// Returns whether we want to check for library UB or language UB at monomorphization time.
|
||||
/// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in
|
||||
/// const-eval/Miri, because the interpreter has its own better checks for language UB.
|
||||
UbCheck(UbKind),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum UbKind {
|
||||
LanguageUb,
|
||||
LibraryUb,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
||||
tcx.types.usize
|
||||
}
|
||||
Rvalue::NullaryOp(NullOp::DebugAssertions, _) => tcx.types.bool,
|
||||
Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool,
|
||||
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
|
||||
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
|
||||
AggregateKind::Tuple => {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub struct Instance<'tcx> {
|
|||
pub args: GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)]
|
||||
pub enum InstanceDef<'tcx> {
|
||||
/// A user-defined callable item.
|
||||
|
|
|
|||
|
|
@ -2008,13 +2008,10 @@ impl<'tcx> Ty<'tcx> {
|
|||
// Single-argument Box is always global. (for "minicore" tests)
|
||||
return true;
|
||||
};
|
||||
if let Some(alloc_adt) = alloc.expect_ty().ty_adt_def() {
|
||||
alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| {
|
||||
let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None);
|
||||
alloc_adt.did() == global_alloc
|
||||
} else {
|
||||
// Allocator is not an ADT...
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
either = "1"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
|||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::NullaryOp(
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::DebugAssertions,
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_),
|
||||
_,
|
||||
) => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
either = "1"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
NullOp::OffsetOf(fields) => {
|
||||
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
|
||||
}
|
||||
NullOp::DebugAssertions => return None,
|
||||
NullOp::UbCheck(_) => return None,
|
||||
};
|
||||
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
||||
let imm = ImmTy::try_from_uint(val, usize_layout)?;
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
NullOp::OffsetOf(fields) => {
|
||||
op_layout.offset_of_subfield(self, fields.iter()).bytes()
|
||||
}
|
||||
NullOp::DebugAssertions => return None,
|
||||
NullOp::UbCheck(_) => return None,
|
||||
};
|
||||
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
|||
sym::unreachable => {
|
||||
terminator.kind = TerminatorKind::Unreachable;
|
||||
}
|
||||
sym::debug_assertions => {
|
||||
sym::check_language_ub => {
|
||||
let target = target.unwrap();
|
||||
block.statements.push(Statement {
|
||||
source_info: terminator.source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
*destination,
|
||||
Rvalue::NullaryOp(NullOp::DebugAssertions, tcx.types.bool),
|
||||
Rvalue::NullaryOp(
|
||||
NullOp::UbCheck(UbKind::LanguageUb),
|
||||
tcx.types.bool,
|
||||
),
|
||||
))),
|
||||
});
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
}
|
||||
sym::check_library_ub => {
|
||||
let target = target.unwrap();
|
||||
block.statements.push(Statement {
|
||||
source_info: terminator.source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
*destination,
|
||||
Rvalue::NullaryOp(
|
||||
NullOp::UbCheck(UbKind::LibraryUb),
|
||||
tcx.types.bool,
|
||||
),
|
||||
))),
|
||||
});
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
NullOp::SizeOf => {}
|
||||
NullOp::AlignOf => {}
|
||||
NullOp::OffsetOf(_) => {}
|
||||
NullOp::DebugAssertions => {}
|
||||
NullOp::UbCheck(_) => {}
|
||||
},
|
||||
|
||||
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
|
||||
|
|
|
|||
|
|
@ -667,7 +667,7 @@ impl<'a> Parser<'a> {
|
|||
{
|
||||
err.note("you may be trying to write a c-string literal");
|
||||
err.note("c-string literals require Rust 2021 or later");
|
||||
HelpUseLatestEdition::new().add_to_diagnostic(&mut err);
|
||||
err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new());
|
||||
}
|
||||
|
||||
// `pub` may be used for an item or `pub(crate)`
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
pattern_analysis_excluside_range_missing_gap = multiple ranges are one apart
|
||||
.label = this range doesn't match `{$gap}` because `..` is an exclusive range
|
||||
.suggestion = use an inclusive range instead
|
||||
|
||||
pattern_analysis_excluside_range_missing_max = exclusive range missing `{$max}`
|
||||
.label = this range doesn't match `{$max}` because `..` is an exclusive range
|
||||
.suggestion = use an inclusive range instead
|
||||
|
||||
pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly
|
||||
.help = ensure that all variants are matched explicitly by adding the suggested match arms
|
||||
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ impl fmt::Display for RangeEnd {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum MaybeInfiniteInt {
|
||||
NegInfinity,
|
||||
/// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
|
||||
/// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite_{int,uint}`.
|
||||
#[non_exhaustive]
|
||||
Finite(u128),
|
||||
/// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range.
|
||||
|
|
@ -229,25 +229,22 @@ impl MaybeInfiniteInt {
|
|||
}
|
||||
|
||||
/// Note: this will not turn a finite value into an infinite one or vice-versa.
|
||||
pub fn minus_one(self) -> Self {
|
||||
pub fn minus_one(self) -> Option<Self> {
|
||||
match self {
|
||||
Finite(n) => match n.checked_sub(1) {
|
||||
Some(m) => Finite(m),
|
||||
None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"),
|
||||
},
|
||||
JustAfterMax => Finite(u128::MAX),
|
||||
x => x,
|
||||
Finite(n) => n.checked_sub(1).map(Finite),
|
||||
JustAfterMax => Some(Finite(u128::MAX)),
|
||||
x => Some(x),
|
||||
}
|
||||
}
|
||||
/// Note: this will not turn a finite value into an infinite one or vice-versa.
|
||||
pub fn plus_one(self) -> Self {
|
||||
pub fn plus_one(self) -> Option<Self> {
|
||||
match self {
|
||||
Finite(n) => match n.checked_add(1) {
|
||||
Some(m) => Finite(m),
|
||||
None => JustAfterMax,
|
||||
Some(m) => Some(Finite(m)),
|
||||
None => Some(JustAfterMax),
|
||||
},
|
||||
JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"),
|
||||
x => x,
|
||||
JustAfterMax => None,
|
||||
x => Some(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -268,18 +265,24 @@ impl IntRange {
|
|||
pub fn is_singleton(&self) -> bool {
|
||||
// Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite
|
||||
// to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`.
|
||||
self.lo.plus_one() == self.hi
|
||||
self.lo.plus_one() == Some(self.hi)
|
||||
}
|
||||
|
||||
/// Construct a singleton range.
|
||||
/// `x` must be a `Finite(_)` value.
|
||||
#[inline]
|
||||
pub fn from_singleton(x: MaybeInfiniteInt) -> IntRange {
|
||||
IntRange { lo: x, hi: x.plus_one() }
|
||||
// `unwrap()` is ok on a finite value
|
||||
IntRange { lo: x, hi: x.plus_one().unwrap() }
|
||||
}
|
||||
|
||||
/// Construct a range with these boundaries.
|
||||
/// `lo` must not be `PosInfinity` or `JustAfterMax`. `hi` must not be `NegInfinity`.
|
||||
/// If `end` is `Included`, `hi` must also not be `JustAfterMax`.
|
||||
#[inline]
|
||||
pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange {
|
||||
if end == RangeEnd::Included {
|
||||
hi = hi.plus_one();
|
||||
hi = hi.plus_one().unwrap();
|
||||
}
|
||||
if lo >= hi {
|
||||
// This should have been caught earlier by E0030.
|
||||
|
|
|
|||
|
|
@ -76,6 +76,57 @@ impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(pattern_analysis_excluside_range_missing_max)]
|
||||
pub struct ExclusiveRangeMissingMax<'tcx> {
|
||||
#[label]
|
||||
#[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")]
|
||||
/// This is an exclusive range that looks like `lo..max` (i.e. doesn't match `max`).
|
||||
pub first_range: Span,
|
||||
/// Suggest `lo..=max` instead.
|
||||
pub suggestion: String,
|
||||
pub max: Pat<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(pattern_analysis_excluside_range_missing_gap)]
|
||||
pub struct ExclusiveRangeMissingGap<'tcx> {
|
||||
#[label]
|
||||
#[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")]
|
||||
/// This is an exclusive range that looks like `lo..gap` (i.e. doesn't match `gap`).
|
||||
pub first_range: Span,
|
||||
pub gap: Pat<'tcx>,
|
||||
/// Suggest `lo..=gap` instead.
|
||||
pub suggestion: String,
|
||||
#[subdiagnostic]
|
||||
/// All these ranges skipped over `gap` which we think is probably a mistake.
|
||||
pub gap_with: Vec<GappedRange<'tcx>>,
|
||||
}
|
||||
|
||||
pub struct GappedRange<'tcx> {
|
||||
pub span: Span,
|
||||
pub gap: Pat<'tcx>,
|
||||
pub first_range: Pat<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> AddToDiagnostic for GappedRange<'tcx> {
|
||||
fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
_: F,
|
||||
) {
|
||||
let GappedRange { span, gap, first_range } = self;
|
||||
|
||||
// FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
|
||||
// does not support `#[subdiagnostic(eager)]`...
|
||||
let message = format!(
|
||||
"this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \
|
||||
either of them"
|
||||
);
|
||||
diag.span_label(span, message);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(pattern_analysis_non_exhaustive_omitted_pattern)]
|
||||
#[help]
|
||||
|
|
|
|||
|
|
@ -70,14 +70,8 @@ use rustc_middle::ty::Ty;
|
|||
use rustc_span::ErrorGuaranteed;
|
||||
|
||||
use crate::constructor::{Constructor, ConstructorSet, IntRange};
|
||||
#[cfg(feature = "rustc")]
|
||||
use crate::lints::lint_nonexhaustive_missing_variants;
|
||||
use crate::pat::DeconstructedPat;
|
||||
use crate::pat_column::PatternColumn;
|
||||
#[cfg(feature = "rustc")]
|
||||
use crate::rustc::RustcMatchCheckCtxt;
|
||||
#[cfg(feature = "rustc")]
|
||||
use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||
|
||||
pub trait Captures<'a> {}
|
||||
impl<'a, T: ?Sized> Captures<'a> for T {}
|
||||
|
|
@ -145,6 +139,18 @@ pub trait TypeCx: Sized + fmt::Debug {
|
|||
|
||||
/// The maximum pattern complexity limit was reached.
|
||||
fn complexity_exceeded(&self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Lint that there is a gap `gap` between `pat` and all of `gapped_with` such that the gap is
|
||||
/// not matched by another range. If `gapped_with` is empty, then `gap` is `T::MAX`. We only
|
||||
/// detect singleton gaps.
|
||||
/// The default implementation does nothing.
|
||||
fn lint_non_contiguous_range_endpoints(
|
||||
&self,
|
||||
_pat: &DeconstructedPat<Self>,
|
||||
_gap: IntRange,
|
||||
_gapped_with: &[&DeconstructedPat<Self>],
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The arm of a match expression.
|
||||
|
|
@ -167,11 +173,14 @@ impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {}
|
|||
/// useful, and runs some lints.
|
||||
#[cfg(feature = "rustc")]
|
||||
pub fn analyze_match<'p, 'tcx>(
|
||||
tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
||||
tycx: &rustc::RustcMatchCheckCtxt<'p, 'tcx>,
|
||||
arms: &[rustc::MatchArm<'p, 'tcx>],
|
||||
scrut_ty: Ty<'tcx>,
|
||||
pattern_complexity_limit: Option<usize>,
|
||||
) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
|
||||
use lints::lint_nonexhaustive_missing_variants;
|
||||
use usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||
|
||||
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
|
||||
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
|
||||
let report =
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_index::{Idx, IndexVec};
|
|||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
|
||||
use rustc_session::lint;
|
||||
|
|
@ -718,12 +718,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
|||
let value = mir::Const::from_ty_const(c, cx.tcx);
|
||||
lo = PatRangeBoundary::Finite(value);
|
||||
}
|
||||
let hi = if matches!(range.hi, Finite(0)) {
|
||||
let hi = if let Some(hi) = range.hi.minus_one() {
|
||||
hi
|
||||
} else {
|
||||
// The range encodes `..ty::MIN`, so we can't convert it to an inclusive range.
|
||||
end = rustc_hir::RangeEnd::Excluded;
|
||||
range.hi
|
||||
} else {
|
||||
range.hi.minus_one()
|
||||
};
|
||||
let hi = cx.hoist_pat_range_bdy(hi, ty);
|
||||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
|
||||
|
|
@ -900,6 +900,70 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
|||
let span = self.whole_match_span.unwrap_or(self.scrut_span);
|
||||
Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
|
||||
}
|
||||
|
||||
fn lint_non_contiguous_range_endpoints(
|
||||
&self,
|
||||
pat: &crate::pat::DeconstructedPat<Self>,
|
||||
gap: IntRange,
|
||||
gapped_with: &[&crate::pat::DeconstructedPat<Self>],
|
||||
) {
|
||||
let Some(&thir_pat) = pat.data() else { return };
|
||||
let thir::PatKind::Range(range) = &thir_pat.kind else { return };
|
||||
// Only lint when the left range is an exclusive range.
|
||||
if range.end != rustc_hir::RangeEnd::Excluded {
|
||||
return;
|
||||
}
|
||||
// `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with
|
||||
// `gap+1`.
|
||||
let suggested_range: thir::Pat<'_> = {
|
||||
// Suggest `lo..=gap` instead.
|
||||
let mut suggested_range = thir_pat.clone();
|
||||
let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() };
|
||||
range.end = rustc_hir::RangeEnd::Included;
|
||||
suggested_range
|
||||
};
|
||||
let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty());
|
||||
if gapped_with.is_empty() {
|
||||
// If `gapped_with` is empty, `gap == T::MAX`.
|
||||
self.tcx.emit_node_span_lint(
|
||||
lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
|
||||
self.match_lint_level,
|
||||
thir_pat.span,
|
||||
errors::ExclusiveRangeMissingMax {
|
||||
// Point at this range.
|
||||
first_range: thir_pat.span,
|
||||
// That's the gap that isn't covered.
|
||||
max: gap_as_pat.clone(),
|
||||
// Suggest `lo..=max` instead.
|
||||
suggestion: suggested_range.to_string(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
|
||||
self.match_lint_level,
|
||||
thir_pat.span,
|
||||
errors::ExclusiveRangeMissingGap {
|
||||
// Point at this range.
|
||||
first_range: thir_pat.span,
|
||||
// That's the gap that isn't covered.
|
||||
gap: gap_as_pat.clone(),
|
||||
// Suggest `lo..=gap` instead.
|
||||
suggestion: suggested_range.to_string(),
|
||||
// All these ranges skipped over `gap` which we think is probably a
|
||||
// mistake.
|
||||
gap_with: gapped_with
|
||||
.iter()
|
||||
.map(|pat| errors::GappedRange {
|
||||
span: pat.data().unwrap().span,
|
||||
gap: gap_as_pat.clone(),
|
||||
first_range: thir_pat.clone(),
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
|
||||
|
|
|
|||
|
|
@ -1489,7 +1489,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
|
|||
/// We can however get false negatives because exhaustiveness does not explore all cases. See the
|
||||
/// section on relevancy at the top of the file.
|
||||
fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
|
||||
mcx: &mut UsefulnessCtxt<'_, Cx>,
|
||||
cx: &Cx,
|
||||
overlap_range: IntRange,
|
||||
matrix: &Matrix<'p, Cx>,
|
||||
specialized_matrix: &Matrix<'p, Cx>,
|
||||
|
|
@ -1522,11 +1522,11 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
|
|||
.map(|&(_, pat)| pat)
|
||||
.collect();
|
||||
if !overlaps_with.is_empty() {
|
||||
mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
|
||||
cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
|
||||
}
|
||||
}
|
||||
suffixes.push((child_row_id, pat))
|
||||
} else if this_range.hi == overlap.plus_one() {
|
||||
} else if Some(this_range.hi) == overlap.plus_one() {
|
||||
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
|
||||
// ranges that look like `overlap..=hi`.
|
||||
if !suffixes.is_empty() {
|
||||
|
|
@ -1538,7 +1538,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
|
|||
.map(|&(_, pat)| pat)
|
||||
.collect();
|
||||
if !overlaps_with.is_empty() {
|
||||
mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
|
||||
cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
|
||||
}
|
||||
}
|
||||
prefixes.push((child_row_id, pat))
|
||||
|
|
@ -1546,6 +1546,33 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Collect ranges that have a singleton gap between them.
|
||||
fn collect_non_contiguous_range_endpoints<'p, Cx: TypeCx>(
|
||||
cx: &Cx,
|
||||
gap_range: &IntRange,
|
||||
matrix: &Matrix<'p, Cx>,
|
||||
) {
|
||||
let gap = gap_range.lo;
|
||||
// Ranges that look like `lo..gap`.
|
||||
let mut onebefore: SmallVec<[_; 1]> = Default::default();
|
||||
// Ranges that start on `gap+1` or singletons `gap+1`.
|
||||
let mut oneafter: SmallVec<[_; 1]> = Default::default();
|
||||
// Look through the column for ranges near the gap.
|
||||
for pat in matrix.heads() {
|
||||
let PatOrWild::Pat(pat) = pat else { continue };
|
||||
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
|
||||
if gap == this_range.hi {
|
||||
onebefore.push(pat)
|
||||
} else if gap.plus_one() == Some(this_range.lo) {
|
||||
oneafter.push(pat)
|
||||
}
|
||||
}
|
||||
|
||||
for pat_before in onebefore {
|
||||
cx.lint_non_contiguous_range_endpoints(pat_before, *gap_range, oneafter.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
/// The core of the algorithm.
|
||||
///
|
||||
/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
|
||||
|
|
@ -1626,13 +1653,24 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
|||
&& spec_matrix.rows.len() >= 2
|
||||
&& spec_matrix.rows.iter().any(|row| !row.intersects.is_empty())
|
||||
{
|
||||
collect_overlapping_range_endpoints(mcx, overlap_range, matrix, &spec_matrix);
|
||||
collect_overlapping_range_endpoints(mcx.tycx, overlap_range, matrix, &spec_matrix);
|
||||
}
|
||||
}
|
||||
|
||||
matrix.unspecialize(spec_matrix);
|
||||
}
|
||||
|
||||
// Detect singleton gaps between ranges.
|
||||
if missing_ctors.iter().any(|c| matches!(c, Constructor::IntRange(..))) {
|
||||
for missing in &missing_ctors {
|
||||
if let Constructor::IntRange(gap) = missing {
|
||||
if gap.is_singleton() {
|
||||
collect_non_contiguous_range_endpoints(mcx.tycx, gap, matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record usefulness in the patterns.
|
||||
for row in matrix.rows() {
|
||||
if row.useful {
|
||||
|
|
|
|||
|
|
@ -17,10 +17,9 @@ use std::num::NonZero;
|
|||
use {
|
||||
parking_lot::{Condvar, Mutex},
|
||||
rustc_data_structures::fx::FxHashSet,
|
||||
rustc_data_structures::{defer, jobserver},
|
||||
rustc_data_structures::jobserver,
|
||||
rustc_span::DUMMY_SP,
|
||||
std::iter,
|
||||
std::process,
|
||||
std::sync::Arc,
|
||||
};
|
||||
|
||||
|
|
@ -514,12 +513,7 @@ fn remove_cycle(
|
|||
/// There may be multiple cycles involved in a deadlock, so this searches
|
||||
/// all active queries for cycles before finally resuming all the waiters at once.
|
||||
#[cfg(parallel_compiler)]
|
||||
pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
|
||||
let on_panic = defer(|| {
|
||||
eprintln!("deadlock handler panicked, aborting process");
|
||||
process::abort();
|
||||
});
|
||||
|
||||
pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) {
|
||||
let mut wakelist = Vec::new();
|
||||
let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
|
||||
|
||||
|
|
@ -539,19 +533,17 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
|
|||
// X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
|
||||
// only considers the true dependency and won't detect a cycle.
|
||||
if !found_cycle {
|
||||
if query_map.len() == 0 {
|
||||
panic!("deadlock detected without any query!")
|
||||
} else {
|
||||
panic!("deadlock detected! current query map:\n{:#?}", query_map);
|
||||
}
|
||||
panic!(
|
||||
"deadlock detected as we're unable to find a query cycle to break\n\
|
||||
current query map:\n{:#?}",
|
||||
query_map
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME: Ensure this won't cause a deadlock before we return
|
||||
for waiter in wakelist.into_iter() {
|
||||
waiter.notify(registry);
|
||||
}
|
||||
|
||||
on_panic.disable();
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ pub use self::plumbing::*;
|
|||
|
||||
mod job;
|
||||
#[cfg(parallel_compiler)]
|
||||
pub use self::job::deadlock;
|
||||
pub use self::job::break_query_cycles;
|
||||
pub use self::job::{
|
||||
print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -251,13 +251,19 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
|
|||
type T = stable_mir::mir::NullOp;
|
||||
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
|
||||
use rustc_middle::mir::NullOp::*;
|
||||
use rustc_middle::mir::UbKind;
|
||||
match self {
|
||||
SizeOf => stable_mir::mir::NullOp::SizeOf,
|
||||
AlignOf => stable_mir::mir::NullOp::AlignOf,
|
||||
OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf(
|
||||
indices.iter().map(|idx| idx.stable(tables)).collect(),
|
||||
),
|
||||
DebugAssertions => stable_mir::mir::NullOp::DebugAssertions,
|
||||
UbCheck(UbKind::LanguageUb) => {
|
||||
stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb)
|
||||
}
|
||||
UbCheck(UbKind::LibraryUb) => {
|
||||
stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
indexmap = { version = "2.0.0" }
|
||||
itoa = "1.0"
|
||||
md5 = { package = "md-5", version = "0.10.0" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
|
|||
|
|
@ -518,6 +518,8 @@ symbols! {
|
|||
cfi,
|
||||
cfi_encoding,
|
||||
char,
|
||||
check_language_ub,
|
||||
check_library_ub,
|
||||
client,
|
||||
clippy,
|
||||
clobber_abi,
|
||||
|
|
@ -998,6 +1000,11 @@ symbols! {
|
|||
is_val_statically_known,
|
||||
isa_attribute,
|
||||
isize,
|
||||
isize_legacy_const_max,
|
||||
isize_legacy_const_min,
|
||||
isize_legacy_fn_max_value,
|
||||
isize_legacy_fn_min_value,
|
||||
isize_legacy_mod,
|
||||
issue,
|
||||
issue_5723_bootstrap,
|
||||
issue_tracker_base_url,
|
||||
|
|
@ -1450,6 +1457,7 @@ symbols! {
|
|||
residual,
|
||||
result,
|
||||
resume,
|
||||
retag_box_to_raw,
|
||||
return_position_impl_trait_in_trait,
|
||||
return_type_notation,
|
||||
rhs,
|
||||
|
|
@ -1907,6 +1915,11 @@ symbols! {
|
|||
used_with_arg,
|
||||
using,
|
||||
usize,
|
||||
usize_legacy_const_max,
|
||||
usize_legacy_const_min,
|
||||
usize_legacy_fn_max_value,
|
||||
usize_legacy_fn_min_value,
|
||||
usize_legacy_mod,
|
||||
va_arg,
|
||||
va_copy,
|
||||
va_end,
|
||||
|
|
@ -2327,13 +2340,15 @@ pub mod sym {
|
|||
///
|
||||
/// The first few non-negative integers each have a static symbol and therefore
|
||||
/// are fast.
|
||||
pub fn integer<N: TryInto<usize> + Copy + ToString>(n: N) -> Symbol {
|
||||
pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
|
||||
if let Result::Ok(idx) = n.try_into() {
|
||||
if idx < 10 {
|
||||
return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
|
||||
}
|
||||
}
|
||||
Symbol::intern(&n.to_string())
|
||||
let mut buffer = itoa::Buffer::new();
|
||||
let printed = buffer.format(n);
|
||||
Symbol::intern(printed)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
itertools = "0.11.0"
|
||||
itertools = "0.12"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_ir = { path = "../rustc_ast_ir" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ use crate::infer::InferCtxt;
|
|||
use crate::traits;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
|
||||
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
|
@ -535,305 +537,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn compute(&mut self, arg: GenericArg<'tcx>) {
|
||||
let mut walker = arg.walk();
|
||||
let param_env = self.param_env;
|
||||
let depth = self.recursion_depth;
|
||||
while let Some(arg) = walker.next() {
|
||||
debug!(?arg, ?self.out);
|
||||
let ty = match arg.unpack() {
|
||||
GenericArgKind::Type(ty) => ty,
|
||||
|
||||
// No WF constraints for lifetimes being present, any outlives
|
||||
// obligations are handled by the parent (e.g. `ty::Ref`).
|
||||
GenericArgKind::Lifetime(_) => continue,
|
||||
|
||||
GenericArgKind::Const(ct) => {
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => {
|
||||
if !ct.has_escaping_bound_vars() {
|
||||
let obligations = self.nominal_obligations(uv.def, uv.args);
|
||||
self.out.extend(obligations);
|
||||
|
||||
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::ConstEvaluatable(ct),
|
||||
));
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
predicate,
|
||||
));
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Infer(_) => {
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::WellFormed(ct.into()),
|
||||
)),
|
||||
));
|
||||
}
|
||||
ty::ConstKind::Expr(_) => {
|
||||
// FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
|
||||
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
|
||||
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
|
||||
// which means that the `DefId` would have been typeck'd elsewhere. However in
|
||||
// the future we may allow directly lowering to `ConstKind::Expr` in which case
|
||||
// we would not be proving bounds we should.
|
||||
|
||||
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::ConstEvaluatable(ct),
|
||||
));
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
predicate,
|
||||
));
|
||||
}
|
||||
|
||||
ty::ConstKind::Error(_)
|
||||
| ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Bound(..)
|
||||
| ty::ConstKind::Placeholder(..) => {
|
||||
// These variants are trivially WF, so nothing to do here.
|
||||
}
|
||||
ty::ConstKind::Value(..) => {
|
||||
// FIXME: Enforce that values are structurally-matchable.
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind());
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(..)
|
||||
| ty::Uint(..)
|
||||
| ty::Float(..)
|
||||
| ty::Error(_)
|
||||
| ty::Str
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Param(_)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Foreign(..) => {
|
||||
// WfScalar, WfParameter, etc
|
||||
}
|
||||
|
||||
// Can only infer to `ty::Int(_) | ty::Uint(_)`.
|
||||
ty::Infer(ty::IntVar(_)) => {}
|
||||
|
||||
// Can only infer to `ty::Float(_)`.
|
||||
ty::Infer(ty::FloatVar(_)) => {}
|
||||
|
||||
ty::Slice(subty) => {
|
||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||
}
|
||||
|
||||
ty::Array(subty, _) => {
|
||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||
// Note that we handle the len is implicitly checked while walking `arg`.
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => {
|
||||
if let Some((_last, rest)) = tys.split_last() {
|
||||
for &elem in rest {
|
||||
self.require_sized(elem, traits::TupleElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::RawPtr(_) => {
|
||||
// Simple cases that are WF if their type args are WF.
|
||||
}
|
||||
|
||||
ty::Alias(ty::Projection, data) => {
|
||||
walker.skip_current_subtree(); // Subtree handled by compute_projection.
|
||||
self.compute_projection(data);
|
||||
}
|
||||
ty::Alias(ty::Inherent, data) => {
|
||||
walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection.
|
||||
self.compute_inherent_projection(data);
|
||||
}
|
||||
|
||||
ty::Adt(def, args) => {
|
||||
// WfNominalType
|
||||
let obligations = self.nominal_obligations(def.did(), args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::FnDef(did, args) => {
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::Ref(r, rty, _) => {
|
||||
// WfReference
|
||||
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
|
||||
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
depth,
|
||||
param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)),
|
||||
)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
ty::Coroutine(did, args, ..) => {
|
||||
// Walk ALL the types in the coroutine: this will
|
||||
// include the upvar types as well as the yield
|
||||
// type. Note that this is mildly distinct from
|
||||
// the closure case, where we have to be careful
|
||||
// about the signature of the closure. We don't
|
||||
// have the problem of implied bounds here since
|
||||
// coroutines don't take arguments.
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::Closure(did, args) => {
|
||||
// Only check the upvar types for WF, not the rest
|
||||
// of the types within. This is needed because we
|
||||
// capture the signature and it may not be WF
|
||||
// without the implied bounds. Consider a closure
|
||||
// like `|x: &'a T|` -- it may be that `T: 'a` is
|
||||
// not known to hold in the creator's context (and
|
||||
// indeed the closure may not be invoked by its
|
||||
// creator, but rather turned to someone who *can*
|
||||
// verify that).
|
||||
//
|
||||
// The special treatment of closures here really
|
||||
// ought not to be necessary either; the problem
|
||||
// is related to #25860 -- there is no way for us
|
||||
// to express a fn type complete with the implied
|
||||
// bounds that it is assuming. I think in reality
|
||||
// the WF rules around fn are a bit messed up, and
|
||||
// that is the rot problem: `fn(&'a T)` should
|
||||
// probably always be WF, because it should be
|
||||
// shorthand for something like `where(T: 'a) {
|
||||
// fn(&'a T) }`, as discussed in #25860.
|
||||
walker.skip_current_subtree(); // subtree handled below
|
||||
// FIXME(eddyb) add the type to `walker` instead of recursing.
|
||||
self.compute(args.as_closure().tupled_upvars_ty().into());
|
||||
// Note that we cannot skip the generic types
|
||||
// types. Normally, within the fn
|
||||
// body where they are created, the generics will
|
||||
// always be WF, and outside of that fn body we
|
||||
// are not directly inspecting closure types
|
||||
// anyway, except via auto trait matching (which
|
||||
// only inspects the upvar types).
|
||||
// But when a closure is part of a type-alias-impl-trait
|
||||
// then the function that created the defining site may
|
||||
// have had more bounds available than the type alias
|
||||
// specifies. This may cause us to have a closure in the
|
||||
// hidden type that is not actually well formed and
|
||||
// can cause compiler crashes when the user abuses unsafe
|
||||
// code to procure such a closure.
|
||||
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(did, args) => {
|
||||
// See the above comments. The same apply to coroutine-closures.
|
||||
walker.skip_current_subtree();
|
||||
self.compute(args.as_coroutine_closure().tupled_upvars_ty().into());
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::FnPtr(_) => {
|
||||
// let the loop iterate into the argument/return
|
||||
// types appearing in the fn signature
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// All of the requirements on type parameters
|
||||
// have already been checked for `impl Trait` in
|
||||
// return position. We do need to check type-alias-impl-trait though.
|
||||
if self.tcx().is_type_alias_impl_trait(def_id) {
|
||||
let obligations = self.nominal_obligations(def_id, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
}
|
||||
|
||||
ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
|
||||
let obligations = self.nominal_obligations(def_id, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::Dynamic(data, r, _) => {
|
||||
// WfObject
|
||||
//
|
||||
// Here, we defer WF checking due to higher-ranked
|
||||
// regions. This is perhaps not ideal.
|
||||
self.from_object_ty(ty, data, r);
|
||||
|
||||
// FIXME(#27579) RFC also considers adding trait
|
||||
// obligations that don't refer to Self and
|
||||
// checking those
|
||||
|
||||
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
|
||||
|
||||
if !defer_to_coercion {
|
||||
if let Some(principal) = data.principal_def_id() {
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
self.cause(traits::WellFormed(None)),
|
||||
depth,
|
||||
param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inference variables are the complicated case, since we don't
|
||||
// know what type they are. We do two things:
|
||||
//
|
||||
// 1. Check if they have been resolved, and if so proceed with
|
||||
// THAT type.
|
||||
// 2. If not, we've at least simplified things (e.g., we went
|
||||
// from `Vec<$0>: WF` to `$0: WF`), so we can
|
||||
// register a pending obligation and keep
|
||||
// moving. (Goal is that an "inductive hypothesis"
|
||||
// is satisfied to ensure termination.)
|
||||
// See also the comment on `fn obligations`, describing "livelock"
|
||||
// prevention, which happens before this can be reached.
|
||||
ty::Infer(_) => {
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
||||
ty.into(),
|
||||
))),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
debug!(?self.out);
|
||||
}
|
||||
arg.visit_with(self);
|
||||
debug!(?self.out);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
|
|
@ -933,6 +638,302 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
||||
type Result = ();
|
||||
|
||||
fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
|
||||
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
|
||||
|
||||
match *t.kind() {
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(..)
|
||||
| ty::Uint(..)
|
||||
| ty::Float(..)
|
||||
| ty::Error(_)
|
||||
| ty::Str
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Param(_)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Foreign(..) => {
|
||||
// WfScalar, WfParameter, etc
|
||||
}
|
||||
|
||||
// Can only infer to `ty::Int(_) | ty::Uint(_)`.
|
||||
ty::Infer(ty::IntVar(_)) => {}
|
||||
|
||||
// Can only infer to `ty::Float(_)`.
|
||||
ty::Infer(ty::FloatVar(_)) => {}
|
||||
|
||||
ty::Slice(subty) => {
|
||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||
}
|
||||
|
||||
ty::Array(subty, _) => {
|
||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||
// Note that we handle the len is implicitly checked while walking `arg`.
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => {
|
||||
if let Some((_last, rest)) = tys.split_last() {
|
||||
for &elem in rest {
|
||||
self.require_sized(elem, traits::TupleElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::RawPtr(_) => {
|
||||
// Simple cases that are WF if their type args are WF.
|
||||
}
|
||||
|
||||
ty::Alias(ty::Projection, data) => {
|
||||
self.compute_projection(data);
|
||||
return; // Subtree handled by compute_projection.
|
||||
}
|
||||
ty::Alias(ty::Inherent, data) => {
|
||||
self.compute_inherent_projection(data);
|
||||
return; // Subtree handled by compute_inherent_projection.
|
||||
}
|
||||
|
||||
ty::Adt(def, args) => {
|
||||
// WfNominalType
|
||||
let obligations = self.nominal_obligations(def.did(), args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::FnDef(did, args) => {
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::Ref(r, rty, _) => {
|
||||
// WfReference
|
||||
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
|
||||
let cause = self.cause(traits::ReferenceOutlivesReferent(t));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
|
||||
ty::OutlivesPredicate(rty, r),
|
||||
))),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
ty::Coroutine(did, args, ..) => {
|
||||
// Walk ALL the types in the coroutine: this will
|
||||
// include the upvar types as well as the yield
|
||||
// type. Note that this is mildly distinct from
|
||||
// the closure case, where we have to be careful
|
||||
// about the signature of the closure. We don't
|
||||
// have the problem of implied bounds here since
|
||||
// coroutines don't take arguments.
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::Closure(did, args) => {
|
||||
// Note that we cannot skip the generic types
|
||||
// types. Normally, within the fn
|
||||
// body where they are created, the generics will
|
||||
// always be WF, and outside of that fn body we
|
||||
// are not directly inspecting closure types
|
||||
// anyway, except via auto trait matching (which
|
||||
// only inspects the upvar types).
|
||||
// But when a closure is part of a type-alias-impl-trait
|
||||
// then the function that created the defining site may
|
||||
// have had more bounds available than the type alias
|
||||
// specifies. This may cause us to have a closure in the
|
||||
// hidden type that is not actually well formed and
|
||||
// can cause compiler crashes when the user abuses unsafe
|
||||
// code to procure such a closure.
|
||||
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
// Only check the upvar types for WF, not the rest
|
||||
// of the types within. This is needed because we
|
||||
// capture the signature and it may not be WF
|
||||
// without the implied bounds. Consider a closure
|
||||
// like `|x: &'a T|` -- it may be that `T: 'a` is
|
||||
// not known to hold in the creator's context (and
|
||||
// indeed the closure may not be invoked by its
|
||||
// creator, but rather turned to someone who *can*
|
||||
// verify that).
|
||||
//
|
||||
// The special treatment of closures here really
|
||||
// ought not to be necessary either; the problem
|
||||
// is related to #25860 -- there is no way for us
|
||||
// to express a fn type complete with the implied
|
||||
// bounds that it is assuming. I think in reality
|
||||
// the WF rules around fn are a bit messed up, and
|
||||
// that is the rot problem: `fn(&'a T)` should
|
||||
// probably always be WF, because it should be
|
||||
// shorthand for something like `where(T: 'a) {
|
||||
// fn(&'a T) }`, as discussed in #25860.
|
||||
let upvars = args.as_closure().tupled_upvars_ty();
|
||||
return upvars.visit_with(self);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(did, args) => {
|
||||
// See the above comments. The same apply to coroutine-closures.
|
||||
let obligations = self.nominal_obligations(did, args);
|
||||
self.out.extend(obligations);
|
||||
let upvars = args.as_coroutine_closure().tupled_upvars_ty();
|
||||
return upvars.visit_with(self);
|
||||
}
|
||||
|
||||
ty::FnPtr(_) => {
|
||||
// Let the visitor iterate into the argument/return
|
||||
// types appearing in the fn signature.
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// All of the requirements on type parameters
|
||||
// have already been checked for `impl Trait` in
|
||||
// return position. We do need to check type-alias-impl-trait though.
|
||||
if self.tcx().is_type_alias_impl_trait(def_id) {
|
||||
let obligations = self.nominal_obligations(def_id, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
}
|
||||
|
||||
ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
|
||||
let obligations = self.nominal_obligations(def_id, args);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::Dynamic(data, r, _) => {
|
||||
// WfObject
|
||||
//
|
||||
// Here, we defer WF checking due to higher-ranked
|
||||
// regions. This is perhaps not ideal.
|
||||
self.from_object_ty(t, data, r);
|
||||
|
||||
// FIXME(#27579) RFC also considers adding trait
|
||||
// obligations that don't refer to Self and
|
||||
// checking those
|
||||
|
||||
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
|
||||
|
||||
if !defer_to_coercion {
|
||||
if let Some(principal) = data.principal_def_id() {
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
self.cause(traits::WellFormed(None)),
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inference variables are the complicated case, since we don't
|
||||
// know what type they are. We do two things:
|
||||
//
|
||||
// 1. Check if they have been resolved, and if so proceed with
|
||||
// THAT type.
|
||||
// 2. If not, we've at least simplified things (e.g., we went
|
||||
// from `Vec<$0>: WF` to `$0: WF`), so we can
|
||||
// register a pending obligation and keep
|
||||
// moving. (Goal is that an "inductive hypothesis"
|
||||
// is satisfied to ensure termination.)
|
||||
// See also the comment on `fn obligations`, describing "livelock"
|
||||
// prevention, which happens before this can be reached.
|
||||
ty::Infer(_) => {
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
||||
t.into(),
|
||||
))),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
|
||||
match c.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => {
|
||||
if !c.has_escaping_bound_vars() {
|
||||
let obligations = self.nominal_obligations(uv.def, uv.args);
|
||||
self.out.extend(obligations);
|
||||
|
||||
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::ConstEvaluatable(c),
|
||||
));
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
predicate,
|
||||
));
|
||||
}
|
||||
}
|
||||
ty::ConstKind::Infer(_) => {
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
||||
c.into(),
|
||||
))),
|
||||
));
|
||||
}
|
||||
ty::ConstKind::Expr(_) => {
|
||||
// FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
|
||||
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
|
||||
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
|
||||
// which means that the `DefId` would have been typeck'd elsewhere. However in
|
||||
// the future we may allow directly lowering to `ConstKind::Expr` in which case
|
||||
// we would not be proving bounds we should.
|
||||
|
||||
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::ConstEvaluatable(c),
|
||||
));
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
predicate,
|
||||
));
|
||||
}
|
||||
|
||||
ty::ConstKind::Error(_)
|
||||
| ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Bound(..)
|
||||
| ty::ConstKind::Placeholder(..) => {
|
||||
// These variants are trivially WF, so nothing to do here.
|
||||
}
|
||||
ty::ConstKind::Value(..) => {
|
||||
// FIXME: Enforce that values are structurally-matchable.
|
||||
}
|
||||
}
|
||||
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_predicate(&mut self, _p: <TyCtxt<'tcx> as ty::Interner>::Predicate) -> Self::Result {
|
||||
bug!("predicate should not be checked for well-formedness");
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an object type like `SomeTrait + Send`, computes the lifetime
|
||||
/// bounds that must hold on the elided self type. These are derived
|
||||
/// from the declarations of `SomeTrait`, `Send`, and friends -- if
|
||||
|
|
|
|||
|
|
@ -29,5 +29,5 @@ rustc = [
|
|||
|
||||
[dev-dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
rustc_ast_ir = { path = "../rustc_ast_ir" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_middle::ty::GenericArgsRef;
|
|||
use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::sym;
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_type_ir::ClosureKind;
|
||||
use traits::{translate_args, Reveal};
|
||||
|
||||
use crate::errors::UnexpectedFnPtrAssociatedItem;
|
||||
|
|
@ -296,23 +297,25 @@ fn resolve_associated_item<'tcx>(
|
|||
{
|
||||
match *rcvr_args.type_at(0).kind() {
|
||||
ty::CoroutineClosure(coroutine_closure_def_id, args) => {
|
||||
// If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure,
|
||||
// or `AsyncFnOnce` for a by-mut closure, then construct a new body that
|
||||
// has the right return types.
|
||||
//
|
||||
// Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs
|
||||
// to have its input and output types fixed (`&mut self` and returning
|
||||
// `i16` coroutine kind).
|
||||
if target_kind > args.as_coroutine_closure().kind() {
|
||||
Some(Instance {
|
||||
def: ty::InstanceDef::ConstructCoroutineInClosureShim {
|
||||
coroutine_closure_def_id,
|
||||
target_kind,
|
||||
},
|
||||
args,
|
||||
})
|
||||
} else {
|
||||
Some(Instance::new(coroutine_closure_def_id, args))
|
||||
match (target_kind, args.as_coroutine_closure().kind()) {
|
||||
(ClosureKind::FnOnce | ClosureKind::FnMut, ClosureKind::Fn)
|
||||
| (ClosureKind::FnOnce, ClosureKind::FnMut) => {
|
||||
// If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure,
|
||||
// or `AsyncFnOnce` for a by-mut closure, then construct a new body that
|
||||
// has the right return types.
|
||||
//
|
||||
// Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs
|
||||
// to have its input and output types fixed (`&mut self` and returning
|
||||
// `i16` coroutine kind).
|
||||
Some(Instance {
|
||||
def: ty::InstanceDef::ConstructCoroutineInClosureShim {
|
||||
coroutine_closure_def_id,
|
||||
target_kind,
|
||||
},
|
||||
args,
|
||||
})
|
||||
}
|
||||
_ => Some(Instance::new(coroutine_closure_def_id, args)),
|
||||
}
|
||||
}
|
||||
ty::Closure(closure_def_id, args) => {
|
||||
|
|
|
|||
|
|
@ -369,12 +369,9 @@ rustc_index::newtype_index! {
|
|||
///
|
||||
/// You can get the environment type of a closure using
|
||||
/// `tcx.closure_env_ty()`.
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
|
||||
pub enum ClosureKind {
|
||||
// Warning: Ordering is significant here! The ordering is chosen
|
||||
// because the trait Fn is a subtrait of FnMut and so in turn, and
|
||||
// hence we order it so that Fn < FnMut < FnOnce.
|
||||
Fn,
|
||||
FnMut,
|
||||
FnOnce,
|
||||
|
|
@ -394,8 +391,15 @@ impl ClosureKind {
|
|||
|
||||
/// Returns `true` if a type that impls this closure kind
|
||||
/// must also implement `other`.
|
||||
#[rustfmt::skip]
|
||||
pub fn extends(self, other: ClosureKind) -> bool {
|
||||
self <= other
|
||||
use ClosureKind::*;
|
||||
match (self, other) {
|
||||
(Fn, Fn | FnMut | FnOnce)
|
||||
| (FnMut, FnMut | FnOnce)
|
||||
| (FnOnce, FnOnce) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ impl Rvalue {
|
|||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
||||
Ok(Ty::usize_ty())
|
||||
}
|
||||
Rvalue::NullaryOp(NullOp::DebugAssertions, _) => Ok(Ty::bool_ty()),
|
||||
Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()),
|
||||
Rvalue::Aggregate(ak, ops) => match *ak {
|
||||
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
|
||||
AggregateKind::Tuple => Ok(Ty::new_tuple(
|
||||
|
|
@ -1007,7 +1007,13 @@ pub enum NullOp {
|
|||
/// Returns the offset of a field.
|
||||
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
|
||||
/// cfg!(debug_assertions), but at codegen time
|
||||
DebugAssertions,
|
||||
UbCheck(UbKind),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum UbKind {
|
||||
LanguageUb,
|
||||
LibraryUb,
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ use core::error::Error;
|
|||
use core::fmt;
|
||||
use core::future::Future;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::intrinsics::retag_box_to_raw;
|
||||
use core::iter::FusedIterator;
|
||||
use core::marker::Tuple;
|
||||
use core::marker::Unsize;
|
||||
|
|
@ -1110,8 +1111,16 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
|||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[inline]
|
||||
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
|
||||
let (leaked, alloc) = Box::into_unique(b);
|
||||
(leaked.as_ptr(), alloc)
|
||||
// This is the transition point from `Box` to raw pointers. For Stacked Borrows, these casts
|
||||
// are relevant -- if this is a global allocator Box and we just get the pointer from `b.0`,
|
||||
// it will have `Unique` permission, which is not what we want from a raw pointer. We could
|
||||
// fix that by going through `&mut`, but then if this is *not* a global allocator Box, we'd
|
||||
// be adding uniqueness assertions that we do not want. So for Miri's sake we pass this
|
||||
// pointer through an intrinsic for box-to-raw casts, which can do the right thing wrt the
|
||||
// aliasing model.
|
||||
let b = mem::ManuallyDrop::new(b);
|
||||
let alloc = unsafe { ptr::read(&b.1) };
|
||||
(unsafe { retag_box_to_raw::<T, A>(b.0.as_ptr()) }, alloc)
|
||||
}
|
||||
|
||||
#[unstable(
|
||||
|
|
@ -1122,13 +1131,8 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
|||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn into_unique(b: Self) -> (Unique<T>, A) {
|
||||
// Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a
|
||||
// raw pointer for the type system. Turning it directly into a raw pointer would not be
|
||||
// recognized as "releasing" the unique pointer to permit aliased raw accesses,
|
||||
// so all raw pointer methods have to go through `Box::leak`. Turning *that* to a raw pointer
|
||||
// behaves correctly.
|
||||
let alloc = unsafe { ptr::read(&b.1) };
|
||||
(Unique::from(Box::leak(b)), alloc)
|
||||
let (ptr, alloc) = Box::into_raw_with_allocator(b);
|
||||
unsafe { (Unique::from(&mut *ptr), alloc) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying allocator.
|
||||
|
|
@ -1184,7 +1188,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
|||
where
|
||||
A: 'a,
|
||||
{
|
||||
unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() }
|
||||
unsafe { &mut *Box::into_raw(b) }
|
||||
}
|
||||
|
||||
/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
|
||||
|
|
|
|||
|
|
@ -559,6 +559,30 @@ impl<T> VecDeque<T> {
|
|||
pub fn with_capacity(capacity: usize) -> VecDeque<T> {
|
||||
Self::with_capacity_in(capacity, Global)
|
||||
}
|
||||
|
||||
/// Creates an empty deque with space for at least `capacity` elements.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the capacity exceeds `isize::MAX` _bytes_,
|
||||
/// or if the allocator reports allocation failure.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(try_with_capacity)]
|
||||
/// # #[allow(unused)]
|
||||
/// # fn example() -> Result<(), std::collections::TryReserveError> {
|
||||
/// use std::collections::VecDeque;
|
||||
///
|
||||
/// let deque: VecDeque<u32> = VecDeque::try_with_capacity(10)?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "try_with_capacity", issue = "91913")]
|
||||
pub fn try_with_capacity(capacity: usize) -> Result<VecDeque<T>, TryReserveError> {
|
||||
Ok(VecDeque { head: 0, len: 0, buf: RawVec::try_with_capacity_in(capacity, Global)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A: Allocator> VecDeque<T, A> {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@
|
|||
#![feature(trusted_len)]
|
||||
#![feature(trusted_random_access)]
|
||||
#![feature(try_trait_v2)]
|
||||
#![feature(try_with_capacity)]
|
||||
#![feature(tuple_trait)]
|
||||
#![feature(unchecked_math)]
|
||||
#![feature(unicode_internals)]
|
||||
|
|
|
|||
|
|
@ -17,10 +17,19 @@ use crate::collections::TryReserveErrorKind::*;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// One central function responsible for reporting capacity overflows. This'll
|
||||
// ensure that the code generation related to these panics is minimal as there's
|
||||
// only one location which panics rather than a bunch throughout the module.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
||||
fn capacity_overflow() -> ! {
|
||||
panic!("capacity overflow");
|
||||
}
|
||||
|
||||
enum AllocInit {
|
||||
/// The contents of the new memory are uninitialized.
|
||||
Uninitialized,
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
/// The new memory is guaranteed to be zeroed.
|
||||
Zeroed,
|
||||
}
|
||||
|
|
@ -93,6 +102,8 @@ impl<T> RawVec<T, Global> {
|
|||
/// zero-sized. Note that if `T` is zero-sized this means you will
|
||||
/// *not* get a `RawVec` with the requested capacity.
|
||||
///
|
||||
/// Non-fallible version of `try_with_capacity`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the requested capacity exceeds `isize::MAX` bytes.
|
||||
|
|
@ -104,7 +115,7 @@ impl<T> RawVec<T, Global> {
|
|||
#[must_use]
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self::with_capacity_in(capacity, Global)
|
||||
handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global))
|
||||
}
|
||||
|
||||
/// Like `with_capacity`, but guarantees the buffer is zeroed.
|
||||
|
|
@ -142,7 +153,14 @@ impl<T, A: Allocator> RawVec<T, A> {
|
|||
#[cfg(not(no_global_oom_handling))]
|
||||
#[inline]
|
||||
pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
|
||||
Self::allocate_in(capacity, AllocInit::Uninitialized, alloc)
|
||||
handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc))
|
||||
}
|
||||
|
||||
/// Like `try_with_capacity`, but parameterized over the choice of
|
||||
/// allocator for the returned `RawVec`.
|
||||
#[inline]
|
||||
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
|
||||
Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
|
||||
}
|
||||
|
||||
/// Like `with_capacity_zeroed`, but parameterized over the choice
|
||||
|
|
@ -150,7 +168,7 @@ impl<T, A: Allocator> RawVec<T, A> {
|
|||
#[cfg(not(no_global_oom_handling))]
|
||||
#[inline]
|
||||
pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self {
|
||||
Self::allocate_in(capacity, AllocInit::Zeroed, alloc)
|
||||
handle_reserve(Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc))
|
||||
}
|
||||
|
||||
/// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
|
||||
|
|
@ -179,35 +197,41 @@ impl<T, A: Allocator> RawVec<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self {
|
||||
fn try_allocate_in(
|
||||
capacity: usize,
|
||||
init: AllocInit,
|
||||
alloc: A,
|
||||
) -> Result<Self, TryReserveError> {
|
||||
// Don't allocate here because `Drop` will not deallocate when `capacity` is 0.
|
||||
|
||||
if T::IS_ZST || capacity == 0 {
|
||||
Self::new_in(alloc)
|
||||
Ok(Self::new_in(alloc))
|
||||
} else {
|
||||
// We avoid `unwrap_or_else` here because it bloats the amount of
|
||||
// LLVM IR generated.
|
||||
let layout = match Layout::array::<T>(capacity) {
|
||||
Ok(layout) => layout,
|
||||
Err(_) => capacity_overflow(),
|
||||
Err(_) => return Err(CapacityOverflow.into()),
|
||||
};
|
||||
match alloc_guard(layout.size()) {
|
||||
Ok(_) => {}
|
||||
Err(_) => capacity_overflow(),
|
||||
|
||||
if let Err(err) = alloc_guard(layout.size()) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let result = match init {
|
||||
AllocInit::Uninitialized => alloc.allocate(layout),
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
AllocInit::Zeroed => alloc.allocate_zeroed(layout),
|
||||
};
|
||||
let ptr = match result {
|
||||
Ok(ptr) => ptr,
|
||||
Err(_) => handle_alloc_error(layout),
|
||||
Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()),
|
||||
};
|
||||
|
||||
// Allocators currently return a `NonNull<[u8]>` whose length
|
||||
// matches the size requested. If that ever changes, the capacity
|
||||
// here should change to `ptr.len() / mem::size_of::<T>()`.
|
||||
Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }
|
||||
Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -537,11 +561,11 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
|
|||
// Central function for reserve error handling.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[inline]
|
||||
fn handle_reserve(result: Result<(), TryReserveError>) {
|
||||
fn handle_reserve<T>(result: Result<T, TryReserveError>) -> T {
|
||||
match result.map_err(|e| e.kind()) {
|
||||
Ok(res) => res,
|
||||
Err(CapacityOverflow) => capacity_overflow(),
|
||||
Err(AllocError { layout, .. }) => handle_alloc_error(layout),
|
||||
Ok(()) => { /* yay */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -561,12 +585,3 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// One central function responsible for reporting capacity overflows. This'll
|
||||
// ensure that the code generation related to these panics is minimal as there's
|
||||
// only one location which panics rather than a bunch throughout the module.
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
||||
fn capacity_overflow() -> ! {
|
||||
panic!("capacity overflow");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,13 +105,14 @@ fn zst() {
|
|||
let v: RawVec<ZST> = RawVec::with_capacity_in(100, Global);
|
||||
zst_sanity(&v);
|
||||
|
||||
let v: RawVec<ZST> = RawVec::allocate_in(0, AllocInit::Uninitialized, Global);
|
||||
let v: RawVec<ZST> = RawVec::try_allocate_in(0, AllocInit::Uninitialized, Global).unwrap();
|
||||
zst_sanity(&v);
|
||||
|
||||
let v: RawVec<ZST> = RawVec::allocate_in(100, AllocInit::Uninitialized, Global);
|
||||
let v: RawVec<ZST> = RawVec::try_allocate_in(100, AllocInit::Uninitialized, Global).unwrap();
|
||||
zst_sanity(&v);
|
||||
|
||||
let mut v: RawVec<ZST> = RawVec::allocate_in(usize::MAX, AllocInit::Uninitialized, Global);
|
||||
let mut v: RawVec<ZST> =
|
||||
RawVec::try_allocate_in(usize::MAX, AllocInit::Uninitialized, Global).unwrap();
|
||||
zst_sanity(&v);
|
||||
|
||||
// Check all these operations work as expected with zero-sized elements.
|
||||
|
|
|
|||
|
|
@ -492,6 +492,19 @@ impl String {
|
|||
String { vec: Vec::with_capacity(capacity) }
|
||||
}
|
||||
|
||||
/// Creates a new empty `String` with at least the specified capacity.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Err`] if the capacity exceeds `isize::MAX` bytes,
|
||||
/// or if the memory allocator reports failure.
|
||||
///
|
||||
#[inline]
|
||||
#[unstable(feature = "try_with_capacity", issue = "91913")]
|
||||
pub fn try_with_capacity(capacity: usize) -> Result<String, TryReserveError> {
|
||||
Ok(String { vec: Vec::try_with_capacity(capacity)? })
|
||||
}
|
||||
|
||||
// HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is
|
||||
// required for this method definition, is not available. Since we don't
|
||||
// require this method for testing purposes, I'll just stub it
|
||||
|
|
|
|||
|
|
@ -481,6 +481,22 @@ impl<T> Vec<T> {
|
|||
Self::with_capacity_in(capacity, Global)
|
||||
}
|
||||
|
||||
/// Constructs a new, empty `Vec<T>` with at least the specified capacity.
|
||||
///
|
||||
/// The vector will be able to hold at least `capacity` elements without
|
||||
/// reallocating. This method is allowed to allocate for more elements than
|
||||
/// `capacity`. If `capacity` is 0, the vector will not allocate.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the capacity exceeds `isize::MAX` _bytes_,
|
||||
/// or if the allocator reports allocation failure.
|
||||
#[inline]
|
||||
#[unstable(feature = "try_with_capacity", issue = "91913")]
|
||||
pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
|
||||
Self::try_with_capacity_in(capacity, Global)
|
||||
}
|
||||
|
||||
/// Creates a `Vec<T>` directly from a pointer, a length, and a capacity.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
@ -672,6 +688,24 @@ impl<T, A: Allocator> Vec<T, A> {
|
|||
Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 }
|
||||
}
|
||||
|
||||
/// Constructs a new, empty `Vec<T, A>` with at least the specified capacity
|
||||
/// with the provided allocator.
|
||||
///
|
||||
/// The vector will be able to hold at least `capacity` elements without
|
||||
/// reallocating. This method is allowed to allocate for more elements than
|
||||
/// `capacity`. If `capacity` is 0, the vector will not allocate.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the capacity exceeds `isize::MAX` _bytes_,
|
||||
/// or if the allocator reports allocation failure.
|
||||
#[inline]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "try_with_capacity", issue = "91913")]
|
||||
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
|
||||
Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 })
|
||||
}
|
||||
|
||||
/// Creates a `Vec<T, A>` directly from a pointer, a length, a capacity,
|
||||
/// and an allocator.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#![feature(pattern)]
|
||||
#![feature(trusted_len)]
|
||||
#![feature(try_reserve_kind)]
|
||||
#![feature(try_with_capacity)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(binary_heap_into_iter_sorted)]
|
||||
|
|
|
|||
|
|
@ -723,6 +723,17 @@ fn test_reserve_exact() {
|
|||
assert!(s.capacity() >= 33)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
||||
fn test_try_with_capacity() {
|
||||
let string = String::try_with_capacity(1000).unwrap();
|
||||
assert_eq!(0, string.len());
|
||||
assert!(string.capacity() >= 1000 && string.capacity() <= isize::MAX as usize);
|
||||
|
||||
assert!(String::try_with_capacity(usize::MAX).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
||||
|
|
|
|||
|
|
@ -1694,6 +1694,18 @@ fn test_reserve_exact() {
|
|||
assert!(v.capacity() >= 33)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
||||
fn test_try_with_capacity() {
|
||||
let mut vec: Vec<u32> = Vec::try_with_capacity(5).unwrap();
|
||||
assert_eq!(0, vec.len());
|
||||
assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4);
|
||||
assert!(vec.spare_capacity_mut().len() >= 5);
|
||||
|
||||
assert!(Vec::<u16>::try_with_capacity(isize::MAX as usize + 1).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
||||
|
|
|
|||
|
|
@ -1182,6 +1182,17 @@ fn test_reserve_exact_2() {
|
|||
assert!(v.capacity() >= 33)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
||||
fn test_try_with_capacity() {
|
||||
let vec: VecDeque<u32> = VecDeque::try_with_capacity(5).unwrap();
|
||||
assert_eq!(0, vec.len());
|
||||
assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4);
|
||||
|
||||
assert!(VecDeque::<u16>::try_with_capacity(isize::MAX as usize + 1).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
||||
|
|
|
|||
|
|
@ -605,7 +605,9 @@ impl dyn Any + Send + Sync {
|
|||
#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct TypeId {
|
||||
t: u128,
|
||||
// We avoid using `u128` because that imposes higher alignment requirements on many platforms.
|
||||
// See issue #115620 for more information.
|
||||
t: (u64, u64),
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
@ -637,7 +639,10 @@ impl TypeId {
|
|||
#[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
|
||||
pub const fn of<T: ?Sized + 'static>() -> TypeId {
|
||||
let t: u128 = intrinsics::type_id::<T>();
|
||||
TypeId { t }
|
||||
|
||||
let t1 = (t >> 64) as u64;
|
||||
let t2 = t as u64;
|
||||
TypeId { t: (t1, t2) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -657,7 +662,7 @@ impl hash::Hash for TypeId {
|
|||
// - It is correct to do so -- only hashing a subset of `self` is still
|
||||
// with an `Eq` implementation that considers the entire value, as
|
||||
// ours does.
|
||||
(self.t as u64).hash(state);
|
||||
self.t.1.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@
|
|||
|
||||
use crate::cmp::Ordering;
|
||||
use crate::fmt::{self, Debug, Display};
|
||||
use crate::intrinsics;
|
||||
use crate::marker::{PhantomData, Unsize};
|
||||
use crate::mem::{self, size_of};
|
||||
use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn};
|
||||
|
|
@ -435,8 +434,13 @@ impl<T> Cell<T> {
|
|||
#[inline]
|
||||
#[stable(feature = "move_cell", since = "1.17.0")]
|
||||
pub fn swap(&self, other: &Self) {
|
||||
// This function documents that it *will* panic, and intrinsics::is_nonoverlapping doesn't
|
||||
// do the check in const, so trying to use it here would be inviting unnecessary fragility.
|
||||
fn is_nonoverlapping<T>(src: *const T, dst: *const T) -> bool {
|
||||
intrinsics::is_nonoverlapping(src.cast(), dst.cast(), size_of::<T>(), 1)
|
||||
let src_usize = src.addr();
|
||||
let dst_usize = dst.addr();
|
||||
let diff = src_usize.abs_diff(dst_usize);
|
||||
diff >= size_of::<T>()
|
||||
}
|
||||
|
||||
if ptr::eq(self, other) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char {
|
|||
// SAFETY: the caller must guarantee that `i` is a valid char value.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"invalid value for `char`",
|
||||
(i: u32 = i) => char_try_from_u32(i).is_ok()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -98,12 +98,14 @@ use crate::intrinsics;
|
|||
#[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub const unsafe fn unreachable_unchecked() -> ! {
|
||||
intrinsics::assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"hint::unreachable_unchecked must never be reached",
|
||||
() => false
|
||||
);
|
||||
// SAFETY: the safety contract for `intrinsics::unreachable` must
|
||||
// be upheld by the caller.
|
||||
unsafe {
|
||||
intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false);
|
||||
intrinsics::unreachable()
|
||||
}
|
||||
unsafe { intrinsics::unreachable() }
|
||||
}
|
||||
|
||||
/// Makes a *soundness* promise to the compiler that `cond` holds.
|
||||
|
|
@ -147,6 +149,7 @@ pub const unsafe fn assert_unchecked(cond: bool) {
|
|||
// SAFETY: The caller promised `cond` is true.
|
||||
unsafe {
|
||||
intrinsics::assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"hint::assert_unchecked must never be called when the condition is false",
|
||||
(cond: bool = cond) => cond,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2628,24 +2628,38 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in
|
||||
/// macro expansion.
|
||||
/// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)`
|
||||
/// during monomorphization.
|
||||
///
|
||||
/// This always returns `false` in const eval and Miri. The interpreter provides better
|
||||
/// diagnostics than the checks that this is used to implement. However, this means
|
||||
/// you should only be using this intrinsic to guard requirements that, if violated,
|
||||
/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those
|
||||
/// checks entirely.
|
||||
///
|
||||
/// Since this is evaluated after monomorphization, branching on this value can be used to
|
||||
/// implement debug assertions that are included in the precompiled standard library, but can
|
||||
/// be optimized out by builds that monomorphize the standard library code with debug
|
||||
/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can
|
||||
/// be used to implement debug assertions that are included in the precompiled standard library,
|
||||
/// but can be optimized out by builds that monomorphize the standard library code with debug
|
||||
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
|
||||
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
|
||||
///
|
||||
/// We have separate intrinsics for library UB and language UB because checkers like the const-eval
|
||||
/// interpreter and Miri already implement checks for language UB. Since such checkers do not know
|
||||
/// about library preconditions, checks guarded by this intrinsic let them find more UB.
|
||||
#[rustc_const_unstable(feature = "ub_checks", issue = "none")]
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[inline(always)]
|
||||
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||
pub(crate) const fn debug_assertions() -> bool {
|
||||
pub(crate) const fn check_library_ub() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
}
|
||||
|
||||
/// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)`
|
||||
/// during monomorphization.
|
||||
///
|
||||
/// Since checks implemented at the source level must come strictly before the operation that
|
||||
/// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the
|
||||
/// interpreter's improved diagnostics for the cases that our source-level checks catch.
|
||||
///
|
||||
/// See `check_library_ub` for more information.
|
||||
#[rustc_const_unstable(feature = "ub_checks", issue = "none")]
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[inline(always)]
|
||||
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||
pub(crate) const fn check_language_ub() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
}
|
||||
|
||||
|
|
@ -2695,18 +2709,42 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
/// Retag a box pointer as part of casting it to a raw pointer. This is the `Box` equivalent of
|
||||
/// `(x: &mut T) as *mut T`. The input pointer must be the pointer of a `Box` (passed as raw pointer
|
||||
/// to avoid all questions around move semantics and custom allocators), and `A` must be the `Box`'s
|
||||
/// allocator.
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[rustc_nounwind]
|
||||
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||
#[cfg_attr(bootstrap, inline)]
|
||||
pub unsafe fn retag_box_to_raw<T: ?Sized, A>(ptr: *mut T) -> *mut T {
|
||||
// Miri needs to adjust the provenance, but for regular codegen this is not needed
|
||||
ptr
|
||||
}
|
||||
|
||||
// Some functions are defined here because they accidentally got made
|
||||
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
|
||||
// (`transmute` also falls into this category, but it cannot be wrapped due to the
|
||||
// check that `T` and `U` have the same size.)
|
||||
|
||||
/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
|
||||
/// and only at runtime.
|
||||
/// Check that the preconditions of an unsafe function are followed. The check is enabled at
|
||||
/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
|
||||
/// checks implemented with this macro for language UB are always ignored.
|
||||
///
|
||||
/// This macro should be called as
|
||||
/// `assert_unsafe_precondition!((expr => name: Type, expr => name: Type) => Expression)`
|
||||
/// where each `expr` will be evaluated and passed in as function argument `name: Type`. Then all
|
||||
/// those arguments are passed to a function via [`const_eval_select`].
|
||||
/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)`
|
||||
/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all
|
||||
/// those arguments are passed to a function with the body `check_expr`.
|
||||
/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB
|
||||
/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation
|
||||
/// of a documented library precondition that does not *immediately* lead to language UB.
|
||||
///
|
||||
/// If `check_library_ub` is used but the check is actually guarding language UB, the check will
|
||||
/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice
|
||||
/// diagnostic, but our ability to detect UB is unchanged.
|
||||
/// But if `check_language_ub` is used when the check is actually for library UB, the check is
|
||||
/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the
|
||||
/// library UB, the backtrace Miri reports may be far removed from original cause.
|
||||
///
|
||||
/// These checks are behind a condition which is evaluated at codegen time, not expansion time like
|
||||
/// [`debug_assert`]. This means that a standard library built with optimizations and debug
|
||||
|
|
@ -2715,31 +2753,25 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
|
|||
/// this macro, that monomorphization will contain the check.
|
||||
///
|
||||
/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
|
||||
/// implementation to mitigate their compile-time overhead. The runtime function that we
|
||||
/// [`const_eval_select`] to is monomorphic, `#[inline(never)]`, and `#[rustc_nounwind]`. That
|
||||
/// combination of properties ensures that the code for the checks is only compiled once, and has a
|
||||
/// minimal impact on the caller's code size.
|
||||
/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to
|
||||
/// this structure:
|
||||
/// ```ignore (pseudocode)
|
||||
/// if ::core::intrinsics::check_language_ub() {
|
||||
/// precondition_check(args)
|
||||
/// }
|
||||
/// ```
|
||||
/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and
|
||||
/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is
|
||||
/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in
|
||||
/// MIR, but *can* be inlined and fully optimized by a codegen backend.
|
||||
///
|
||||
/// Callers should also avoid introducing any other `let` bindings or any code outside this macro in
|
||||
/// Callers should avoid introducing any other `let` bindings or any code outside this macro in
|
||||
/// order to call it. Since the precompiled standard library is built with full debuginfo and these
|
||||
/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
|
||||
/// debuginfo to have a measurable compile-time impact on debug builds.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Invoking this macro is only sound if the following code is already UB when the passed
|
||||
/// expression evaluates to false.
|
||||
///
|
||||
/// This macro expands to a check at runtime if debug_assertions is set. It has no effect at
|
||||
/// compile time, but the semantics of the contained `const_eval_select` must be the same at
|
||||
/// runtime and at compile time. Thus if the expression evaluates to false, this macro produces
|
||||
/// different behavior at compile time and at runtime, and invoking it is incorrect.
|
||||
///
|
||||
/// So in a sense it is UB if this macro is useful, but we expect callers of `unsafe fn` to make
|
||||
/// the occasional mistake, and this check should help them figure things out.
|
||||
#[allow_internal_unstable(const_eval_select, delayed_debug_assertions)] // permit this to be called in stably-const fn
|
||||
#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn
|
||||
macro_rules! assert_unsafe_precondition {
|
||||
($message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
|
||||
($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
|
||||
{
|
||||
// #[cfg(bootstrap)] (this comment)
|
||||
// When the standard library is compiled with debug assertions, we want the check to inline for better performance.
|
||||
|
|
@ -2761,17 +2793,17 @@ macro_rules! assert_unsafe_precondition {
|
|||
#[cfg_attr(not(bootstrap), rustc_no_mir_inline)]
|
||||
#[cfg_attr(not(bootstrap), inline)]
|
||||
#[rustc_nounwind]
|
||||
fn precondition_check($($name:$ty),*) {
|
||||
#[rustc_const_unstable(feature = "ub_checks", issue = "none")]
|
||||
const fn precondition_check($($name:$ty),*) {
|
||||
if !$e {
|
||||
::core::panicking::panic_nounwind(
|
||||
concat!("unsafe precondition(s) violated: ", $message)
|
||||
);
|
||||
}
|
||||
}
|
||||
const fn comptime($(_:$ty),*) {}
|
||||
|
||||
if ::core::intrinsics::debug_assertions() {
|
||||
::core::intrinsics::const_eval_select(($($arg,)*), comptime, precondition_check);
|
||||
if ::core::intrinsics::$kind() {
|
||||
precondition_check($($arg,)*);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -2780,32 +2812,60 @@ pub(crate) use assert_unsafe_precondition;
|
|||
|
||||
/// Checks whether `ptr` is properly aligned with respect to
|
||||
/// `align_of::<T>()`.
|
||||
///
|
||||
/// In `const` this is approximate and can fail spuriously. It is primarily intended
|
||||
/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
|
||||
/// check is anyway not executed in `const`.
|
||||
#[inline]
|
||||
pub(crate) fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool {
|
||||
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool {
|
||||
!ptr.is_null() && ptr.is_aligned_to(align)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_valid_allocation_size(size: usize, len: usize) -> bool {
|
||||
pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool {
|
||||
let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size };
|
||||
len <= max_len
|
||||
}
|
||||
|
||||
/// Checks whether the regions of memory starting at `src` and `dst` of size
|
||||
/// `count * size` do *not* overlap.
|
||||
///
|
||||
/// Note that in const-eval this function just returns `true` and therefore must
|
||||
/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
|
||||
#[inline]
|
||||
pub(crate) fn is_nonoverlapping(src: *const (), dst: *const (), size: usize, count: usize) -> bool {
|
||||
let src_usize = src.addr();
|
||||
let dst_usize = dst.addr();
|
||||
let Some(size) = size.checked_mul(count) else {
|
||||
crate::panicking::panic_nounwind(
|
||||
"is_nonoverlapping: `size_of::<T>() * count` overflows a usize",
|
||||
)
|
||||
};
|
||||
let diff = src_usize.abs_diff(dst_usize);
|
||||
// If the absolute distance between the ptrs is at least as big as the size of the buffer,
|
||||
// they do not overlap.
|
||||
diff >= size
|
||||
pub(crate) const fn is_nonoverlapping(
|
||||
src: *const (),
|
||||
dst: *const (),
|
||||
size: usize,
|
||||
count: usize,
|
||||
) -> bool {
|
||||
#[inline]
|
||||
fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool {
|
||||
let src_usize = src.addr();
|
||||
let dst_usize = dst.addr();
|
||||
let Some(size) = size.checked_mul(count) else {
|
||||
crate::panicking::panic_nounwind(
|
||||
"is_nonoverlapping: `size_of::<T>() * count` overflows a usize",
|
||||
)
|
||||
};
|
||||
let diff = src_usize.abs_diff(dst_usize);
|
||||
// If the absolute distance between the ptrs is at least as big as the size of the buffer,
|
||||
// they do not overlap.
|
||||
diff >= size
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// SAFETY: This function's precondition is equivalent to that of `const_eval_select`.
|
||||
// Programs which do not execute UB will only see this function return `true`, which makes the
|
||||
// const and runtime implementation indistinguishable.
|
||||
unsafe {
|
||||
const_eval_select((src, dst, size, count), comptime, runtime)
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
|
||||
|
|
@ -2906,25 +2966,25 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
|
|||
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
|
||||
}
|
||||
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||
and the specified memory ranges do not overlap",
|
||||
(
|
||||
src: *const () = src as *const (),
|
||||
dst: *mut () = dst as *mut (),
|
||||
size: usize = size_of::<T>(),
|
||||
align: usize = align_of::<T>(),
|
||||
count: usize = count,
|
||||
) =>
|
||||
is_aligned_and_not_null(src, align)
|
||||
&& is_aligned_and_not_null(dst, align)
|
||||
&& is_nonoverlapping(src, dst, size, count)
|
||||
);
|
||||
|
||||
// SAFETY: the safety contract for `copy_nonoverlapping` must be
|
||||
// upheld by the caller.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||
and the specified memory ranges do not overlap",
|
||||
(
|
||||
src: *const () = src as *const (),
|
||||
dst: *mut () = dst as *mut (),
|
||||
size: usize = size_of::<T>(),
|
||||
align: usize = align_of::<T>(),
|
||||
count: usize = count,
|
||||
) =>
|
||||
is_aligned_and_not_null(src, align)
|
||||
&& is_aligned_and_not_null(dst, align)
|
||||
&& is_nonoverlapping(src, dst, size, count)
|
||||
);
|
||||
copy_nonoverlapping(src, dst, count)
|
||||
}
|
||||
unsafe { copy_nonoverlapping(src, dst, count) }
|
||||
}
|
||||
|
||||
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
|
||||
|
|
@ -3011,6 +3071,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
|||
// SAFETY: the safety contract for `copy` must be upheld by the caller.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||
and the specified memory ranges do not overlap",
|
||||
(
|
||||
|
|
@ -3091,6 +3152,7 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
|
|||
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::write_bytes requires that the destination pointer is aligned and non-null",
|
||||
(
|
||||
addr: *const () = dst as *const (),
|
||||
|
|
@ -3120,6 +3182,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
|
|||
|
||||
const fn compiletime(_ptr: *const (), _align: usize) {}
|
||||
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// SAFETY: the extra behavior at runtime is for UB checks only.
|
||||
unsafe {
|
||||
const_eval_select((ptr, align), compiletime, runtime);
|
||||
|
|
|
|||
|
|
@ -1125,22 +1125,6 @@ impl<T> MaybeUninit<T> {
|
|||
// unlike copy_from_slice this does not call clone_from_slice on the slice
|
||||
// this is because `MaybeUninit<T: Clone>` does not implement Clone.
|
||||
|
||||
struct Guard<'a, T> {
|
||||
slice: &'a mut [MaybeUninit<T>],
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for Guard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
let initialized_part = &mut self.slice[..self.initialized];
|
||||
// SAFETY: this raw slice will contain only initialized objects
|
||||
// that's why, it is allowed to drop it.
|
||||
unsafe {
|
||||
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(this.len(), src.len(), "destination and source slices have different lengths");
|
||||
// NOTE: We need to explicitly slice them to the same length
|
||||
// for bounds checking to be elided, and the optimizer will
|
||||
|
|
@ -1162,6 +1146,151 @@ impl<T> MaybeUninit<T> {
|
|||
unsafe { MaybeUninit::slice_assume_init_mut(this) }
|
||||
}
|
||||
|
||||
/// Fills `this` with elements by cloning `value`, returning a mutable reference to the now
|
||||
/// initialized contents of `this`.
|
||||
/// Any previously initialized elements will not be dropped.
|
||||
///
|
||||
/// This is similar to [`slice::fill`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if any call to `Clone` panics.
|
||||
///
|
||||
/// If such a panic occurs, any elements previously initialized during this operation will be
|
||||
/// dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Fill an uninit vec with 1.
|
||||
/// ```
|
||||
/// #![feature(maybe_uninit_fill)]
|
||||
/// use std::mem::MaybeUninit;
|
||||
///
|
||||
/// let mut buf = vec![MaybeUninit::uninit(); 10];
|
||||
/// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1);
|
||||
/// assert_eq!(initialized, &mut [1; 10]);
|
||||
/// ```
|
||||
#[doc(alias = "memset")]
|
||||
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
|
||||
pub fn fill<'a>(this: &'a mut [MaybeUninit<T>], value: T) -> &'a mut [T]
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
SpecFill::spec_fill(this, value);
|
||||
// SAFETY: Valid elements have just been filled into `this` so it is initialized
|
||||
unsafe { MaybeUninit::slice_assume_init_mut(this) }
|
||||
}
|
||||
|
||||
/// Fills `this` with elements returned by calling a closure repeatedly.
|
||||
///
|
||||
/// This method uses a closure to create new values. If you'd rather `Clone` a given value, use
|
||||
/// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can
|
||||
/// pass [`Default::default`] as the argument.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if any call to the provided closure panics.
|
||||
///
|
||||
/// If such a panic occurs, any elements previously initialized during this operation will be
|
||||
/// dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Fill an uninit vec with the default value.
|
||||
/// ```
|
||||
/// #![feature(maybe_uninit_fill)]
|
||||
/// use std::mem::MaybeUninit;
|
||||
///
|
||||
/// let mut buf = vec![MaybeUninit::<i32>::uninit(); 10];
|
||||
/// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default);
|
||||
/// assert_eq!(initialized, &mut [0; 10]);
|
||||
/// ```
|
||||
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
|
||||
pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit<T>], mut f: F) -> &'a mut [T]
|
||||
where
|
||||
F: FnMut() -> T,
|
||||
{
|
||||
let mut guard = Guard { slice: this, initialized: 0 };
|
||||
|
||||
for element in guard.slice.iter_mut() {
|
||||
element.write(f());
|
||||
guard.initialized += 1;
|
||||
}
|
||||
|
||||
super::forget(guard);
|
||||
|
||||
// SAFETY: Valid elements have just been written into `this` so it is initialized
|
||||
unsafe { MaybeUninit::slice_assume_init_mut(this) }
|
||||
}
|
||||
|
||||
/// Fills `this` with elements yielded by an iterator until either all elements have been
|
||||
/// initialized or the iterator is empty.
|
||||
///
|
||||
/// Returns two slices. The first slice contains the initialized portion of the original slice.
|
||||
/// The second slice is the still-uninitialized remainder of the original slice.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the iterator's `next` function panics.
|
||||
///
|
||||
/// If such a panic occurs, any elements previously initialized during this operation will be
|
||||
/// dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Fill an uninit vec with a cycling iterator.
|
||||
/// ```
|
||||
/// #![feature(maybe_uninit_fill)]
|
||||
/// use std::mem::MaybeUninit;
|
||||
///
|
||||
/// let mut buf = vec![MaybeUninit::uninit(); 5];
|
||||
///
|
||||
/// let iter = [1, 2, 3].into_iter().cycle();
|
||||
/// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter);
|
||||
///
|
||||
/// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]);
|
||||
/// assert_eq!(0, remainder.len());
|
||||
/// ```
|
||||
///
|
||||
/// Fill an uninit vec, but not completely.
|
||||
/// ```
|
||||
/// #![feature(maybe_uninit_fill)]
|
||||
/// use std::mem::MaybeUninit;
|
||||
///
|
||||
/// let mut buf = vec![MaybeUninit::uninit(); 5];
|
||||
/// let iter = [1, 2];
|
||||
/// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter);
|
||||
///
|
||||
/// assert_eq!(initialized, &mut [1, 2]);
|
||||
/// assert_eq!(remainder.len(), 3);
|
||||
/// ```
|
||||
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
|
||||
pub fn fill_from<'a, I>(
|
||||
this: &'a mut [MaybeUninit<T>],
|
||||
it: I,
|
||||
) -> (&'a mut [T], &'a mut [MaybeUninit<T>])
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
let iter = it.into_iter();
|
||||
let mut guard = Guard { slice: this, initialized: 0 };
|
||||
|
||||
for (element, val) in guard.slice.iter_mut().zip(iter) {
|
||||
element.write(val);
|
||||
guard.initialized += 1;
|
||||
}
|
||||
|
||||
let initialized_len = guard.initialized;
|
||||
super::forget(guard);
|
||||
|
||||
// SAFETY: guard.initialized <= this.len()
|
||||
let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) };
|
||||
|
||||
// SAFETY: Valid elements have just been written into `init`, so that portion
|
||||
// of `this` is initialized.
|
||||
(unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder)
|
||||
}
|
||||
|
||||
/// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes.
|
||||
///
|
||||
/// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still
|
||||
|
|
@ -1315,3 +1444,44 @@ impl<T, const N: usize> [MaybeUninit<T>; N] {
|
|||
unsafe { intrinsics::transmute_unchecked(self) }
|
||||
}
|
||||
}
|
||||
|
||||
struct Guard<'a, T> {
|
||||
slice: &'a mut [MaybeUninit<T>],
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for Guard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
let initialized_part = &mut self.slice[..self.initialized];
|
||||
// SAFETY: this raw sub-slice will contain only initialized objects.
|
||||
unsafe {
|
||||
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SpecFill<T> {
|
||||
fn spec_fill(&mut self, value: T);
|
||||
}
|
||||
|
||||
impl<T: Clone> SpecFill<T> for [MaybeUninit<T>] {
|
||||
default fn spec_fill(&mut self, value: T) {
|
||||
let mut guard = Guard { slice: self, initialized: 0 };
|
||||
|
||||
if let Some((last, elems)) = guard.slice.split_last_mut() {
|
||||
for el in elems {
|
||||
el.write(value.clone());
|
||||
guard.initialized += 1;
|
||||
}
|
||||
|
||||
last.write(value);
|
||||
}
|
||||
super::forget(guard);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> SpecFill<T> for [MaybeUninit<T>] {
|
||||
fn spec_fill(&mut self, value: T) {
|
||||
self.fill(MaybeUninit::new(value));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,8 +328,9 @@ where
|
|||
// SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable.
|
||||
unsafe {
|
||||
intrinsics::assert_unsafe_precondition!(
|
||||
"NonZero::new_unchecked requires the argument to be non-zero",
|
||||
() => false,
|
||||
check_language_ub,
|
||||
"NonZero::new_unchecked requires the argument to be non-zero",
|
||||
() => false,
|
||||
);
|
||||
intrinsics::unreachable()
|
||||
}
|
||||
|
|
@ -367,8 +368,9 @@ where
|
|||
// SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable.
|
||||
unsafe {
|
||||
intrinsics::assert_unsafe_precondition!(
|
||||
"NonZero::from_mut_unchecked requires the argument to dereference as non-zero",
|
||||
() => false,
|
||||
check_library_ub,
|
||||
"NonZero::from_mut_unchecked requires the argument to dereference as non-zero",
|
||||
() => false,
|
||||
);
|
||||
intrinsics::unreachable()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2972,6 +2972,7 @@ macro_rules! uint_impl {
|
|||
#[inline(always)]
|
||||
#[rustc_const_stable(feature = "const_max_value", since = "1.32.0")]
|
||||
#[deprecated(since = "TBD", note = "replaced by the `MIN` associated constant on this type")]
|
||||
#[rustc_diagnostic_item = concat!(stringify!($SelfT), "_legacy_fn_min_value")]
|
||||
pub const fn min_value() -> Self { Self::MIN }
|
||||
|
||||
/// New code should prefer to use
|
||||
|
|
@ -2983,6 +2984,7 @@ macro_rules! uint_impl {
|
|||
#[inline(always)]
|
||||
#[rustc_const_stable(feature = "const_max_value", since = "1.32.0")]
|
||||
#[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on this type")]
|
||||
#[rustc_diagnostic_item = concat!(stringify!($SelfT), "_legacy_fn_max_value")]
|
||||
pub const fn max_value() -> Self { Self::MAX }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::intrinsics::{unchecked_add, unchecked_sub};
|
||||
use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub};
|
||||
use crate::iter::{FusedIterator, TrustedLen};
|
||||
use crate::num::NonZero;
|
||||
|
||||
|
|
@ -19,9 +19,10 @@ impl IndexRange {
|
|||
/// - `start <= end`
|
||||
#[inline]
|
||||
pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
|
||||
crate::panic::debug_assert_nounwind!(
|
||||
start <= end,
|
||||
"IndexRange::new_unchecked requires `start <= end`"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"IndexRange::new_unchecked requires `start <= end`",
|
||||
(start: usize = start, end: usize = end) => start <= end,
|
||||
);
|
||||
IndexRange { start, end }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,43 +139,6 @@ pub macro unreachable_2021 {
|
|||
),
|
||||
}
|
||||
|
||||
/// Like `assert_unsafe_precondition!` the defining features of this macro are that its
|
||||
/// checks are enabled when they are monomorphized with debug assertions enabled, and upon failure
|
||||
/// a non-unwinding panic is launched so that failures cannot compromise unwind safety.
|
||||
///
|
||||
/// But there are many differences from `assert_unsafe_precondition!`. This macro does not use
|
||||
/// `const_eval_select` internally, and therefore it is sound to call this with an expression
|
||||
/// that evaluates to `false`. Also unlike `assert_unsafe_precondition!` the condition being
|
||||
/// checked here is not put in an outlined function. If the check compiles to a lot of IR, this
|
||||
/// can cause code bloat if the check is monomorphized many times. But it also means that the checks
|
||||
/// from this macro can be deduplicated or otherwise optimized out.
|
||||
///
|
||||
/// In general, this macro should be used to check all public-facing preconditions. But some
|
||||
/// preconditions may be called too often or instantiated too often to make the overhead of the
|
||||
/// checks tolerable. In such cases, place `#[cfg(debug_assertions)]` on the macro call. That will
|
||||
/// disable the check in our precompiled standard library, but if a user wishes, they can still
|
||||
/// enable the check by recompiling the standard library with debug assertions enabled.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "panic_internals", issue = "none")]
|
||||
#[allow_internal_unstable(panic_internals, delayed_debug_assertions)]
|
||||
#[rustc_macro_transparency = "semitransparent"]
|
||||
pub macro debug_assert_nounwind {
|
||||
($cond:expr $(,)?) => {
|
||||
if $crate::intrinsics::debug_assertions() {
|
||||
if !$cond {
|
||||
$crate::panicking::panic_nounwind($crate::concat!("assertion failed: ", $crate::stringify!($cond)));
|
||||
}
|
||||
}
|
||||
},
|
||||
($cond:expr, $message:expr) => {
|
||||
if $crate::intrinsics::debug_assertions() {
|
||||
if !$cond {
|
||||
$crate::panicking::panic_nounwind($message);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/// An internal trait used by std to pass data from std to `panic_unwind` and
|
||||
/// other panic runtimes. Not intended to be stabilized any time soon, do not
|
||||
/// use.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::convert::{TryFrom, TryInto};
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::intrinsics::assert_unsafe_precondition;
|
||||
use crate::num::NonZero;
|
||||
use crate::{cmp, fmt, hash, mem, num};
|
||||
|
||||
|
|
@ -77,9 +79,10 @@ impl Alignment {
|
|||
#[inline]
|
||||
pub const unsafe fn new_unchecked(align: usize) -> Self {
|
||||
#[cfg(debug_assertions)]
|
||||
crate::panic::debug_assert_nounwind!(
|
||||
align.is_power_of_two(),
|
||||
"Alignment::new_unchecked requires a power of two"
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"Alignment::new_unchecked requires a power of two",
|
||||
(align: usize = align) => align.is_power_of_two()
|
||||
);
|
||||
|
||||
// SAFETY: By precondition, this must be a power of two, and
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ impl<T: ?Sized> *const T {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// on bootstrap bump, remove unsafe block
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))]
|
||||
// SAFETY: The two versions are equivalent at runtime.
|
||||
unsafe {
|
||||
const_eval_select((self as *const u8,), const_impl, runtime_impl)
|
||||
|
|
@ -809,18 +810,31 @@ impl<T: ?Sized> *const T {
|
|||
where
|
||||
T: Sized,
|
||||
{
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// SAFETY: The comparison has no side-effects, and the intrinsic
|
||||
// does this check internally in the CTFE implementation.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
"ptr::sub_ptr requires `self >= origin`",
|
||||
(
|
||||
this: *const () = self as *const (),
|
||||
origin: *const () = origin as *const (),
|
||||
) => this >= origin
|
||||
)
|
||||
};
|
||||
const fn runtime_ptr_ge(this: *const (), origin: *const ()) -> bool {
|
||||
fn runtime(this: *const (), origin: *const ()) -> bool {
|
||||
this >= origin
|
||||
}
|
||||
const fn comptime(_: *const (), _: *const ()) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))]
|
||||
// on bootstrap bump, remove unsafe block
|
||||
// SAFETY: This function is only used to provide the same check that the const eval
|
||||
// interpreter does at runtime.
|
||||
unsafe {
|
||||
intrinsics::const_eval_select((this, origin), comptime, runtime)
|
||||
}
|
||||
}
|
||||
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::sub_ptr requires `self >= origin`",
|
||||
(
|
||||
this: *const () = self as *const (),
|
||||
origin: *const () = origin as *const (),
|
||||
) => runtime_ptr_ge(this, origin)
|
||||
);
|
||||
|
||||
let pointee_size = mem::size_of::<T>();
|
||||
assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
|
||||
|
|
@ -1628,12 +1642,12 @@ impl<T: ?Sized> *const T {
|
|||
#[inline]
|
||||
const fn const_impl(ptr: *const (), align: usize) -> bool {
|
||||
// We can't use the address of `self` in a `const fn`, so we use `align_offset` instead.
|
||||
// The cast to `()` is used to
|
||||
// 1. deal with fat pointers; and
|
||||
// 2. ensure that `align_offset` doesn't actually try to compute an offset.
|
||||
ptr.align_offset(align) == 0
|
||||
}
|
||||
|
||||
// The cast to `()` is used to
|
||||
// 1. deal with fat pointers; and
|
||||
// 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset.
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// SAFETY: The two versions are equivalent at runtime.
|
||||
unsafe {
|
||||
|
|
|
|||
|
|
@ -1019,25 +1019,21 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
|||
};
|
||||
}
|
||||
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// SAFETY: the caller must guarantee that `x` and `y` are
|
||||
// valid for writes and properly aligned.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
"ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||
and the specified memory ranges do not overlap",
|
||||
(
|
||||
x: *mut () = x as *mut (),
|
||||
y: *mut () = y as *mut (),
|
||||
size: usize = size_of::<T>(),
|
||||
align: usize = align_of::<T>(),
|
||||
count: usize = count,
|
||||
) =>
|
||||
is_aligned_and_not_null(x, align)
|
||||
&& is_aligned_and_not_null(y, align)
|
||||
&& is_nonoverlapping(x, y, size, count)
|
||||
);
|
||||
}
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||
and the specified memory ranges do not overlap",
|
||||
(
|
||||
x: *mut () = x as *mut (),
|
||||
y: *mut () = y as *mut (),
|
||||
size: usize = size_of::<T>(),
|
||||
align: usize = align_of::<T>(),
|
||||
count: usize = count,
|
||||
) =>
|
||||
is_aligned_and_not_null(x, align)
|
||||
&& is_aligned_and_not_null(y, align)
|
||||
&& is_nonoverlapping(x, y, size, count)
|
||||
);
|
||||
|
||||
// Split up the slice into small power-of-two-sized chunks that LLVM is able
|
||||
// to vectorize (unless it's a special type with more-than-pointer alignment,
|
||||
|
|
@ -1125,6 +1121,7 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
|
|||
// allocated object.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::replace requires that the pointer argument is aligned and non-null",
|
||||
(
|
||||
addr: *const () = dst as *const (),
|
||||
|
|
@ -1277,6 +1274,7 @@ pub const unsafe fn read<T>(src: *const T) -> T {
|
|||
unsafe {
|
||||
#[cfg(debug_assertions)] // Too expensive to always enable (for now?)
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::read requires that the pointer argument is aligned and non-null",
|
||||
(
|
||||
addr: *const () = src as *const (),
|
||||
|
|
@ -1485,6 +1483,7 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
|||
unsafe {
|
||||
#[cfg(debug_assertions)] // Too expensive to always enable (for now?)
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::write requires that the pointer argument is aligned and non-null",
|
||||
(
|
||||
addr: *mut () = dst as *mut (),
|
||||
|
|
@ -1656,6 +1655,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
|||
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::read_volatile requires that the pointer argument is aligned and non-null",
|
||||
(
|
||||
addr: *const () = src as *const (),
|
||||
|
|
@ -1734,6 +1734,7 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
|||
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"ptr::write_volatile requires that the pointer argument is aligned and non-null",
|
||||
(
|
||||
addr: *mut () = dst as *mut (),
|
||||
|
|
|
|||
|
|
@ -1900,12 +1900,12 @@ impl<T: ?Sized> *mut T {
|
|||
#[inline]
|
||||
const fn const_impl(ptr: *mut (), align: usize) -> bool {
|
||||
// We can't use the address of `self` in a `const fn`, so we use `align_offset` instead.
|
||||
// The cast to `()` is used to
|
||||
// 1. deal with fat pointers; and
|
||||
// 2. ensure that `align_offset` doesn't actually try to compute an offset.
|
||||
ptr.align_offset(align) == 0
|
||||
}
|
||||
|
||||
// The cast to `()` is used to
|
||||
// 1. deal with fat pointers; and
|
||||
// 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset.
|
||||
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||
// SAFETY: The two versions are equivalent at runtime.
|
||||
unsafe {
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ impl<T: ?Sized> NonNull<T> {
|
|||
// SAFETY: the caller must guarantee that `ptr` is non-null.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"NonNull::new_unchecked requires that the pointer is non-null",
|
||||
(ptr: *mut () = ptr as *mut ()) => !ptr.is_null()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
//! Indexing implementations for `[T]`.
|
||||
|
||||
use crate::intrinsics::assert_unsafe_precondition;
|
||||
use crate::intrinsics::const_eval_select;
|
||||
use crate::intrinsics::unchecked_sub;
|
||||
use crate::ops;
|
||||
use crate::panic::debug_assert_nounwind;
|
||||
use crate::ptr;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
@ -230,9 +230,10 @@ unsafe impl<T> SliceIndex<[T]> for usize {
|
|||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
|
||||
debug_assert_nounwind!(
|
||||
self < slice.len(),
|
||||
"slice::get_unchecked requires that the index is within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"slice::get_unchecked requires that the index is within the slice",
|
||||
(this: usize = self, len: usize = slice.len()) => this < len
|
||||
);
|
||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||
|
|
@ -248,9 +249,10 @@ unsafe impl<T> SliceIndex<[T]> for usize {
|
|||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
|
||||
debug_assert_nounwind!(
|
||||
self < slice.len(),
|
||||
"slice::get_unchecked_mut requires that the index is within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::get_unchecked_mut requires that the index is within the slice",
|
||||
(this: usize = self, len: usize = slice.len()) => this < len
|
||||
);
|
||||
// SAFETY: see comments for `get_unchecked` above.
|
||||
unsafe { slice.as_mut_ptr().add(self) }
|
||||
|
|
@ -297,9 +299,10 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
|
|||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
debug_assert_nounwind!(
|
||||
self.end() <= slice.len(),
|
||||
"slice::get_unchecked requires that the index is within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::get_unchecked requires that the index is within the slice",
|
||||
(end: usize = self.end(), len: usize = slice.len()) => end <= len
|
||||
);
|
||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||
|
|
@ -310,9 +313,10 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
|
|||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
debug_assert_nounwind!(
|
||||
self.end() <= slice.len(),
|
||||
"slice::get_unchecked_mut requires that the index is within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::get_unchecked_mut requires that the index is within the slice",
|
||||
(end: usize = self.end(), len: usize = slice.len()) => end <= len
|
||||
);
|
||||
|
||||
// SAFETY: see comments for `get_unchecked` above.
|
||||
|
|
@ -367,9 +371,14 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
|||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||
debug_assert_nounwind!(
|
||||
self.end >= self.start && self.end <= slice.len(),
|
||||
"slice::get_unchecked requires that the range is within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::get_unchecked requires that the range is within the slice",
|
||||
(
|
||||
start: usize = self.start,
|
||||
end: usize = self.end,
|
||||
len: usize = slice.len()
|
||||
) => end >= start && end <= len
|
||||
);
|
||||
|
||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||
|
|
@ -384,9 +393,14 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
|
|||
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||
debug_assert_nounwind!(
|
||||
self.end >= self.start && self.end <= slice.len(),
|
||||
"slice::get_unchecked_mut requires that the range is within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::get_unchecked_mut requires that the range is within the slice",
|
||||
(
|
||||
start: usize = self.start,
|
||||
end: usize = self.end,
|
||||
len: usize = slice.len()
|
||||
) => end >= start && end <= len
|
||||
);
|
||||
// SAFETY: see comments for `get_unchecked` above.
|
||||
unsafe {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@
|
|||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::fmt;
|
||||
use crate::hint;
|
||||
use crate::intrinsics::assert_unsafe_precondition;
|
||||
use crate::intrinsics::exact_div;
|
||||
use crate::mem::{self, SizedTypeProperties};
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::{Bound, OneSidedRange, Range, RangeBounds};
|
||||
use crate::panic::debug_assert_nounwind;
|
||||
use crate::ptr;
|
||||
use crate::simd::{self, Simd};
|
||||
use crate::slice;
|
||||
|
|
@ -945,9 +945,14 @@ impl<T> [T] {
|
|||
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
|
||||
debug_assert_nounwind!(
|
||||
a < self.len() && b < self.len(),
|
||||
"slice::swap_unchecked requires that the indices are within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::swap_unchecked requires that the indices are within the slice",
|
||||
(
|
||||
len: usize = self.len(),
|
||||
a: usize = a,
|
||||
b: usize = b,
|
||||
) => a < len && b < len,
|
||||
);
|
||||
|
||||
let ptr = self.as_mut_ptr();
|
||||
|
|
@ -1285,9 +1290,10 @@ impl<T> [T] {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] {
|
||||
debug_assert_nounwind!(
|
||||
N != 0 && self.len() % N == 0,
|
||||
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks"
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
|
||||
(n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0,
|
||||
);
|
||||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
||||
let new_len = unsafe { exact_div(self.len(), N) };
|
||||
|
|
@ -1439,9 +1445,10 @@ impl<T> [T] {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] {
|
||||
debug_assert_nounwind!(
|
||||
N != 0 && self.len() % N == 0,
|
||||
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks"
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
|
||||
(n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0
|
||||
);
|
||||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
||||
let new_len = unsafe { exact_div(self.len(), N) };
|
||||
|
|
@ -1971,9 +1978,10 @@ impl<T> [T] {
|
|||
let len = self.len();
|
||||
let ptr = self.as_ptr();
|
||||
|
||||
debug_assert_nounwind!(
|
||||
mid <= len,
|
||||
"slice::split_at_unchecked requires the index to be within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::split_at_unchecked requires the index to be within the slice",
|
||||
(mid: usize = mid, len: usize = len) => mid <= len,
|
||||
);
|
||||
|
||||
// SAFETY: Caller has to check that `0 <= mid <= self.len()`
|
||||
|
|
@ -2021,9 +2029,10 @@ impl<T> [T] {
|
|||
let len = self.len();
|
||||
let ptr = self.as_mut_ptr();
|
||||
|
||||
debug_assert_nounwind!(
|
||||
mid <= len,
|
||||
"slice::split_at_mut_unchecked requires the index to be within the slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"slice::split_at_mut_unchecked requires the index to be within the slice",
|
||||
(mid: usize = mid, len: usize = len) => mid <= len,
|
||||
);
|
||||
|
||||
// SAFETY: Caller has to check that `0 <= mid <= self.len()`.
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
|
|||
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
|
||||
(
|
||||
data: *mut () = data as *mut (),
|
||||
|
|
@ -149,6 +150,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
|
|||
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
|
||||
unsafe {
|
||||
assert_unsafe_precondition!(
|
||||
check_language_ub,
|
||||
"slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
|
||||
(
|
||||
data: *mut () = data as *mut (),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
//! Trait implementations for `str`.
|
||||
|
||||
use crate::cmp::Ordering;
|
||||
use crate::intrinsics::assert_unsafe_precondition;
|
||||
use crate::ops;
|
||||
use crate::panic::debug_assert_nounwind;
|
||||
use crate::ptr;
|
||||
use crate::slice::SliceIndex;
|
||||
|
||||
|
|
@ -192,15 +192,20 @@ unsafe impl SliceIndex<str> for ops::Range<usize> {
|
|||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
let slice = slice as *const [u8];
|
||||
|
||||
debug_assert_nounwind!(
|
||||
assert_unsafe_precondition!(
|
||||
// We'd like to check that the bounds are on char boundaries,
|
||||
// but there's not really a way to do so without reading
|
||||
// behind the pointer, which has aliasing implications.
|
||||
// It's also not possible to move this check up to
|
||||
// `str::get_unchecked` without adding a special function
|
||||
// to `SliceIndex` just for this.
|
||||
self.end >= self.start && self.end <= slice.len(),
|
||||
"str::get_unchecked requires that the range is within the string slice"
|
||||
check_library_ub,
|
||||
"str::get_unchecked requires that the range is within the string slice",
|
||||
(
|
||||
start: usize = self.start,
|
||||
end: usize = self.end,
|
||||
len: usize = slice.len()
|
||||
) => end >= start && end <= len,
|
||||
);
|
||||
|
||||
// SAFETY: the caller guarantees that `self` is in bounds of `slice`
|
||||
|
|
@ -213,9 +218,14 @@ unsafe impl SliceIndex<str> for ops::Range<usize> {
|
|||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
let slice = slice as *mut [u8];
|
||||
|
||||
debug_assert_nounwind!(
|
||||
self.end >= self.start && self.end <= slice.len(),
|
||||
"str::get_unchecked_mut requires that the range is within the string slice"
|
||||
assert_unsafe_precondition!(
|
||||
check_library_ub,
|
||||
"str::get_unchecked_mut requires that the range is within the string slice",
|
||||
(
|
||||
start: usize = self.start,
|
||||
end: usize = self.end,
|
||||
len: usize = slice.len()
|
||||
) => end >= start && end <= len,
|
||||
);
|
||||
|
||||
// SAFETY: see comments for `get_unchecked`.
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
#![feature(slice_from_ptr_range)]
|
||||
#![feature(slice_split_once)]
|
||||
#![feature(split_as_slice)]
|
||||
#![feature(maybe_uninit_fill)]
|
||||
#![feature(maybe_uninit_uninit_array)]
|
||||
#![feature(maybe_uninit_write_slice)]
|
||||
#![feature(maybe_uninit_uninit_array_transpose)]
|
||||
|
|
|
|||
|
|
@ -308,17 +308,17 @@ fn uninit_write_slice_cloned_mid_panic() {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Bomb;
|
||||
|
||||
impl Drop for Bomb {
|
||||
fn drop(&mut self) {
|
||||
panic!("dropped a bomb! kaboom!")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_write_slice_cloned_no_drop() {
|
||||
#[derive(Clone)]
|
||||
struct Bomb;
|
||||
|
||||
impl Drop for Bomb {
|
||||
fn drop(&mut self) {
|
||||
panic!("dropped a bomb! kaboom")
|
||||
}
|
||||
}
|
||||
|
||||
let mut dst = [MaybeUninit::uninit()];
|
||||
let src = [Bomb];
|
||||
|
||||
|
|
@ -327,6 +327,211 @@ fn uninit_write_slice_cloned_no_drop() {
|
|||
forget(src);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_fill() {
|
||||
let mut dst = [MaybeUninit::new(255); 64];
|
||||
let expect = [0; 64];
|
||||
|
||||
assert_eq!(MaybeUninit::fill(&mut dst, 0), &expect);
|
||||
}
|
||||
|
||||
#[cfg(panic = "unwind")]
|
||||
struct CloneUntilPanic {
|
||||
limit: usize,
|
||||
rc: Rc<()>,
|
||||
}
|
||||
|
||||
#[cfg(panic = "unwind")]
|
||||
impl Clone for CloneUntilPanic {
|
||||
fn clone(&self) -> Self {
|
||||
if Rc::strong_count(&self.rc) >= self.limit {
|
||||
panic!("expected panic on clone");
|
||||
}
|
||||
Self { limit: self.limit, rc: self.rc.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn uninit_fill_clone_panic_drop() {
|
||||
use std::panic;
|
||||
|
||||
let rc = Rc::new(());
|
||||
|
||||
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
|
||||
|
||||
let src = CloneUntilPanic { limit: 3, rc: rc.clone() };
|
||||
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
MaybeUninit::fill(&mut dst, src);
|
||||
}));
|
||||
|
||||
match err {
|
||||
Ok(_) => unreachable!(),
|
||||
Err(payload) => {
|
||||
payload
|
||||
.downcast::<&'static str>()
|
||||
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
|
||||
.unwrap_or_else(|p| panic::resume_unwind(p));
|
||||
assert_eq!(Rc::strong_count(&rc), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn uninit_fill_clone_no_drop_clones() {
|
||||
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
|
||||
|
||||
MaybeUninit::fill(&mut dst, Bomb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_fill_with() {
|
||||
let mut dst = [MaybeUninit::new(255); 64];
|
||||
let expect = [0; 64];
|
||||
|
||||
assert_eq!(MaybeUninit::fill_with(&mut dst, || 0), &expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn uninit_fill_with_mid_panic() {
|
||||
use std::panic;
|
||||
|
||||
let rc = Rc::new(());
|
||||
|
||||
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
|
||||
|
||||
let src = CloneUntilPanic { limit: 3, rc: rc.clone() };
|
||||
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
MaybeUninit::fill_with(&mut dst, || src.clone());
|
||||
}));
|
||||
|
||||
drop(src);
|
||||
|
||||
match err {
|
||||
Ok(_) => unreachable!(),
|
||||
Err(payload) => {
|
||||
payload
|
||||
.downcast::<&'static str>()
|
||||
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
|
||||
.unwrap_or_else(|p| panic::resume_unwind(p));
|
||||
|
||||
assert_eq!(Rc::strong_count(&rc), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn uninit_fill_with_no_drop() {
|
||||
let mut dst = [MaybeUninit::uninit()];
|
||||
let src = Bomb;
|
||||
|
||||
MaybeUninit::fill_with(&mut dst, || src.clone());
|
||||
|
||||
forget(src);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_fill_from() {
|
||||
let mut dst = [MaybeUninit::new(255); 64];
|
||||
let src = [0; 64];
|
||||
|
||||
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
|
||||
assert_eq!(initted, &src);
|
||||
assert_eq!(remainder.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_fill_from_partial() {
|
||||
let mut dst = [MaybeUninit::new(255); 64];
|
||||
let src = [0; 48];
|
||||
|
||||
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
|
||||
assert_eq!(initted, &src);
|
||||
assert_eq!(remainder.len(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_over_fill() {
|
||||
let mut dst = [MaybeUninit::new(255); 64];
|
||||
let src = [0; 72];
|
||||
|
||||
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
|
||||
assert_eq!(initted, &src[0..64]);
|
||||
assert_eq!(remainder.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_empty_fill() {
|
||||
let mut dst = [MaybeUninit::new(255); 64];
|
||||
let src = [0; 0];
|
||||
|
||||
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
|
||||
assert_eq!(initted, &src[0..0]);
|
||||
assert_eq!(remainder.len(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn uninit_fill_from_mid_panic() {
|
||||
use std::panic;
|
||||
|
||||
struct IterUntilPanic {
|
||||
limit: usize,
|
||||
rc: Rc<()>,
|
||||
}
|
||||
|
||||
impl Iterator for IterUntilPanic {
|
||||
type Item = Rc<()>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if Rc::strong_count(&self.rc) >= self.limit {
|
||||
panic!("expected panic on next");
|
||||
}
|
||||
Some(self.rc.clone())
|
||||
}
|
||||
}
|
||||
|
||||
let rc = Rc::new(());
|
||||
|
||||
let mut dst = [
|
||||
MaybeUninit::uninit(),
|
||||
MaybeUninit::uninit(),
|
||||
MaybeUninit::uninit(),
|
||||
MaybeUninit::uninit(),
|
||||
];
|
||||
|
||||
let src = IterUntilPanic { limit: 3, rc: rc.clone() };
|
||||
|
||||
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
MaybeUninit::fill_from(&mut dst, src);
|
||||
}));
|
||||
|
||||
match err {
|
||||
Ok(_) => unreachable!(),
|
||||
Err(payload) => {
|
||||
payload
|
||||
.downcast::<&'static str>()
|
||||
.and_then(|s| if *s == "expected panic on next" { Ok(s) } else { Err(s) })
|
||||
.unwrap_or_else(|p| panic::resume_unwind(p));
|
||||
|
||||
assert_eq!(Rc::strong_count(&rc), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn uninit_fill_from_no_drop() {
|
||||
let mut dst = [MaybeUninit::uninit()];
|
||||
let src = [Bomb];
|
||||
|
||||
MaybeUninit::fill_from(&mut dst, src.iter());
|
||||
|
||||
forget(src);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninit_const_assume_init_read() {
|
||||
const FOO: u32 = unsafe { MaybeUninit::new(42).assume_init_read() };
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
|||
let mut file = File::open(path)?;
|
||||
let size = file.metadata().map(|m| m.len() as usize).ok();
|
||||
let mut bytes = Vec::new();
|
||||
bytes.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
|
||||
bytes.try_reserve_exact(size.unwrap_or(0))?;
|
||||
io::default_read_to_end(&mut file, &mut bytes, size)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
|
@ -304,7 +304,7 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
|
|||
let mut file = File::open(path)?;
|
||||
let size = file.metadata().map(|m| m.len() as usize).ok();
|
||||
let mut string = String::new();
|
||||
string.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
|
||||
string.try_reserve_exact(size.unwrap_or(0))?;
|
||||
io::default_read_to_string(&mut file, &mut string, size)?;
|
||||
Ok(string)
|
||||
}
|
||||
|
|
@ -777,14 +777,14 @@ impl Read for &File {
|
|||
// Reserves space in the buffer based on the file size when available.
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let size = buffer_capacity_required(self);
|
||||
buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
|
||||
buf.try_reserve(size.unwrap_or(0))?;
|
||||
io::default_read_to_end(self, buf, size)
|
||||
}
|
||||
|
||||
// Reserves space in the buffer based on the file size when available.
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
let size = buffer_capacity_required(self);
|
||||
buf.try_reserve(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
|
||||
buf.try_reserve(size.unwrap_or(0))?;
|
||||
io::default_read_to_string(self, buf, size)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@ use crate::os::unix::fs::symlink as symlink_dir;
|
|||
#[cfg(unix)]
|
||||
use crate::os::unix::fs::symlink as symlink_file;
|
||||
#[cfg(unix)]
|
||||
use crate::os::unix::fs::symlink as symlink_junction;
|
||||
use crate::os::unix::fs::symlink as junction_point;
|
||||
#[cfg(windows)]
|
||||
use crate::os::windows::fs::{symlink_dir, symlink_file, OpenOptionsExt};
|
||||
#[cfg(windows)]
|
||||
use crate::sys::fs::symlink_junction;
|
||||
use crate::os::windows::fs::{junction_point, symlink_dir, symlink_file, OpenOptionsExt};
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::sys::weak::weak;
|
||||
|
||||
|
|
@ -598,7 +596,7 @@ fn recursive_rmdir() {
|
|||
check!(fs::create_dir_all(&dtt));
|
||||
check!(fs::create_dir_all(&d2));
|
||||
check!(check!(File::create(&canary)).write(b"foo"));
|
||||
check!(symlink_junction(&d2, &dt.join("d2")));
|
||||
check!(junction_point(&d2, &dt.join("d2")));
|
||||
let _ = symlink_file(&canary, &d1.join("canary"));
|
||||
check!(fs::remove_dir_all(&d1));
|
||||
|
||||
|
|
@ -615,7 +613,7 @@ fn recursive_rmdir_of_symlink() {
|
|||
let canary = dir.join("do_not_delete");
|
||||
check!(fs::create_dir_all(&dir));
|
||||
check!(check!(File::create(&canary)).write(b"foo"));
|
||||
check!(symlink_junction(&dir, &link));
|
||||
check!(junction_point(&dir, &link));
|
||||
check!(fs::remove_dir_all(&link));
|
||||
|
||||
assert!(!link.is_dir());
|
||||
|
|
@ -1403,7 +1401,7 @@ fn create_dir_all_with_junctions() {
|
|||
|
||||
fs::create_dir(&target).unwrap();
|
||||
|
||||
check!(symlink_junction(&target, &junction));
|
||||
check!(junction_point(&target, &junction));
|
||||
check!(fs::create_dir_all(&b));
|
||||
// the junction itself is not a directory, but `is_dir()` on a Path
|
||||
// follows links
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ impl<R: ?Sized + Read> Read for BufReader<R> {
|
|||
// delegate to the inner implementation.
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let inner_buf = self.buffer();
|
||||
buf.try_reserve(inner_buf.len()).map_err(|_| io::ErrorKind::OutOfMemory)?;
|
||||
buf.try_reserve(inner_buf.len())?;
|
||||
buf.extend_from_slice(inner_buf);
|
||||
let nread = inner_buf.len();
|
||||
self.discard_buffer();
|
||||
|
|
|
|||
|
|
@ -83,6 +83,18 @@ impl From<alloc::ffi::NulError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "io_error_from_try_reserve", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl From<alloc::collections::TryReserveError> for Error {
|
||||
/// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`].
|
||||
///
|
||||
/// `TryReserveError` won't be available as the error `source()`,
|
||||
/// but this may change in the future.
|
||||
fn from(_: alloc::collections::TryReserveError) -> Error {
|
||||
// ErrorData::Custom allocates, which isn't great for handling OOM errors.
|
||||
ErrorKind::OutOfMemory.into()
|
||||
}
|
||||
}
|
||||
|
||||
// Only derive debug in tests, to make sure it
|
||||
// doesn't accidentally get printed.
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ impl Read for &[u8] {
|
|||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let len = self.len();
|
||||
buf.try_reserve(len).map_err(|_| ErrorKind::OutOfMemory)?;
|
||||
buf.try_reserve(len)?;
|
||||
buf.extend_from_slice(*self);
|
||||
*self = &self[len..];
|
||||
Ok(len)
|
||||
|
|
@ -452,7 +452,7 @@ impl<A: Allocator> Read for VecDeque<u8, A> {
|
|||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
// The total len is known upfront so we can reserve it in a single call.
|
||||
let len = self.len();
|
||||
buf.try_reserve(len).map_err(|_| ErrorKind::OutOfMemory)?;
|
||||
buf.try_reserve(len)?;
|
||||
|
||||
let (front, back) = self.as_slices();
|
||||
buf.extend_from_slice(front);
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
|
|||
|
||||
if buf.len() == buf.capacity() {
|
||||
// buf is full, need more space
|
||||
buf.try_reserve(PROBE_SIZE).map_err(|_| ErrorKind::OutOfMemory)?;
|
||||
buf.try_reserve(PROBE_SIZE)?;
|
||||
}
|
||||
|
||||
let mut spare = buf.spare_capacity_mut();
|
||||
|
|
@ -834,7 +834,7 @@ pub trait Read {
|
|||
/// if src_buf.is_empty() {
|
||||
/// break;
|
||||
/// }
|
||||
/// dest_vec.try_reserve(src_buf.len()).map_err(|_| io::ErrorKind::OutOfMemory)?;
|
||||
/// dest_vec.try_reserve(src_buf.len())?;
|
||||
/// dest_vec.extend_from_slice(src_buf);
|
||||
///
|
||||
/// // Any irreversible side effects should happen after `try_reserve` succeeds,
|
||||
|
|
|
|||
|
|
@ -453,6 +453,32 @@ impl Read for Stdin {
|
|||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "read_shared_stdin", since = "CURRENT_RUSTC_VERSION")]
|
||||
impl Read for &Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.lock().read(buf)
|
||||
}
|
||||
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.lock().read_buf(buf)
|
||||
}
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.lock().read_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.lock().is_read_vectored()
|
||||
}
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.lock().read_to_end(buf)
|
||||
}
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
self.lock().read_to_string(buf)
|
||||
}
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
self.lock().read_exact(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// only used by platform-dependent io::copy specializations, i.e. unused on some platforms
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
impl StdinLock<'_> {
|
||||
|
|
|
|||
|
|
@ -692,3 +692,13 @@ fn read_buf_full_read() {
|
|||
|
||||
assert_eq!(BufReader::new(FullRead).fill_buf().unwrap().len(), DEFAULT_BUF_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// 64-bit only to be sure the allocator will fail fast on an impossible to satsify size
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
fn try_oom_error() {
|
||||
let mut v = Vec::<u8>::new();
|
||||
let reserve_err = v.try_reserve(isize::MAX as usize - 1).unwrap_err();
|
||||
let io_err = io::Error::from(reserve_err);
|
||||
assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue