Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2024-03-10 05:04:18 +00:00
commit bca5e33727
162 changed files with 2920 additions and 1112 deletions

View file

@ -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

View file

@ -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",

View file

@ -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 {}

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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);

View file

@ -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()),

View file

@ -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"] }

View file

@ -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"

View file

@ -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)
}

View file

@ -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)?;

View file

@ -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(())
}

View file

@ -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.

View file

@ -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(_, _) => {}

View file

@ -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);

View file

@ -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());

View file

@ -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" }

View file

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

View file

@ -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" }

View file

@ -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)
}
_ => {}
}
}
}
}

View file

@ -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(), &registry));
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(), &registry);
on_panic.disable();
})
.unwrap();
});
if let Some(size) = get_stack_size() {
builder = builder.stack_size(size);

View file

@ -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

View file

@ -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
}
})
}

View file

@ -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.

View file

@ -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,
))
}
}
}
}

View file

@ -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> {

View file

@ -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"

View file

@ -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| {

View file

@ -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)]

View file

@ -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 => {

View file

@ -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.

View file

@ -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,
}

View file

@ -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" }

View file

@ -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(_),
_,
) => {}
}

View file

@ -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" }

View file

@ -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)?;

View file

@ -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()
}

View file

@ -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 };

View file

@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> {
NullOp::SizeOf => {}
NullOp::AlignOf => {}
NullOp::OffsetOf(_) => {}
NullOp::DebugAssertions => {}
NullOp::UbCheck(_) => {}
},
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),

View file

@ -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)`

View file

@ -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" }

View file

@ -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

View file

@ -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.

View file

@ -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]

View file

@ -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 =

View file

@ -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.

View file

@ -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 {

View file

@ -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)]

View file

@ -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,
};

View file

@ -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)
}
}
}
}

View file

@ -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" }

View file

@ -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)
}
}

View file

@ -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" }

View file

@ -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

View file

@ -29,5 +29,5 @@ rustc = [
[dev-dependencies]
# tidy-alphabetical-start
itertools = "0.11"
itertools = "0.12"
# tidy-alphabetical-end

View file

@ -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" }

View file

@ -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) => {

View file

@ -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,
}
}
}

View file

@ -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 {

View file

@ -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

View file

@ -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> {

View file

@ -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)]

View file

@ -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");
}

View file

@ -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.

View file

@ -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

View file

@ -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.
///

View file

@ -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)]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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()
);

View file

@ -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,
);

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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()
}

View file

@ -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 }
}
}

View file

@ -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 }
}

View file

@ -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.

View file

@ -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

View file

@ -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 {

View file

@ -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 (),

View file

@ -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 {

View file

@ -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()
);

View file

@ -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 {

View file

@ -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()`.

View file

@ -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 (),

View file

@ -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`.

View file

@ -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)]

View file

@ -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() };

View file

@ -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)
}
}

View file

@ -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

View file

@ -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();

View file

@ -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))]

View file

@ -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);

View file

@ -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,

View file

@ -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<'_> {

View file

@ -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