Merge pull request #4699 from rust-lang/rustup-2025-11-17

Automatic Rustup
This commit is contained in:
Ralf Jung
2025-11-17 07:26:43 +00:00
committed by GitHub
317 changed files with 7128 additions and 4959 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ no_llvm_build
/llvm/
/mingw-build/
/build
/build-rust-analyzer/
/build-rust-analyzer
/dist/
/unicode-downloads
/target
+29 -29
View File
@@ -80,9 +80,9 @@ dependencies = [
[[package]]
name = "annotate-snippets"
version = "0.12.8"
version = "0.12.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "025c7edcdffa4ccc5c0905f472a0ae3759378cfbef88ef518a3575e19ae3aebd"
checksum = "a44baf24dd94e781f74dfe67ffee75a09a57971ddf0f615a178b4f6d404b48ff"
dependencies = [
"anstyle",
"unicode-width 0.2.2",
@@ -3766,7 +3766,7 @@ dependencies = [
name = "rustc_errors"
version = "0.0.0"
dependencies = [
"annotate-snippets 0.12.8",
"annotate-snippets 0.12.9",
"anstream",
"anstyle",
"derive_setters",
@@ -6070,9 +6070,9 @@ dependencies = [
[[package]]
name = "wasi-preview1-component-adapter-provider"
version = "37.0.2"
version = "38.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d0fcd636ad2b29a7c0490799a23ad61d1c8dedfafdb970447fddd0549502b60"
checksum = "7ec3ef3783e18f2457796ed91b1e6c2adc46f2905f740d1527ab3053fe8e5682"
[[package]]
name = "wasm-bindgen"
@@ -6134,9 +6134,9 @@ dependencies = [
[[package]]
name = "wasm-component-ld"
version = "0.5.18"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11f565dfcfd9aabb10d865b608a92ce1f93051aeb56f4c89550ed9cd97d8ce0e"
checksum = "4bfc50dd0b883d841bc1dba5ff7020ca52fa7b2c3bb1266d8bf6a09dd032e115"
dependencies = [
"anyhow",
"clap",
@@ -6144,7 +6144,7 @@ dependencies = [
"libc",
"tempfile",
"wasi-preview1-component-adapter-provider",
"wasmparser 0.240.0",
"wasmparser 0.241.2",
"wat",
"windows-sys 0.61.2",
"winsplit",
@@ -6171,24 +6171,24 @@ dependencies = [
[[package]]
name = "wasm-encoder"
version = "0.240.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06d642d8c5ecc083aafe9ceb32809276a304547a3a6eeecceb5d8152598bc71f"
checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8"
dependencies = [
"leb128fmt",
"wasmparser 0.240.0",
"wasmparser 0.241.2",
]
[[package]]
name = "wasm-metadata"
version = "0.240.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee093e1e1ccffa005b9b778f7a10ccfd58e25a20eccad294a1a93168d076befb"
checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder 0.240.0",
"wasmparser 0.240.0",
"wasm-encoder 0.241.2",
"wasmparser 0.241.2",
]
[[package]]
@@ -6213,9 +6213,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.240.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b722dcf61e0ea47440b53ff83ccb5df8efec57a69d150e4f24882e4eba7e24a4"
checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8"
dependencies = [
"bitflags",
"hashbrown",
@@ -6226,22 +6226,22 @@ dependencies = [
[[package]]
name = "wast"
version = "240.0.0"
version = "241.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0efe1c93db4ac562b9733e3dca19ed7fc878dba29aef22245acf84f13da4a19"
checksum = "63f66e07e2ddf531fef6344dbf94d112df7c2f23ed6ffb10962e711500b8d816"
dependencies = [
"bumpalo",
"leb128fmt",
"memchr",
"unicode-width 0.2.2",
"wasm-encoder 0.240.0",
"wasm-encoder 0.241.2",
]
[[package]]
name = "wat"
version = "1.240.0"
version = "1.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec9b6eab7ecd4d639d78515e9ea491c9bacf494aa5eda10823bd35992cf8c1e"
checksum = "45f923705c40830af909c5dec2352ec2821202e4a66008194585e1917458a26d"
dependencies = [
"wast",
]
@@ -6679,9 +6679,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.240.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dc5474b078addc5fe8a72736de8da3acfb3ff324c2491133f8b59594afa1a20"
checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241"
dependencies = [
"anyhow",
"bitflags",
@@ -6690,17 +6690,17 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"wasm-encoder 0.240.0",
"wasm-encoder 0.241.2",
"wasm-metadata",
"wasmparser 0.240.0",
"wasmparser 0.241.2",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.240.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9875ea3fa272f57cc1fc50f225a7b94021a7878c484b33792bccad0d93223439"
checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91"
dependencies = [
"anyhow",
"id-arena",
@@ -6711,7 +6711,7 @@ dependencies = [
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser 0.240.0",
"wasmparser 0.241.2",
]
[[package]]
+4 -1
View File
@@ -59,7 +59,10 @@
# toolchain or changing LLVM locally, you probably want to leave this enabled.
#
# Set this to `true` to download if CI llvm available otherwise it builds
# from `src/llvm-project`.
# from `src/llvm-project`. If you set it to `true`, it's safe and time-saving to run
# `git submodule deinit src/llvm-project` to avoid git updating the llvm-project submodule
# when building compiler locally.
#
#
# Set this to `"if-unchanged"` to download only if the llvm-project has not
# been modified. You can also use this if you are unsure whether you're on a
+47 -4
View File
@@ -269,13 +269,56 @@ pub fn supports_c_variadic(self) -> CVariadicStatus {
| Self::Aapcs { .. }
| Self::Win64 { .. }
| Self::SysV64 { .. }
| Self::EfiApi => CVariadicStatus::Stable,
Self::System { .. } => {
CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs }
}
| Self::EfiApi
| Self::System { .. } => CVariadicStatus::Stable,
_ => CVariadicStatus::NotSupported,
}
}
/// Returns whether the ABI supports guaranteed tail calls.
#[cfg(feature = "nightly")]
pub fn supports_guaranteed_tail_call(self) -> bool {
match self {
Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
// See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers
// before returning, and hence cannot guarantee a tail call.
false
}
Self::AvrInterrupt
| Self::AvrNonBlockingInterrupt
| Self::Msp430Interrupt
| Self::RiscvInterruptM
| Self::RiscvInterruptS
| Self::X86Interrupt => {
// See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.
false
}
Self::GpuKernel | Self::PtxKernel => {
// See https://godbolt.org/z/jq5TE5jK1.
false
}
Self::Custom => {
// This ABI does not support calls at all (except via assembly).
false
}
Self::C { .. }
| Self::System { .. }
| Self::Rust
| Self::RustCall
| Self::RustCold
| Self::RustInvalid
| Self::Unadjusted
| Self::EfiApi
| Self::Aapcs { .. }
| Self::Cdecl { .. }
| Self::Stdcall { .. }
| Self::Fastcall { .. }
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. } => true,
}
}
}
pub fn all_names() -> Vec<&'static str> {
+6 -2
View File
@@ -1929,7 +1929,7 @@ fn lower_expr_for(
/// ControlFlow::Break(residual) =>
/// #[allow(unreachable_code)]
/// // If there is an enclosing `try {...}`:
/// break 'catch_target Try::from_residual(residual),
/// break 'catch_target Residual::into_try_type(residual),
/// // Otherwise:
/// return Try::from_residual(residual),
/// }
@@ -1979,7 +1979,11 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir>
let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
let from_residual_expr = self.wrap_in_try_constructor(
hir::LangItem::TryTraitFromResidual,
if self.catch_scope.is_some() {
hir::LangItem::ResidualIntoTryType
} else {
hir::LangItem::TryTraitFromResidual
},
try_span,
self.arena.alloc(residual_expr),
unstable_span,
+6 -1
View File
@@ -183,7 +183,12 @@ fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
allow_contracts: [sym::contracts_internals].into(),
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
allow_try_trait: [
sym::try_trait_v2,
sym::try_trait_v2_residual,
sym::yeet_desugar_details,
]
.into(),
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
allow_gen_future: if tcx.features().async_fn_track_caller() {
[sym::gen_future, sym::closure_track_caller].into()
@@ -683,7 +683,7 @@ fn handle_offload<'ll>(cx: &'ll SimpleCx<'_>, old_fn: &llvm::Value) {
// Here we map the old arguments to the new arguments, with an offset of 1 to make sure
// that we don't use the newly added `%dyn_ptr`.
unsafe {
llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn);
llvm::LLVMRustOffloadMapper(old_fn, new_fn);
}
llvm::set_linkage(new_fn, llvm::get_linkage(old_fn));
+84 -24
View File
@@ -760,30 +760,18 @@ fn write_operand_repeatedly(
count: u64,
dest: PlaceRef<'tcx, &'ll Value>,
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
let next_bb = self.append_sibling_block("repeat_loop_next");
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]);
let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
let dest_elem = dest.project_index(&mut body_bx, i);
cg_elem.val.store(&mut body_bx, dest_elem);
let next = body_bx.unchecked_uadd(i, self.const_usize(1));
body_bx.br(header_bb);
header_bx.add_incoming_to_phi(i, next, body_bb);
*self = Self::build(self.cx, next_bb);
if self.cx.sess().opts.optimize == OptLevel::No {
// To let debuggers single-step over lines like
//
// let foo = ["bar"; 42];
//
// we need the debugger-friendly LLVM IR that `_unoptimized()`
// provides. The `_optimized()` version generates trickier LLVM IR.
// See PR #148058 for a failed attempt at handling that.
self.write_operand_repeatedly_unoptimized(cg_elem, count, dest);
} else {
self.write_operand_repeatedly_optimized(cg_elem, count, dest);
}
}
fn range_metadata(&mut self, load: &'ll Value, range: WrappingRange) {
@@ -1514,6 +1502,78 @@ pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) {
self.set_metadata_node(inst, llvm::MD_unpredictable, &[]);
}
fn write_operand_repeatedly_optimized(
&mut self,
cg_elem: OperandRef<'tcx, &'ll Value>,
count: u64,
dest: PlaceRef<'tcx, &'ll Value>,
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
let next_bb = self.append_sibling_block("repeat_loop_next");
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]);
let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
let dest_elem = dest.project_index(&mut body_bx, i);
cg_elem.val.store(&mut body_bx, dest_elem);
let next = body_bx.unchecked_uadd(i, self.const_usize(1));
body_bx.br(header_bb);
header_bx.add_incoming_to_phi(i, next, body_bb);
*self = Self::build(self.cx, next_bb);
}
fn write_operand_repeatedly_unoptimized(
&mut self,
cg_elem: OperandRef<'tcx, &'ll Value>,
count: u64,
dest: PlaceRef<'tcx, &'ll Value>,
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
let start = dest.project_index(self, zero).val.llval;
let end = dest.project_index(self, count).val.llval;
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
let next_bb = self.append_sibling_block("repeat_loop_next");
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
let current = header_bx.phi(self.val_ty(start), &[start], &[self.llbb()]);
let keep_going = header_bx.icmp(IntPredicate::IntNE, current, end);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
let align = dest.val.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size);
cg_elem
.val
.store(&mut body_bx, PlaceRef::new_sized_aligned(current, cg_elem.layout, align));
let next = body_bx.inbounds_gep(
self.backend_type(cg_elem.layout),
current,
&[self.const_usize(1)],
);
body_bx.br(header_bb);
header_bx.add_incoming_to_phi(current, next, body_bb);
*self = Self::build(self.cx, next_bb);
}
pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
self.call_intrinsic("llvm.minnum", &[self.val_ty(lhs)], &[lhs, rhs])
}
+1 -1
View File
@@ -2025,7 +2025,7 @@ pub(crate) fn LLVMRustCreateRangeAttribute(
) -> &Attribute;
// Operations on functions
pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value);
pub(crate) fn LLVMRustOffloadMapper<'a>(Fn: &'a Value, Fn: &'a Value);
pub(crate) fn LLVMRustGetOrInsertFunction<'a>(
M: &'a Module,
Name: *const c_char,
@@ -1498,14 +1498,10 @@ pub fn mem_copy_repeatedly(
// Prepare getting source provenance.
let src_bytes = src_alloc.get_bytes_unchecked(src_range).as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation
// first copy the provenance to a temporary buffer, because
// `get_bytes_mut` will clear the provenance, which is correct,
// since we don't want to keep any provenance at the target.
// This will also error if copying partial provenance is not supported.
let provenance = src_alloc
.provenance()
.prepare_copy(src_range, self)
.map_err(|e| e.to_interp_error(src_alloc_id))?;
// First copy the provenance to a temporary buffer, because
// `get_bytes_unchecked_for_overwrite_ptr` will clear the provenance (in preparation for
// inserting the new provenance), and that can overlap with the source range.
let provenance = src_alloc.provenance_prepare_copy(src_range, self);
// Prepare a copy of the initialization mask.
let init = src_alloc.init_mask().prepare_copy(src_range);
@@ -704,6 +704,7 @@ fn write_immediate_to_mplace_no_validate(
// wrong type.
let tcx = *self.tcx;
let will_later_validate = M::enforce_validity(self, layout);
let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else {
// zero-sized access
return interp_ok(());
@@ -714,23 +715,31 @@ fn write_immediate_to_mplace_no_validate(
alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?;
}
Immediate::ScalarPair(a_val, b_val) => {
let BackendRepr::ScalarPair(a, b) = layout.backend_repr else {
let BackendRepr::ScalarPair(_a, b) = layout.backend_repr else {
span_bug!(
self.cur_span(),
"write_immediate_to_mplace: invalid ScalarPair layout: {:#?}",
layout
)
};
let b_offset = a.size(&tcx).align_to(b.align(&tcx).abi);
let a_size = a_val.size();
let b_offset = a_size.align_to(b.align(&tcx).abi);
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
// It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
// but that does not work: We could be a newtype around a pair, then the
// fields do not match the `ScalarPair` components.
alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?;
// In preparation, if we do *not* later reset the padding, we clear the entire
// destination now to ensure that no stray pointer fragments are being
// preserved (see <https://github.com/rust-lang/rust/issues/148470>).
// We can skip this if there is no padding (e.g. for wide pointers).
if !will_later_validate && a_size + b_val.size() != layout.size {
alloc.write_uninit_full();
}
alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?;
alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?;
// We don't have to reset padding here, `write_immediate` will anyway do a validation run.
}
Immediate::Uninit => alloc.write_uninit_full(),
}
+1 -1
View File
@@ -5,7 +5,7 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
annotate-snippets = "0.12.8"
annotate-snippets = "0.12.9"
anstream = "0.6.20"
anstyle = "1.0.13"
derive_setters = "0.1.6"
+3
View File
@@ -1099,6 +1099,9 @@ pub fn expn_data(
pub struct DeriveResolution {
pub path: ast::Path,
pub item: Annotatable,
// FIXME: currently this field is only used in `is_none`/`is_some` conditions. However, the
// `Arc<SyntaxExtension>` will be used if the FIXME in `MacroExpander::fully_expand_fragment`
// is completed.
pub exts: Option<Arc<SyntaxExtension>>,
pub is_const: bool,
}
+2
View File
@@ -215,6 +215,8 @@ macro_rules! declare_features {
(accepted, extern_crate_self, "1.34.0", Some(56409)),
/// Allows access to crate names passed via `--extern` through prelude.
(accepted, extern_prelude, "1.30.0", Some(44660)),
/// Allows using `system` as a calling convention with varargs.
(accepted, extern_system_varargs, "CURRENT_RUSTC_VERSION", Some(136946)),
/// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`.
(accepted, f16c_target_feature, "1.68.0", Some(44839)),
/// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.
-2
View File
@@ -501,8 +501,6 @@ pub fn internal(&self, feature: Symbol) -> bool {
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
/// Allows using `#[export_stable]` which indicates that an item is exportable.
(incomplete, export_stable, "1.88.0", Some(139939)),
/// Allows using `system` as a calling convention with varargs.
(unstable, extern_system_varargs, "1.86.0", Some(136946)),
/// Allows defining `extern type`s.
(unstable, extern_types, "1.23.0", Some(43467)),
/// Allow using 128-bit (quad precision) floating point numbers.
+1
View File
@@ -370,6 +370,7 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> {
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
ResidualIntoTryType, sym::into_try_type, into_try_type_fn, Target::Fn, GenericRequirement::None;
CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0);
+13 -1
View File
@@ -7,7 +7,7 @@
use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
use rustc_target::asm::{
InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo,
};
@@ -27,6 +27,7 @@ enum NonAsmTypeReason<'tcx> {
InvalidElement(DefId, Ty<'tcx>),
NotSizedPtr(Ty<'tcx>),
EmptySIMDArray(Ty<'tcx>),
Tainted(ErrorGuaranteed),
}
impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
@@ -93,6 +94,14 @@ fn get_asm_ty(
}
}
ty::Adt(adt, args) if adt.repr().simd() => {
if !adt.is_struct() {
let guar = self.fcx.dcx().span_delayed_bug(
span,
format!("repr(simd) should only be used on structs, got {}", adt.descr()),
);
return Err(NonAsmTypeReason::Tainted(guar));
}
let fields = &adt.non_enum_variant().fields;
if fields.is_empty() {
return Err(NonAsmTypeReason::EmptySIMDArray(ty));
@@ -234,6 +243,9 @@ fn check_asm_operand_type(
let msg = format!("use of empty SIMD vector `{ty}`");
self.fcx.dcx().struct_span_err(expr.span, msg).emit();
}
NonAsmTypeReason::Tainted(_error_guard) => {
// An error has already been reported.
}
}
return None;
}
+3 -1
View File
@@ -596,7 +596,9 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
.map(|fmap| {
(
escape_dep_filename(&fmap.name.prefer_local().to_string()),
fmap.source_len.0 as u64,
// This needs to be unnormalized,
// as external tools wouldn't know how rustc normalizes them
fmap.unnormalized_source_len as u64,
fmap.checksum_hash,
)
})
+1 -1
View File
@@ -252,7 +252,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || {
// Ensure there was no errors collecting all active jobs.
// We need the complete map to ensure we find a cycle to break.
QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler")
QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler")
});
break_query_cycles(query_map, &registry);
})
@@ -144,9 +144,7 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) {
llvm::PrintStatistics(OS);
}
extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn,
LLVMValueRef NewFn) {
llvm::Module *module = llvm::unwrap(M);
extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) {
llvm::Function *oldFn = llvm::unwrap<llvm::Function>(OldFn);
llvm::Function *newFn = llvm::unwrap<llvm::Function>(NewFn);
+6 -4
View File
@@ -1744,7 +1744,8 @@ fn filter<'a>(
src_hash,
checksum_hash,
start_pos: original_start_pos,
source_len,
normalized_source_len,
unnormalized_source_len,
lines,
multibyte_chars,
normalized_pos,
@@ -1804,7 +1805,8 @@ fn filter<'a>(
src_hash,
checksum_hash,
stable_id,
source_len.to_u32(),
normalized_source_len.to_u32(),
unnormalized_source_len,
self.cnum,
lines,
multibyte_chars,
@@ -1817,9 +1819,9 @@ fn filter<'a>(
translated (start_pos {:?} source_len {:?})",
local_version.name,
original_start_pos,
source_len,
normalized_source_len,
local_version.start_pos,
local_version.source_len
local_version.normalized_source_len
);
ImportedSourceFile {
+1
View File
@@ -51,6 +51,7 @@
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(ptr_alignment_type)]
#![feature(range_bounds_is_empty)]
#![feature(rustc_attrs)]
#![feature(sized_hierarchy)]
#![feature(try_blocks)]
+15 -4
View File
@@ -234,6 +234,17 @@ pub struct ScopeTree {
pub backwards_incompatible_scope: UnordMap<hir::ItemLocalId, Scope>,
}
/// Temporary lifetime information for expressions, used when lowering to MIR.
#[derive(Clone, Copy, Debug, HashStable)]
pub struct TempLifetime {
/// The scope in which a temporary should be dropped. If `None`, no drop is scheduled; this is
/// the case for lifetime-extended temporaries extended by a const/static item or const block.
pub temp_lifetime: Option<Scope>,
/// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition.
/// If `None`, then no changes are expected, or lints are disabled.
pub backwards_incompatible: Option<Scope>,
}
impl ScopeTree {
pub fn record_scope_parent(&mut self, child: Scope, parent: Option<Scope>) {
debug!("{:?}.parent = {:?}", child, parent);
@@ -332,16 +343,16 @@ pub fn default_temporary_scope(&self, inner: Scope) -> (Scope, Option<Scope>) {
/// Returns the scope when the temp created by `expr_id` will be cleaned up.
/// It also emits a lint on potential backwards incompatible change to the temporary scope
/// which is *for now* always shortening.
pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> (Option<Scope>, Option<Scope>) {
pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> TempLifetime {
// Check for a designated extended temporary scope.
if let Some(&s) = self.extended_temp_scopes.get(&expr_id) {
debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
return (s, None);
return TempLifetime { temp_lifetime: s, backwards_incompatible: None };
}
// Otherwise, locate the innermost terminating scope.
let (scope, backward_incompatible) =
let (scope, backwards_incompatible) =
self.default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node });
(Some(scope), backward_incompatible)
TempLifetime { temp_lifetime: Some(scope), backwards_incompatible }
}
}
@@ -19,9 +19,9 @@
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use super::{
AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer,
PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch,
UndefinedBehaviorInfo, UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint,
AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, Provenance,
ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo,
interp_ok, read_target_uint, write_target_uint,
};
use crate::ty;
@@ -601,14 +601,13 @@ pub fn get_bytes_strip_provenance(
})?;
if !Prov::OFFSET_IS_ADDR && !self.provenance.range_empty(range, cx) {
// Find the provenance.
let (offset, _prov) = self
let (prov_range, _prov) = self
.provenance
.range_ptrs_get(range, cx)
.first()
.copied()
.get_range(range, cx)
.next()
.expect("there must be provenance somewhere here");
let start = offset.max(range.start); // the pointer might begin before `range`!
let end = (offset + cx.pointer_size()).min(range.end()); // the pointer might end after `range`!
let start = prov_range.start.max(range.start); // the pointer might begin before `range`!
let end = prov_range.end().min(range.end()); // the pointer might end after `range`!
return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess {
access: range,
bad: AllocRange::from(start..end),
@@ -630,7 +629,7 @@ pub fn get_bytes_unchecked_for_overwrite(
range: AllocRange,
) -> &mut [u8] {
self.mark_init(range, true);
self.provenance.clear(range, cx);
self.provenance.clear(range, &self.bytes, cx);
&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]
}
@@ -643,7 +642,7 @@ pub fn get_bytes_unchecked_for_overwrite_ptr(
range: AllocRange,
) -> *mut [u8] {
self.mark_init(range, true);
self.provenance.clear(range, cx);
self.provenance.clear(range, &self.bytes, cx);
assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check
// Crucially, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`.
@@ -711,57 +710,14 @@ pub fn read_scalar(
if read_provenance {
assert_eq!(range.size, cx.data_layout().pointer_size());
// When reading data with provenance, the easy case is finding provenance exactly where we
// are reading, then we can put data and provenance back together and return that.
if let Some(prov) = self.provenance.get_ptr(range.start) {
// Now we can return the bits, with their appropriate provenance.
if let Some(prov) = self.provenance.read_ptr(range.start, cx)? {
// Assemble the bits with their provenance.
let ptr = Pointer::new(prov, Size::from_bytes(bits));
return Ok(Scalar::from_pointer(ptr, cx));
Ok(Scalar::from_pointer(ptr, cx))
} else {
// Return raw bits without provenance.
Ok(Scalar::from_uint(bits, range.size))
}
// The other easy case is total absence of provenance.
if self.provenance.range_empty(range, cx) {
return Ok(Scalar::from_uint(bits, range.size));
}
// If we get here, we have to check per-byte provenance, and join them together.
let prov = 'prov: {
if !Prov::OFFSET_IS_ADDR {
// FIXME(#146291): We need to ensure that we don't mix different pointers with
// the same provenance.
return Err(AllocError::ReadPartialPointer(range.start));
}
// Initialize with first fragment. Must have index 0.
let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else {
break 'prov None;
};
// Update with the remaining fragments.
for offset in Size::from_bytes(1)..range.size {
// Ensure there is provenance here and it has the right index.
let Some((frag_prov, frag_idx)) =
self.provenance.get_byte(range.start + offset, cx)
else {
break 'prov None;
};
// Wildcard provenance is allowed to come with any index (this is needed
// for Miri's native-lib mode to work).
if u64::from(frag_idx) != offset.bytes() && Some(frag_prov) != Prov::WILDCARD {
break 'prov None;
}
// Merge this byte's provenance with the previous ones.
joint_prov = match Prov::join(joint_prov, frag_prov) {
Some(prov) => prov,
None => break 'prov None,
};
}
break 'prov Some(joint_prov);
};
if prov.is_none() && !Prov::OFFSET_IS_ADDR {
// There are some bytes with provenance here but overall the provenance does not add up.
// We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we must error.
return Err(AllocError::ReadPartialPointer(range.start));
}
// We can use this provenance.
let ptr = Pointer::new(prov, Size::from_bytes(bits));
return Ok(Scalar::from_maybe_pointer(ptr, cx));
} else {
// We are *not* reading a pointer.
// If we can just ignore provenance or there is none, that's easy.
@@ -816,7 +772,7 @@ pub fn write_scalar(
/// Write "uninit" to the given memory range.
pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
self.mark_init(range, false);
self.provenance.clear(range, cx);
self.provenance.clear(range, &self.bytes, cx);
}
/// Mark all bytes in the given range as initialised and reset the provenance
@@ -831,21 +787,28 @@ pub fn process_native_write(&mut self, cx: &impl HasDataLayout, range: Option<Al
size: Size::from_bytes(self.len()),
});
self.mark_init(range, true);
self.provenance.write_wildcards(cx, range);
self.provenance.write_wildcards(cx, &self.bytes, range);
}
/// Remove all provenance in the given memory range.
pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
self.provenance.clear(range, cx);
self.provenance.clear(range, &self.bytes, cx);
}
pub fn provenance_merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool {
self.provenance.merge_bytes(cx)
}
pub fn provenance_prepare_copy(
&self,
range: AllocRange,
cx: &impl HasDataLayout,
) -> ProvenanceCopy<Prov> {
self.provenance.prepare_copy(range, &self.bytes, cx)
}
/// Applies a previously prepared provenance copy.
/// The affected range, as defined in the parameters to `provenance().prepare_copy` is expected
/// to be clear of provenance.
/// The affected range is expected to be clear of provenance.
///
/// This is dangerous to use as it can violate internal `Allocation` invariants!
/// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
@@ -2,7 +2,7 @@
//! representation for the common case where PTR_SIZE consecutive bytes have the same provenance.
use std::cmp;
use std::ops::Range;
use std::ops::{Range, RangeBounds};
use rustc_abi::{HasDataLayout, Size};
use rustc_data_structures::sorted_map::SortedMap;
@@ -13,6 +13,23 @@
use super::{AllocRange, CtfeProvenance, Provenance, alloc_range};
use crate::mir::interpret::{AllocError, AllocResult};
/// A pointer fragment represents one byte of a pointer.
/// If the bytes are re-assembled in their original order, the pointer can be used again.
/// Wildcard provenance is allowed to have index 0 everywhere.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(HashStable)]
pub struct PointerFrag<Prov> {
/// The position of this fragment inside the pointer (in `0..8`).
pub idx: u8,
/// The provenance of the pointer this is a fragment of.
pub prov: Prov,
/// The raw bytes of the pointer this is a fragment of.
/// This is taken as a direct subslice of the raw allocation data, so we don't have to worry
/// about endianness. If the pointer size is less than 8, only the first N bytes of this are
/// ever non-zero.
pub bytes: [u8; 8],
}
/// Stores the provenance information of pointers stored in memory.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(HashStable)]
@@ -21,10 +38,7 @@ pub struct ProvenanceMap<Prov = CtfeProvenance> {
/// bytes. Two entries in this map are always at least a pointer size apart.
ptrs: SortedMap<Size, Prov>,
/// This stores byte-sized provenance fragments.
/// The `u8` indicates the position of this byte inside its original pointer.
/// If the bytes are re-assembled in their original order, the pointer can be used again.
/// Wildcard provenance is allowed to have index 0 everywhere.
bytes: Option<Box<SortedMap<Size, (Prov, u8)>>>,
bytes: Option<Box<SortedMap<Size, PointerFrag<Prov>>>>,
}
// These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable
@@ -49,7 +63,7 @@ pub fn new() -> Self {
}
/// The caller must guarantee that the given provenance list is already sorted
/// by address and contain no duplicates.
/// by offset and contain no duplicates.
pub fn from_presorted_ptrs(r: Vec<(Size, Prov)>) -> Self {
ProvenanceMap { ptrs: SortedMap::from_presorted_elements(r), bytes: None }
}
@@ -80,11 +94,7 @@ fn adjusted_range_ptrs(range: AllocRange, cx: &impl HasDataLayout) -> Range<Size
/// Returns all ptr-sized provenance in the given range.
/// If the range has length 0, returns provenance that crosses the edge between `start-1` and
/// `start`.
pub(super) fn range_ptrs_get(
&self,
range: AllocRange,
cx: &impl HasDataLayout,
) -> &[(Size, Prov)] {
fn range_ptrs_get(&self, range: AllocRange, cx: &impl HasDataLayout) -> &[(Size, Prov)] {
self.ptrs.range(Self::adjusted_range_ptrs(range, cx))
}
@@ -93,8 +103,14 @@ fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> boo
self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx))
}
/// Check if there is ptr-sized provenance at the given index.
/// Does not mean anything for bytewise provenance! But can be useful as an optimization.
pub fn get_ptr(&self, offset: Size) -> Option<Prov> {
self.ptrs.get(&offset).copied()
}
/// Returns all byte-wise provenance in the given range.
fn range_bytes_get(&self, range: AllocRange) -> &[(Size, (Prov, u8))] {
fn range_bytes_get(&self, range: AllocRange) -> &[(Size, PointerFrag<Prov>)] {
if let Some(bytes) = self.bytes.as_ref() {
bytes.range(range.start..range.end())
} else {
@@ -107,68 +123,126 @@ fn range_bytes_is_empty(&self, range: AllocRange) -> bool {
self.bytes.as_ref().is_none_or(|bytes| bytes.range_is_empty(range.start..range.end()))
}
/// Get the provenance of a single byte.
pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u8)> {
let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx);
debug_assert!(prov.len() <= 1);
if let Some(entry) = prov.first() {
// If it overlaps with this byte, it is on this byte.
debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset)));
Some((entry.1, (offset - entry.0).bytes() as u8))
} else {
// Look up per-byte provenance.
self.bytes.as_ref().and_then(|b| b.get(&offset).copied())
}
/// Get the provenance of a single byte. Must only be called if there is no
/// pointer-sized provenance here.
pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<&PointerFrag<Prov>> {
debug_assert!(self.range_ptrs_is_empty(alloc_range(offset, Size::from_bytes(1)), cx));
self.bytes.as_ref().and_then(|b| b.get(&offset))
}
/// Gets the provenances of all bytes (including from pointers) in a range.
pub fn get_range(
&self,
cx: &impl HasDataLayout,
range: AllocRange,
) -> impl Iterator<Item = Prov> {
let ptr_provs = self.range_ptrs_get(range, cx).iter().map(|(_, p)| *p);
let byte_provs = self.range_bytes_get(range).iter().map(|(_, (p, _))| *p);
cx: &impl HasDataLayout,
) -> impl Iterator<Item = (AllocRange, Prov)> {
let ptr_size = cx.data_layout().pointer_size();
let ptr_provs = self
.range_ptrs_get(range, cx)
.iter()
.map(move |(offset, p)| (alloc_range(*offset, ptr_size), *p));
let byte_provs = self
.range_bytes_get(range)
.iter()
.map(move |(offset, frag)| (alloc_range(*offset, Size::from_bytes(1)), frag.prov));
ptr_provs.chain(byte_provs)
}
/// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments
/// sit next to each other. Return `false` is that is not possible due to partial pointers.
/// sit next to each other. Return `false` if that is not possible due to partial pointers.
pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool {
let Some(bytes) = self.bytes.as_deref_mut() else {
return true;
};
if !Prov::OFFSET_IS_ADDR {
// FIXME(#146291): We need to ensure that we don't mix different pointers with
// the same provenance.
return false;
}
let ptr_size = cx.data_layout().pointer_size();
while let Some((offset, (prov, _))) = bytes.iter().next().copied() {
while let Some((offset, first_frag)) = bytes.iter().next() {
let offset = *offset;
// Check if this fragment starts a pointer.
let range = offset..offset + ptr_size;
let frags = bytes.range(range.clone());
if frags.len() != ptr_size.bytes_usize() {
// We can't merge this one, no point in trying to merge the rest.
return false;
}
for (idx, (_offset, (frag_prov, frag_idx))) in frags.iter().copied().enumerate() {
if frag_prov != prov || frag_idx != idx as u8 {
for (idx, (_offset, frag)) in frags.iter().enumerate() {
if !(frag.prov == first_frag.prov
&& frag.bytes == first_frag.bytes
&& frag.idx == idx as u8)
{
return false;
}
}
// Looks like a pointer! Move it over to the ptr provenance map.
self.ptrs.insert(offset, first_frag.prov);
bytes.remove_range(range);
self.ptrs.insert(offset, prov);
}
// We managed to convert everything into whole pointers.
self.bytes = None;
true
}
/// Check if there is ptr-sized provenance at the given index.
/// Does not mean anything for bytewise provenance! But can be useful as an optimization.
pub fn get_ptr(&self, offset: Size) -> Option<Prov> {
self.ptrs.get(&offset).copied()
/// Try to read a pointer from the given location, possibly by loading from many per-byte
/// provenances.
pub fn read_ptr(&self, offset: Size, cx: &impl HasDataLayout) -> AllocResult<Option<Prov>> {
// If there is pointer-sized provenance exactly here, we can just return that.
if let Some(prov) = self.get_ptr(offset) {
return Ok(Some(prov));
}
// The other easy case is total absence of provenance, that also always works.
let range = alloc_range(offset, cx.data_layout().pointer_size());
let no_ptrs = self.range_ptrs_is_empty(range, cx);
if no_ptrs && self.range_bytes_is_empty(range) {
return Ok(None);
}
// If we get here, we have to check whether we can merge per-byte provenance.
let prov = 'prov: {
// If there is any ptr-sized provenance overlapping with this range,
// this is definitely mixing multiple pointers and we can bail.
if !no_ptrs {
break 'prov None;
}
// Scan all fragments, and ensure their indices, provenance, and bytes match.
// However, we have to ignore wildcard fragments for this (this is needed for Miri's
// native-lib mode). Therefore, we will only know the expected provenance and bytes
// once we find the first non-wildcard fragment.
let mut expected = None;
for idx in Size::ZERO..range.size {
// Ensure there is provenance here.
let Some(frag) = self.get_byte(offset + idx, cx) else {
break 'prov None;
};
// If this is wildcard provenance, ignore this fragment.
if Some(frag.prov) == Prov::WILDCARD {
continue;
}
// For non-wildcard fragments, the index must match.
if u64::from(frag.idx) != idx.bytes() {
break 'prov None;
}
// If there are expectations registered, check them.
// If not, record this fragment as setting the expectations.
match expected {
Some(expected) => {
if (frag.prov, frag.bytes) != expected {
break 'prov None;
}
}
None => {
expected = Some((frag.prov, frag.bytes));
}
}
}
// The final provenance is the expected one we found along the way, or wildcard if
// we didn't find any.
Some(expected.map(|(prov, _addr)| prov).or_else(|| Prov::WILDCARD).unwrap())
};
if prov.is_none() && !Prov::OFFSET_IS_ADDR {
// There are some bytes with provenance here but overall the provenance does not add up.
// We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we
// must error.
return Err(AllocError::ReadPartialPointer(offset));
}
Ok(prov)
}
/// Returns whether this allocation has provenance overlapping with the given range.
@@ -182,8 +256,8 @@ pub fn range_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool {
/// Yields all the provenances stored in this map.
pub fn provenances(&self) -> impl Iterator<Item = Prov> {
let bytes = self.bytes.iter().flat_map(|b| b.values().map(|(p, _i)| p));
self.ptrs.values().chain(bytes).copied()
let bytes = self.bytes.iter().flat_map(|b| b.values().map(|frag| frag.prov));
self.ptrs.values().copied().chain(bytes)
}
pub fn insert_ptr(&mut self, offset: Size, prov: Prov, cx: &impl HasDataLayout) {
@@ -191,9 +265,37 @@ pub fn insert_ptr(&mut self, offset: Size, prov: Prov, cx: &impl HasDataLayout)
self.ptrs.insert(offset, prov);
}
/// Returns an iterator that yields the fragments of this pointer whose absolute positions are
/// inside `pos_range`.
fn ptr_fragments(
pos_range: impl RangeBounds<Size>,
ptr_pos: Size,
prov: Prov,
data_bytes: &[u8],
ptr_size: Size,
) -> impl Iterator<Item = (Size, PointerFrag<Prov>)> {
if pos_range.is_empty() {
return either::Left(std::iter::empty());
}
// Read ptr_size many bytes starting at ptr_pos.
let mut bytes = [0u8; 8];
(&mut bytes[..ptr_size.bytes_usize()])
.copy_from_slice(&data_bytes[ptr_pos.bytes_usize()..][..ptr_size.bytes_usize()]);
// Yield the fragments of this pointer.
either::Right(
(ptr_pos..ptr_pos + ptr_size).filter(move |pos| pos_range.contains(pos)).map(
move |pos| (pos, PointerFrag { idx: (pos - ptr_pos).bytes() as u8, bytes, prov }),
),
)
}
/// Removes all provenance inside the given range.
/// If there is provenance overlapping with the edges, might result in an error.
pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) {
#[allow(irrefutable_let_patterns)] // these actually make the code more clear
pub fn clear(&mut self, range: AllocRange, data_bytes: &[u8], cx: &impl HasDataLayout) {
if range.size == Size::ZERO {
return;
}
let start = range.start;
let end = range.end();
// Clear the bytewise part -- this is easy.
@@ -201,46 +303,42 @@ pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) {
bytes.remove_range(start..end);
}
// Find all provenance overlapping the given range.
let ptrs_range = Self::adjusted_range_ptrs(range, cx);
if self.ptrs.range_is_empty(ptrs_range.clone()) {
// No provenance in this range, we are done. This is the common case.
return;
}
let pointer_size = cx.data_layout().pointer_size();
// For the ptr-sized part, find the first (inclusive) and last (exclusive) byte of
// provenance that overlaps with the given range.
let (first, last) = {
// Find all provenance overlapping the given range.
if self.range_ptrs_is_empty(range, cx) {
// No provenance in this range, we are done. This is the common case.
return;
}
// This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much
// colder than the early return above, so it's worth it.
let provenance = self.range_ptrs_get(range, cx);
(provenance.first().unwrap().0, provenance.last().unwrap().0 + pointer_size)
};
// This redoes some of the work of `range_is_empty`, but this path is much
// colder than the early return above, so it's worth it.
let ptrs = self.ptrs.range(ptrs_range.clone());
// We need to handle clearing the provenance from parts of a pointer.
if first < start {
if let &(first, prov) = ptrs.first().unwrap()
&& first < start
{
// Insert the remaining part in the bytewise provenance.
let prov = self.ptrs[&first];
let bytes = self.bytes.get_or_insert_with(Box::default);
for offset in first..start {
bytes.insert(offset, (prov, (offset - first).bytes() as u8));
for (pos, frag) in Self::ptr_fragments(..start, first, prov, data_bytes, pointer_size) {
bytes.insert(pos, frag);
}
}
if last > end {
let begin_of_last = last - pointer_size;
if let &(last, prov) = ptrs.last().unwrap()
&& last + pointer_size > end
{
// Insert the remaining part in the bytewise provenance.
let prov = self.ptrs[&begin_of_last];
let bytes = self.bytes.get_or_insert_with(Box::default);
for offset in end..last {
bytes.insert(offset, (prov, (offset - begin_of_last).bytes() as u8));
for (pos, frag) in Self::ptr_fragments(end.., last, prov, data_bytes, pointer_size) {
bytes.insert(pos, frag);
}
}
// Forget all the provenance.
// Since provenance do not overlap, we know that removing until `last` (exclusive) is fine,
// i.e., this will not remove any other provenance just after the ones we care about.
self.ptrs.remove_range(first..last);
self.ptrs.remove_range(ptrs_range);
}
/// Overwrites all provenance in the given range with wildcard provenance.
@@ -248,30 +346,25 @@ pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) {
/// bytewise on their remaining bytes.
///
/// Provided for usage in Miri and panics otherwise.
pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
pub fn write_wildcards(
&mut self,
cx: &impl HasDataLayout,
data_bytes: &[u8],
range: AllocRange,
) {
let wildcard = Prov::WILDCARD.unwrap();
// Clear existing provenance in this range.
self.clear(range, data_bytes, cx);
// Make everything in the range wildcards.
let bytes = self.bytes.get_or_insert_with(Box::default);
// Remove pointer provenances that overlap with the range, then readd the edge ones bytewise.
let ptr_range = Self::adjusted_range_ptrs(range, cx);
let ptrs = self.ptrs.range(ptr_range.clone());
if let Some((offset, prov)) = ptrs.first().copied() {
for byte_ofs in offset..range.start {
bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8));
}
}
if let Some((offset, prov)) = ptrs.last().copied() {
for byte_ofs in range.end()..offset + cx.data_layout().pointer_size() {
bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8));
}
}
self.ptrs.remove_range(ptr_range);
// Overwrite bytewise provenance.
for offset in range.start..range.end() {
// The fragment index does not matter for wildcard provenance.
bytes.insert(offset, (wildcard, 0));
// The fragment index and bytes do not matter for wildcard provenance.
bytes.insert(
offset,
PointerFrag { prov: wildcard, idx: Default::default(), bytes: Default::default() },
);
}
}
}
@@ -281,15 +374,16 @@ pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) {
/// Offsets are relative to the beginning of the copied range.
pub struct ProvenanceCopy<Prov> {
ptrs: Box<[(Size, Prov)]>,
bytes: Box<[(Size, (Prov, u8))]>,
bytes: Box<[(Size, PointerFrag<Prov>)]>,
}
impl<Prov: Provenance> ProvenanceMap<Prov> {
pub fn prepare_copy(
&self,
range: AllocRange,
data_bytes: &[u8],
cx: &impl HasDataLayout,
) -> AllocResult<ProvenanceCopy<Prov>> {
) -> ProvenanceCopy<Prov> {
let shift_offset = move |offset| offset - range.start;
let ptr_size = cx.data_layout().pointer_size();
@@ -312,14 +406,15 @@ pub fn prepare_copy(
let end_overlap = self.range_ptrs_get(alloc_range(range.end(), Size::ZERO), cx).first();
// We only need to go here if there is some overlap or some bytewise provenance.
if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() {
let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new();
let mut bytes: Vec<(Size, PointerFrag<Prov>)> = Vec::new();
// First, if there is a part of a pointer at the start, add that.
if let Some(entry) = begin_overlap {
trace!("start overlapping entry: {entry:?}");
if let Some(&(pos, prov)) = begin_overlap {
// For really small copies, make sure we don't run off the end of the range.
let entry_end = cmp::min(entry.0 + ptr_size, range.end());
for offset in range.start..entry_end {
bytes.push((shift_offset(offset), (entry.1, (offset - entry.0).bytes() as u8)));
let end = cmp::min(pos + ptr_size, range.end());
for (pos, frag) in
Self::ptr_fragments(range.start..end, pos, prov, data_bytes, ptr_size)
{
bytes.push((shift_offset(pos), frag));
}
} else {
trace!("no start overlapping entry");
@@ -329,45 +424,35 @@ pub fn prepare_copy(
bytes.extend(
self.range_bytes_get(range)
.iter()
.map(|&(offset, reloc)| (shift_offset(offset), reloc)),
.map(|(offset, frag)| (shift_offset(*offset), frag.clone())),
);
// And finally possibly parts of a pointer at the end.
if let Some(entry) = end_overlap {
trace!("end overlapping entry: {entry:?}");
// For really small copies, make sure we don't start before `range` does.
let entry_start = cmp::max(entry.0, range.start);
for offset in entry_start..range.end() {
if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) {
// The last entry, if it exists, has a lower offset than us, so we
// can add it at the end and remain sorted.
bytes.push((
shift_offset(offset),
(entry.1, (offset - entry.0).bytes() as u8),
));
} else {
// There already is an entry for this offset in there! This can happen when the
// start and end range checks actually end up hitting the same pointer, so we
// already added this in the "pointer at the start" part above.
assert!(entry.0 <= range.start);
}
// We only have to go here if this is actually different than the begin_overlap.
if let Some(&(pos, prov)) = end_overlap
&& begin_overlap.is_none_or(|(begin, _)| *begin != pos)
{
// If this was a really small copy, we'd have handled this in begin_overlap.
assert!(pos >= range.start);
for (pos, frag) in
Self::ptr_fragments(pos..range.end(), pos, prov, data_bytes, ptr_size)
{
let pos = shift_offset(pos);
// The last entry, if it exists, has a lower offset than us, so we
// can add it at the end and remain sorted.
debug_assert!(bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < pos));
bytes.push((pos, frag));
}
} else {
trace!("no end overlapping entry");
}
trace!("byte provenances: {bytes:?}");
if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR {
// FIXME(#146291): We need to ensure that we don't mix different pointers with
// the same provenance.
return Err(AllocError::ReadPartialPointer(range.start));
}
// And again a buffer for the new list on the target side.
bytes_box = bytes.into_boxed_slice();
}
Ok(ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box })
ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box }
}
/// Applies a provenance copy.
@@ -381,8 +466,8 @@ pub fn apply_copy(&mut self, copy: ProvenanceCopy<Prov>, range: AllocRange, repe
let chunk_len = copy.ptrs.len() as u64;
self.ptrs.insert_presorted((0..chunk_len * repeat).map(|i| {
let chunk = i / chunk_len;
let (offset, reloc) = copy.ptrs[(i % chunk_len) as usize];
(shift_offset(chunk, offset), reloc)
let (offset, prov) = copy.ptrs[(i % chunk_len) as usize];
(shift_offset(chunk, offset), prov)
}));
}
if !copy.bytes.is_empty() {
@@ -390,8 +475,8 @@ pub fn apply_copy(&mut self, copy: ProvenanceCopy<Prov>, range: AllocRange, repe
self.bytes.get_or_insert_with(Box::default).insert_presorted(
(0..chunk_len * repeat).map(|i| {
let chunk = i / chunk_len;
let (offset, reloc) = copy.bytes[(i % chunk_len) as usize];
(shift_offset(chunk, offset), reloc)
let (offset, frag) = &copy.bytes[(i % chunk_len) as usize];
(shift_offset(chunk, *offset), frag.clone())
}),
);
}
@@ -77,9 +77,6 @@ pub trait Provenance: Copy + PartialEq + fmt::Debug + 'static {
/// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
/// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
fn get_alloc_id(self) -> Option<AllocId>;
/// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance.
fn join(left: Self, right: Self) -> Option<Self>;
}
/// The type of provenance in the compile-time interpreter.
@@ -191,10 +188,6 @@ fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn get_alloc_id(self) -> Option<AllocId> {
Some(self.alloc_id())
}
fn join(left: Self, right: Self) -> Option<Self> {
if left == right { Some(left) } else { None }
}
}
// We also need this impl so that one can debug-print `Pointer<AllocId>`
@@ -223,10 +216,6 @@ fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn get_alloc_id(self) -> Option<AllocId> {
Some(self)
}
fn join(_left: Self, _right: Self) -> Option<Self> {
unreachable!()
}
}
/// Represents a pointer in the Miri engine.
+3 -2
View File
@@ -1791,7 +1791,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
ascii.push('╼');
i += ptr_size;
}
} else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) {
} else if let Some(frag) = alloc.provenance().get_byte(i, &tcx) {
// Memory with provenance must be defined
assert!(
alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()
@@ -1801,7 +1801,8 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
// Format is similar to "oversized" above.
let j = i.bytes_usize();
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?;
// FIXME: Find a way to print `frag.offset` that does not look terrible...
write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼", prov = frag.prov, idx = frag.idx)?;
i += Size::from_bytes(1);
} else if alloc
.init_mask()
+5 -15
View File
@@ -256,25 +256,15 @@ pub struct Expr<'tcx> {
/// The type of this expression
pub ty: Ty<'tcx>,
/// The lifetime of this expression if it should be spilled into a
/// temporary
pub temp_lifetime: TempLifetime,
/// The id of the HIR expression whose [temporary scope] should be used for this expression.
///
/// [temporary scope]: https://doc.rust-lang.org/reference/destructors.html#temporary-scopes
pub temp_scope_id: hir::ItemLocalId,
/// span of the expression in the source
pub span: Span,
}
/// Temporary lifetime information for THIR expressions
#[derive(Clone, Copy, Debug, HashStable)]
pub struct TempLifetime {
/// Lifetime for temporaries as expected.
/// This should be `None` in a constant context.
pub temp_lifetime: Option<region::Scope>,
/// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition.
/// If `None`, then no changes are expected, or lints are disabled.
pub backwards_incompatible: Option<region::Scope>,
}
#[derive(Clone, Debug, HashStable)]
pub enum ExprKind<'tcx> {
/// `Scope`s are used to explicitly mark destruction scopes,
@@ -1127,7 +1117,7 @@ mod size_asserts {
use super::*;
// tidy-alphabetical-start
static_assert_size!(Block, 48);
static_assert_size!(Expr<'_>, 72);
static_assert_size!(Expr<'_>, 64);
static_assert_size!(ExprKind<'_>, 40);
static_assert_size!(Pat<'_>, 64);
static_assert_size!(PatKind<'_>, 48);
+1 -1
View File
@@ -45,7 +45,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
expr: &'thir Expr<'tcx>,
) {
use ExprKind::*;
let Expr { kind, ty: _, temp_lifetime: _, span: _ } = expr;
let Expr { kind, ty: _, temp_scope_id: _, span: _ } = expr;
match *kind {
Scope { value, region_scope: _, lint_level: _ } => {
visitor.visit_expr(&visitor.thir()[value])
@@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_constant(&mut self, expr: &Expr<'tcx>) -> ConstOperand<'tcx> {
let this = self;
let tcx = this.tcx;
let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
let Expr { ty, temp_scope_id: _, span, ref kind } = *expr;
match kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => {
this.as_constant(&this.thir[*value])
@@ -46,7 +46,7 @@ pub(crate) fn as_constant_inner<'tcx>(
push_cuta: impl FnMut(&Box<CanonicalUserType<'tcx>>) -> Option<UserTypeAnnotationIndex>,
tcx: TyCtxt<'tcx>,
) -> ConstOperand<'tcx> {
let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
let Expr { ty, temp_scope_id: _, span, ref kind } = *expr;
match *kind {
ExprKind::Literal { lit, neg } => {
let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg });
@@ -1,5 +1,6 @@
//! See docs in build/expr/mod.rs
use rustc_middle::middle::region::TempLifetime;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use tracing::{debug, instrument};
@@ -503,10 +503,9 @@ fn expr_as_place(
block.and(place_builder)
}
ExprKind::ValueTypeAscription { source, ref user_ty, user_ty_span } => {
let source_expr = &this.thir[source];
let temp = unpack!(
block = this.as_temp(block, source_expr.temp_lifetime, source, mutability)
);
let temp_lifetime =
this.region_scope_tree.temporary_scope(this.thir[source].temp_scope_id);
let temp = unpack!(block = this.as_temp(block, temp_lifetime, source, mutability));
if let Some(user_ty) = user_ty {
let ty_source_info = this.source_info(user_ty_span);
let annotation_index =
@@ -539,10 +538,9 @@ fn expr_as_place(
block.and(place_builder.project(PlaceElem::UnwrapUnsafeBinder(expr.ty)))
}
ExprKind::ValueUnwrapUnsafeBinder { source } => {
let source_expr = &this.thir[source];
let temp = unpack!(
block = this.as_temp(block, source_expr.temp_lifetime, source, mutability)
);
let temp_lifetime =
this.region_scope_tree.temporary_scope(this.thir[source].temp_scope_id);
let temp = unpack!(block = this.as_temp(block, temp_lifetime, source, mutability));
block.and(PlaceBuilder::from(temp).project(PlaceElem::UnwrapUnsafeBinder(expr.ty)))
}
@@ -590,8 +588,8 @@ fn expr_as_place(
| ExprKind::WrapUnsafeBinder { .. } => {
// these are not places, so we need to make a temporary.
debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));
let temp =
unpack!(block = this.as_temp(block, expr.temp_lifetime, expr_id, mutability));
let temp_lifetime = this.region_scope_tree.temporary_scope(expr.temp_scope_id);
let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability));
block.and(PlaceBuilder::from(temp))
}
}
@@ -637,7 +635,10 @@ fn lower_index_expression(
// Making this a *fresh* temporary means we do not have to worry about
// the index changing later: Nothing will ever change this temporary.
// The "retagging" transformation (for Stacked Borrows) relies on this.
let index_lifetime = self.thir[index].temp_lifetime;
// Using the enclosing temporary scope for the index ensures it will live past where this
// place is used. This lifetime may be larger than strictly necessary but it means we don't
// need to pass a scope for operands to `as_place`.
let index_lifetime = self.region_scope_tree.temporary_scope(self.thir[index].temp_scope_id);
let idx = unpack!(block = self.as_temp(block, index_lifetime, index, Mutability::Not));
block = self.bounds_check(block, &base_place, idx, expr_span, source_info);
@@ -4,7 +4,7 @@
use rustc_hir::lang_items::LangItem;
use rustc_index::{Idx, IndexVec};
use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::middle::region::{self, TempLifetime};
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
@@ -2,7 +2,7 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::HirId;
use rustc_middle::middle::region::{Scope, ScopeData};
use rustc_middle::middle::region::{Scope, ScopeData, TempLifetime};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use tracing::{debug, instrument};
@@ -1,4 +1,4 @@
use rustc_middle::middle::region;
use rustc_middle::middle::region::{self, TempLifetime};
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::thir::*;
@@ -15,7 +15,7 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node, Pinnedness};
use rustc_middle::middle::region;
use rustc_middle::middle::region::{self, TempLifetime};
use rustc_middle::mir::*;
use rustc_middle::thir::{self, *};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
@@ -135,6 +135,10 @@ fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) {
self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi);
}
if !callee_sig.abi.supports_guaranteed_tail_call() {
self.report_unsupported_abi(expr.span, callee_sig.abi);
}
// FIXME(explicit_tail_calls): this currently fails for cases where opaques are used.
// e.g.
// ```
@@ -358,6 +362,16 @@ fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: E
self.found_errors = Err(err);
}
fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) {
let err = self
.tcx
.dcx()
.struct_span_err(sp, "ABI does not support guaranteed tail calls")
.with_note(format!("`become` is not supported for `extern {callee_abi}` functions"))
.emit();
self.found_errors = Err(err);
}
fn report_signature_mismatch(
&mut self,
sp: Span,
+29 -56
View File
@@ -71,7 +71,7 @@ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> E
// Finally, wrap this up in the expr's scope.
expr = Expr {
temp_lifetime: expr.temp_lifetime,
temp_scope_id: expr_scope.local_id,
ty: expr.ty,
span: hir_expr.span,
kind: ExprKind::Scope {
@@ -93,7 +93,7 @@ fn apply_adjustment(
adjustment: &Adjustment<'tcx>,
mut span: Span,
) -> Expr<'tcx> {
let Expr { temp_lifetime, .. } = expr;
let Expr { temp_scope_id, .. } = expr;
// Adjust the span from the block, to the last expression of the
// block. This is a better span when returning a mutable reference
@@ -152,7 +152,7 @@ fn apply_adjustment(
Ty::new_fn_def(self.tcx, call_def_id, self.tcx.mk_args(&[expr.ty.into()]));
expr = Expr {
temp_lifetime,
temp_scope_id,
ty: Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, deref.mutbl),
span,
kind: ExprKind::Borrow {
@@ -199,13 +199,13 @@ fn apply_adjustment(
variant_index: FIRST_VARIANT,
name: FieldIdx::ZERO,
};
let arg = Expr { temp_lifetime, ty: pin_ty, span, kind: pointer_target };
let arg = Expr { temp_scope_id, ty: pin_ty, span, kind: pointer_target };
let arg = self.thir.exprs.push(arg);
// arg = *pointer
let expr = ExprKind::Deref { arg };
let arg = self.thir.exprs.push(Expr {
temp_lifetime,
temp_scope_id,
ty: ptr_target_ty,
span,
kind: expr,
@@ -219,7 +219,7 @@ fn apply_adjustment(
let new_pin_target =
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, ptr_target_ty, mutbl);
let expr = self.thir.exprs.push(Expr {
temp_lifetime,
temp_scope_id,
ty: new_pin_target,
span,
kind: ExprKind::Borrow { borrow_kind, arg },
@@ -242,7 +242,7 @@ fn apply_adjustment(
}
};
Expr { temp_lifetime, ty: adjustment.target, span, kind }
Expr { temp_scope_id, ty: adjustment.target, span, kind }
}
/// Lowers a cast expression.
@@ -251,7 +251,7 @@ fn apply_adjustment(
fn mirror_expr_cast(
&mut self,
source: &'tcx hir::Expr<'tcx>,
temp_lifetime: TempLifetime,
temp_scope_id: hir::ItemLocalId,
span: Span,
) -> ExprKind<'tcx> {
let tcx = self.tcx;
@@ -309,7 +309,7 @@ fn mirror_expr_cast(
);
}
let kind = ExprKind::NonHirLiteral { lit, user_ty: None };
let offset = self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind });
let offset = self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind });
let source = match discr_did {
// in case we are offsetting from a computed discriminant
@@ -317,9 +317,9 @@ fn mirror_expr_cast(
Some(did) => {
let kind = ExprKind::NamedConst { def_id: did, args, user_ty: None };
let lhs =
self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind });
self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind });
let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset };
self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind: bin })
self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind: bin })
}
None => offset,
};
@@ -336,8 +336,6 @@ fn mirror_expr_cast(
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
let tcx = self.tcx;
let expr_ty = self.typeck_results.expr_ty(expr);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let kind = match expr.kind {
// Here comes the interesting stuff:
@@ -372,7 +370,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
let arg_tys = args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e));
let tupled_args = Expr {
ty: Ty::new_tup_from_iter(tcx, arg_tys),
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
span: expr.span,
kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
};
@@ -398,7 +396,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
}
let value = &args[0];
return Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
ty: expr_ty,
span: expr.span,
kind: ExprKind::Box { value: self.mirror_expr(value) },
@@ -502,17 +500,15 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
expr: Some(arg),
safety_mode: BlockSafety::Safe,
});
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(arg_expr.hir_id.local_id);
arg = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: arg_expr.hir_id.local_id,
ty: arg_ty,
span: arg_expr.span,
kind: ExprKind::Block { block },
});
}
let expr = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
ty,
span: expr.span,
kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg },
@@ -995,12 +991,10 @@ fn local(
}
} else {
let block_ty = self.typeck_results.node_type(body.hir_id);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(body.hir_id.local_id);
let block = self.mirror_block(body);
let body = self.thir.exprs.push(Expr {
ty: block_ty,
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: body.hir_id.local_id,
span: self.thir[block].span,
kind: ExprKind::Block { block },
});
@@ -1022,17 +1016,13 @@ fn local(
expr, cast_ty.hir_id, user_ty,
);
let cast = self.mirror_expr_cast(
source,
TempLifetime { temp_lifetime, backwards_incompatible },
expr.span,
);
let cast = self.mirror_expr_cast(source, expr.hir_id.local_id, expr.span);
if let Some(user_ty) = user_ty {
// NOTE: Creating a new Expr and wrapping a Cast inside of it may be
// inefficient, revisit this when performance becomes an issue.
let cast_expr = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
ty: expr_ty,
span: expr.span,
kind: cast,
@@ -1091,12 +1081,7 @@ fn local(
hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"),
};
Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty: expr_ty,
span: expr.span,
kind,
}
Expr { temp_scope_id: expr.hir_id.local_id, ty: expr_ty, span: expr.span, kind }
}
fn user_args_applied_to_res(
@@ -1140,8 +1125,6 @@ fn method_callee(
span: Span,
overloaded_callee: Option<Ty<'tcx>>,
) -> Expr<'tcx> {
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let (ty, user_ty) = match overloaded_callee {
Some(fn_def) => (fn_def, None),
None => {
@@ -1158,7 +1141,7 @@ fn method_callee(
}
};
Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
ty,
span,
kind: ExprKind::ZstLiteral { user_ty },
@@ -1235,8 +1218,6 @@ fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKi
Res::Def(DefKind::Static { .. }, id) => {
// this is &raw for extern static or static mut, and & for other statics
let ty = self.tcx.static_ptr_ty(id, self.typing_env);
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let kind = if self.tcx.is_thread_local_static(id) {
ExprKind::ThreadLocalRef(id)
} else {
@@ -1246,7 +1227,7 @@ fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKi
ExprKind::Deref {
arg: self.thir.exprs.push(Expr {
ty,
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
span: expr.span,
kind,
}),
@@ -1317,13 +1298,11 @@ fn overloaded_place(
// construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let fun = self.method_callee(expr, span, overloaded_callee);
let fun = self.thir.exprs.push(fun);
let fun_ty = self.thir[fun].ty;
let ref_expr = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id: expr.hir_id.local_id,
ty: ref_ty,
span,
kind: ExprKind::Call { ty: fun_ty, fun, args, from_hir_call: false, fn_span: span },
@@ -1338,8 +1317,7 @@ fn convert_captured_hir_place(
closure_expr: &'tcx hir::Expr<'tcx>,
place: HirPlace<'tcx>,
) -> Expr<'tcx> {
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
let temp_scope_id = closure_expr.hir_id.local_id;
let var_ty = place.base_ty;
// The result of capture analysis in `rustc_hir_typeck/src/upvar.rs` represents a captured path
@@ -1353,7 +1331,7 @@ fn convert_captured_hir_place(
};
let mut captured_place_expr = Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id,
ty: var_ty,
span: closure_expr.span,
kind: self.convert_var(var_hir_id),
@@ -1381,12 +1359,8 @@ fn convert_captured_hir_place(
}
};
captured_place_expr = Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty: proj.ty,
span: closure_expr.span,
kind,
};
captured_place_expr =
Expr { temp_scope_id, ty: proj.ty, span: closure_expr.span, kind };
}
captured_place_expr
@@ -1401,8 +1375,7 @@ fn capture_upvar(
let upvar_capture = captured_place.info.capture_kind;
let captured_place_expr =
self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
let (temp_lifetime, backwards_incompatible) =
self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
let temp_scope_id = closure_expr.hir_id.local_id;
match upvar_capture {
ty::UpvarCapture::ByValue => captured_place_expr,
@@ -1411,7 +1384,7 @@ fn capture_upvar(
let expr_id = self.thir.exprs.push(captured_place_expr);
Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id,
ty: upvar_ty,
span: closure_expr.span,
kind: ExprKind::ByUse { expr: expr_id, span },
@@ -1428,7 +1401,7 @@ fn capture_upvar(
}
};
Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
temp_scope_id,
ty: upvar_ty,
span: closure_expr.span,
kind: ExprKind::Borrow {
@@ -10,7 +10,6 @@
use rustc_hir::lang_items::LangItem;
use rustc_hir::{self as hir, HirId, find_attr};
use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, TyCtxt};
use tracing::instrument;
@@ -60,7 +59,6 @@ struct ThirBuildCx<'tcx> {
typing_env: ty::TypingEnv<'tcx>,
region_scope_tree: &'tcx region::ScopeTree,
typeck_results: &'tcx ty::TypeckResults<'tcx>,
/// False to indicate that adjustments should not be applied. Only used for `custom_mir`
@@ -106,7 +104,6 @@ fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self {
// FIXME(#132279): We're in a body, we should use a typing
// mode which reveals the opaque types defined by that body.
typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
region_scope_tree: tcx.region_scope_tree(def),
typeck_results,
body_owner: def.to_def_id(),
apply_adjustments:
+2 -2
View File
@@ -183,10 +183,10 @@ fn print_stmt(&mut self, stmt_id: StmtId, depth_lvl: usize) {
}
fn print_expr(&mut self, expr: ExprId, depth_lvl: usize) {
let Expr { ty, temp_lifetime, span, kind } = &self.thir[expr];
let Expr { ty, temp_scope_id, span, kind } = &self.thir[expr];
print_indented!(self, "Expr {", depth_lvl);
print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1);
print_indented!(self, format!("temp_lifetime: {:?}", temp_lifetime), depth_lvl + 1);
print_indented!(self, format!("temp_scope_id: {:?}", temp_scope_id), depth_lvl + 1);
print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
print_indented!(self, "kind: ", depth_lvl + 1);
self.print_expr_kind(kind, depth_lvl + 2);
@@ -1,7 +1,12 @@
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
use rustc_middle::mir;
use rustc_middle::mir::coverage::BasicCoverageBlock;
use rustc_span::{ExpnId, ExpnKind, Span};
use crate::coverage::from_mir;
use crate::coverage::graph::CoverageGraph;
use crate::coverage::hir_info::ExtractedHirInfo;
#[derive(Clone, Copy, Debug)]
pub(crate) struct SpanWithBcb {
pub(crate) span: Span,
@@ -70,6 +75,10 @@ pub(crate) struct ExpnNode {
pub(crate) spans: Vec<SpanWithBcb>,
/// Expansions whose call-site is in this expansion.
pub(crate) child_expn_ids: FxIndexSet<ExpnId>,
/// Hole spans belonging to this expansion, to be carved out from the
/// code spans during span refinement.
pub(crate) hole_spans: Vec<Span>,
}
impl ExpnNode {
@@ -88,17 +97,27 @@ fn new(expn_id: ExpnId) -> Self {
spans: vec![],
child_expn_ids: FxIndexSet::default(),
hole_spans: vec![],
}
}
}
/// Given a collection of span/BCB pairs from potentially-different syntax contexts,
/// Extracts raw span/BCB pairs from potentially-different syntax contexts, and
/// arranges them into an "expansion tree" based on their expansion call-sites.
pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree {
pub(crate) fn build_expn_tree(
mir_body: &mir::Body<'_>,
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
) -> ExpnTree {
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
let mut nodes = FxIndexMap::default();
let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id);
for span_with_bcb in spans {
for from_mir::RawSpanFromMir { raw_span, bcb } in raw_spans {
let span_with_bcb = SpanWithBcb { span: raw_span, bcb };
// Create a node for this span's enclosing expansion, and add the span to it.
let expn_id = span_with_bcb.span.ctxt().outer_expn();
let node = nodes.entry(expn_id).or_insert_with_key(new_node);
@@ -123,5 +142,13 @@ pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> E
}
}
// Associate each hole span (extracted from HIR) with its corresponding
// expansion tree node.
for &hole_span in &hir_info.hole_spans {
let expn_id = hole_span.ctxt().outer_expn();
let Some(node) = nodes.get_mut(&expn_id) else { continue };
node.hole_spans.push(hole_span);
}
ExpnTree { nodes }
}
@@ -142,19 +142,3 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
| TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span),
}
}
#[derive(Debug)]
pub(crate) struct Hole {
pub(crate) span: Span,
}
impl Hole {
pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
if !self.span.overlaps_or_adjacent(other.span) {
return false;
}
self.span = self.span.to(other.span);
true
}
}
@@ -5,6 +5,7 @@
use rustc_middle::mir::{self, BasicBlock, StatementKind};
use rustc_middle::ty::TyCtxt;
use crate::coverage::expansion;
use crate::coverage::graph::CoverageGraph;
use crate::coverage::hir_info::ExtractedHirInfo;
use crate::coverage::spans::extract_refined_covspans;
@@ -23,10 +24,12 @@ pub(crate) fn extract_mappings_from_mir<'tcx>(
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
) -> ExtractedMappings {
let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph);
let mut mappings = vec![];
// Extract ordinary code mappings from MIR statement/terminator spans.
extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut mappings);
extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings);
extract_branch_mappings(mir_body, hir_info, graph, &mut mappings);
@@ -9,6 +9,7 @@
mod counters;
mod expansion;
mod from_mir;
mod graph;
mod hir_info;
mod mappings;
@@ -1,22 +1,18 @@
use rustc_middle::mir;
use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB};
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span};
use tracing::instrument;
use crate::coverage::expansion::{self, ExpnTree, SpanWithBcb};
use crate::coverage::expansion::{ExpnTree, SpanWithBcb};
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
use crate::coverage::hir_info::ExtractedHirInfo;
use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir};
mod from_mir;
pub(super) fn extract_refined_covspans<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
expn_tree: &ExpnTree,
mappings: &mut Vec<Mapping>,
) {
if hir_info.is_async_fn {
@@ -32,22 +28,32 @@ pub(super) fn extract_refined_covspans<'tcx>(
let &ExtractedHirInfo { body_span, .. } = hir_info;
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
// Use the raw spans to build a tree of expansions for this function.
let expn_tree = expansion::build_expn_tree(
raw_spans
.into_iter()
.map(|RawSpanFromMir { raw_span, bcb }| SpanWithBcb { span: raw_span, bcb }),
);
// If there somehow isn't an expansion tree node corresponding to the
// body span, return now and don't create any mappings.
let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) else { return };
let mut covspans = vec![];
let mut push_covspan = |covspan: Covspan| {
for &SpanWithBcb { span, bcb } in &node.spans {
covspans.push(Covspan { span, bcb });
}
// For each expansion with its call-site in the body span, try to
// distill a corresponding covspan.
for &child_expn_id in &node.child_expn_ids {
if let Some(covspan) = single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id)
{
covspans.push(covspan);
}
}
covspans.retain(|covspan: &Covspan| {
let covspan_span = covspan.span;
// Discard any spans not contained within the function body span.
// Also discard any spans that fill the entire body, because they tend
// to represent compiler-inserted code, e.g. implicitly returning `()`.
if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) {
return;
return false;
}
// Each pushed covspan should have the same context as the body span.
@@ -57,27 +63,11 @@ pub(super) fn extract_refined_covspans<'tcx>(
false,
"span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}"
);
return;
return false;
}
covspans.push(covspan);
};
if let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) {
for &SpanWithBcb { span, bcb } in &node.spans {
push_covspan(Covspan { span, bcb });
}
// For each expansion with its call-site in the body span, try to
// distill a corresponding covspan.
for &child_expn_id in &node.child_expn_ids {
if let Some(covspan) =
single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id)
{
push_covspan(covspan);
}
}
}
true
});
// Only proceed if we found at least one usable span.
if covspans.is_empty() {
@@ -107,14 +97,8 @@ pub(super) fn extract_refined_covspans<'tcx>(
covspans.dedup_by(|b, a| a.span.source_equal(b.span));
// Sort the holes, and merge overlapping/adjacent holes.
let mut holes = hir_info
.hole_spans
.iter()
.copied()
// Discard any holes that aren't directly visible within the body span.
.filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span))
.map(|span| Hole { span })
.collect::<Vec<_>>();
let mut holes = node.hole_spans.iter().copied().map(|span| Hole { span }).collect::<Vec<_>>();
holes.sort_by(|a, b| compare_spans(a.span, b.span));
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
@@ -295,3 +279,19 @@ fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
})
.ok()?
}
#[derive(Debug)]
struct Hole {
span: Span,
}
impl Hole {
fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
if !self.span.overlaps_or_adjacent(other.span) {
return false;
}
self.span = self.span.to(other.span);
true
}
}
+21 -15
View File
@@ -88,17 +88,25 @@ fn current_query_job(self) -> Option<QueryJobId> {
tls::with_related_context(self.tcx, |icx| icx.query)
}
/// Returns a query map representing active query jobs.
/// It returns an incomplete map as an error if it fails
/// to take locks.
/// Returns a map of currently active query jobs.
///
/// If `require_complete` is `true`, this function locks all shards of the
/// query results to produce a complete map, which always returns `Ok`.
/// Otherwise, it may return an incomplete map as an error if any shard
/// lock cannot be acquired.
///
/// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
/// especially when called from within a deadlock handler, unless a
/// complete map is needed and no deadlock is possible at this call site.
fn collect_active_jobs(
self,
require_complete: bool,
) -> Result<QueryMap<QueryStackDeferred<'tcx>>, QueryMap<QueryStackDeferred<'tcx>>> {
let mut jobs = QueryMap::default();
let mut complete = true;
for collect in super::TRY_COLLECT_ACTIVE_JOBS.iter() {
if collect(self.tcx, &mut jobs).is_none() {
for collect in super::COLLECT_ACTIVE_JOBS.iter() {
if collect(self.tcx, &mut jobs, require_complete).is_none() {
complete = false;
}
}
@@ -163,11 +171,7 @@ fn start_query<R>(
}
fn depth_limit_error(self, job: QueryJobId) {
// FIXME: `collect_active_jobs` expects no locks to be held, which doesn't hold for this call.
let query_map = match self.collect_active_jobs() {
Ok(query_map) => query_map,
Err(query_map) => query_map,
};
let query_map = self.collect_active_jobs(true).expect("failed to collect active queries");
let (info, depth) = job.find_dep_kind_root(query_map);
let suggested_limit = match self.recursion_limit() {
@@ -731,19 +735,21 @@ fn restore(value: <Self::Config as QueryConfig<QueryCtxt<'tcx>>>::Value) -> Self
}
}
pub(crate) fn try_collect_active_jobs<'tcx>(
pub(crate) fn collect_active_jobs<'tcx>(
tcx: TyCtxt<'tcx>,
qmap: &mut QueryMap<QueryStackDeferred<'tcx>>,
require_complete: bool,
) -> Option<()> {
let make_query = |tcx, key| {
let kind = rustc_middle::dep_graph::dep_kinds::$name;
let name = stringify!($name);
$crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name)
};
let res = tcx.query_system.states.$name.try_collect_active_jobs(
let res = tcx.query_system.states.$name.collect_active_jobs(
tcx,
make_query,
qmap,
require_complete,
);
// this can be called during unwinding, and the function has a `try_`-prefix, so
// don't `unwrap()` here, just manually check for `None` and do best-effort error
@@ -814,10 +820,10 @@ pub fn dynamic_queries<'tcx>() -> DynamicQueries<'tcx> {
// These arrays are used for iteration and can't be indexed by `DepKind`.
const TRY_COLLECT_ACTIVE_JOBS: &[
for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<QueryStackDeferred<'tcx>>) -> Option<()>
const COLLECT_ACTIVE_JOBS: &[
for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<QueryStackDeferred<'tcx>>, bool) -> Option<()>
] =
&[$(query_impl::$name::try_collect_active_jobs),*];
&[$(query_impl::$name::collect_active_jobs),*];
const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[
for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache)
@@ -54,7 +54,8 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
checksum_hash: _,
external_src: _,
start_pos: _,
source_len: _,
normalized_source_len: _,
unnormalized_source_len: _,
lines: _,
ref multibyte_chars,
ref normalized_pos,
+1 -1
View File
@@ -616,7 +616,7 @@ pub fn print_query_stack<Qcx: QueryContext>(
let mut count_total = 0;
// Make use of a partial query map if we fail to take locks collecting active queries.
let query_map = match qcx.collect_active_jobs() {
let query_map = match qcx.collect_active_jobs(false) {
Ok(query_map) => query_map,
Err(query_map) => query_map,
};
+4 -1
View File
@@ -161,7 +161,10 @@ pub trait QueryContext: HasDepContext {
/// Get the query information from the TLS context.
fn current_query_job(self) -> Option<QueryJobId>;
fn collect_active_jobs(self) -> Result<QueryMap<Self::QueryInfo>, QueryMap<Self::QueryInfo>>;
fn collect_active_jobs(
self,
require_complete: bool,
) -> Result<QueryMap<Self::QueryInfo>, QueryMap<Self::QueryInfo>>;
fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra;
@@ -7,10 +7,12 @@
use std::hash::Hash;
use std::mem;
use hashbrown::HashTable;
use hashbrown::hash_table::Entry;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sharded::{self, Sharded};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::LockGuard;
use rustc_data_structures::{outline, sync};
use rustc_errors::{Diag, FatalError, StashKey};
use rustc_span::{DUMMY_SP, Span};
@@ -63,22 +65,33 @@ pub fn all_inactive(&self) -> bool {
self.active.lock_shards().all(|shard| shard.is_empty())
}
pub fn try_collect_active_jobs<Qcx: Copy>(
pub fn collect_active_jobs<Qcx: Copy>(
&self,
qcx: Qcx,
make_query: fn(Qcx, K) -> QueryStackFrame<I>,
jobs: &mut QueryMap<I>,
require_complete: bool,
) -> Option<()> {
let mut active = Vec::new();
// We use try_lock_shards here since we are called from the
// deadlock handler, and this shouldn't be locked.
for shard in self.active.try_lock_shards() {
for (k, v) in shard?.iter() {
let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult<I>)>>| {
for (k, v) in iter.iter() {
if let QueryResult::Started(ref job) = *v {
active.push((*k, (*job).clone()));
active.push((*k, job.clone()));
}
}
};
if require_complete {
for shard in self.active.lock_shards() {
collect(shard);
}
} else {
// We use try_lock_shards here since we are called from the
// deadlock handler, and this shouldn't be locked.
for shard in self.active.try_lock_shards() {
collect(shard?);
}
}
// Call `make_query` while we're not holding a `self.active` lock as `make_query` may call
@@ -271,7 +284,7 @@ fn cycle_error<Q, Qcx>(
{
// Ensure there was no errors collecting all active jobs.
// We need the complete map to ensure we find a cycle to break.
let query_map = qcx.collect_active_jobs().ok().expect("failed to collect active queries");
let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries");
let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span);
(mk_cycle(query, qcx, error.lift(qcx)), None)
+1 -4
View File
@@ -458,14 +458,11 @@ struct Flags: u8 {
let mut result = Err(Determinacy::Determined);
for derive in parent_scope.derives {
let parent_scope = &ParentScope { derives: &[], ..*parent_scope };
match this.reborrow().resolve_macro_path(
match this.reborrow().resolve_derive_macro_path(
derive,
MacroKind::Derive,
parent_scope,
true,
force,
ignore_import,
None,
) {
Ok((Some(ext), _)) => {
if ext.helper_attrs.contains(&ident.name) {
+3 -3
View File
@@ -2916,9 +2916,9 @@ fn resolve_item(&mut self, item: &'ast Item) {
}
}
fn with_generic_param_rib<'c, F>(
&'c mut self,
params: &'c [GenericParam],
fn with_generic_param_rib<F>(
&mut self,
params: &[GenericParam],
kind: RibKind<'ra>,
binder: NodeId,
generics_kind: LifetimeBinderKind,
+19 -32
View File
@@ -396,14 +396,11 @@ fn resolve_derives(
for (i, resolution) in entry.resolutions.iter_mut().enumerate() {
if resolution.exts.is_none() {
resolution.exts = Some(
match self.cm().resolve_macro_path(
match self.cm().resolve_derive_macro_path(
&resolution.path,
MacroKind::Derive,
&parent_scope,
true,
force,
None,
None,
) {
Ok((Some(ext), _)) => {
if !ext.helper_attrs.is_empty() {
@@ -571,7 +568,6 @@ fn smart_resolve_macro_path(
path,
kind,
parent_scope,
true,
force,
deleg_impl,
invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)),
@@ -713,26 +709,22 @@ fn smart_resolve_macro_path(
Ok((ext, res))
}
pub(crate) fn resolve_macro_path<'r>(
pub(crate) fn resolve_derive_macro_path<'r>(
self: CmResolver<'r, 'ra, 'tcx>,
path: &ast::Path,
kind: MacroKind,
parent_scope: &ParentScope<'ra>,
trace: bool,
force: bool,
ignore_import: Option<Import<'ra>>,
suggestion_span: Option<Span>,
) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> {
self.resolve_macro_or_delegation_path(
path,
kind,
MacroKind::Derive,
parent_scope,
trace,
force,
None,
None,
ignore_import,
suggestion_span,
None,
)
}
@@ -741,7 +733,6 @@ fn resolve_macro_or_delegation_path<'r>(
ast_path: &ast::Path,
kind: MacroKind,
parent_scope: &ParentScope<'ra>,
trace: bool,
force: bool,
deleg_impl: Option<LocalDefId>,
invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>,
@@ -780,16 +771,14 @@ fn resolve_macro_or_delegation_path<'r>(
PathResult::Module(..) => unreachable!(),
};
if trace {
self.multi_segment_macro_resolutions.borrow_mut(&self).push((
path,
path_span,
kind,
*parent_scope,
res.ok(),
ns,
));
}
self.multi_segment_macro_resolutions.borrow_mut(&self).push((
path,
path_span,
kind,
*parent_scope,
res.ok(),
ns,
));
self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span);
res
@@ -807,15 +796,13 @@ fn resolve_macro_or_delegation_path<'r>(
return Err(Determinacy::Undetermined);
}
if trace {
self.single_segment_macro_resolutions.borrow_mut(&self).push((
path[0].ident,
kind,
*parent_scope,
binding.ok(),
suggestion_span,
));
}
self.single_segment_macro_resolutions.borrow_mut(&self).push((
path[0].ident,
kind,
*parent_scope,
binding.ok(),
suggestion_span,
));
let res = binding.map(|binding| binding.res());
self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span);
+24 -12
View File
@@ -1723,8 +1723,10 @@ pub struct SourceFile {
pub external_src: FreezeLock<ExternalSource>,
/// The start position of this source in the `SourceMap`.
pub start_pos: BytePos,
/// The byte length of this source.
pub source_len: RelativeBytePos,
/// The byte length of this source after normalization.
pub normalized_source_len: RelativeBytePos,
/// The byte length of this source before normalization.
pub unnormalized_source_len: u32,
/// Locations of lines beginnings in the source code.
pub lines: FreezeLock<SourceFileLines>,
/// Locations of multi-byte characters in the source code.
@@ -1748,7 +1750,8 @@ fn clone(&self) -> Self {
checksum_hash: self.checksum_hash,
external_src: self.external_src.clone(),
start_pos: self.start_pos,
source_len: self.source_len,
normalized_source_len: self.normalized_source_len,
unnormalized_source_len: self.unnormalized_source_len,
lines: self.lines.clone(),
multibyte_chars: self.multibyte_chars.clone(),
normalized_pos: self.normalized_pos.clone(),
@@ -1764,7 +1767,8 @@ fn encode(&self, s: &mut S) {
self.src_hash.encode(s);
self.checksum_hash.encode(s);
// Do not encode `start_pos` as it's global state for this session.
self.source_len.encode(s);
self.normalized_source_len.encode(s);
self.unnormalized_source_len.encode(s);
// We are always in `Lines` form by the time we reach here.
assert!(self.lines.read().is_lines());
@@ -1837,7 +1841,8 @@ fn decode(d: &mut D) -> SourceFile {
let name: FileName = Decodable::decode(d);
let src_hash: SourceFileHash = Decodable::decode(d);
let checksum_hash: Option<SourceFileHash> = Decodable::decode(d);
let source_len: RelativeBytePos = Decodable::decode(d);
let normalized_source_len: RelativeBytePos = Decodable::decode(d);
let unnormalized_source_len = Decodable::decode(d);
let lines = {
let num_lines: u32 = Decodable::decode(d);
if num_lines > 0 {
@@ -1859,7 +1864,8 @@ fn decode(d: &mut D) -> SourceFile {
SourceFile {
name,
start_pos: BytePos::from_u32(0),
source_len,
normalized_source_len,
unnormalized_source_len,
src: None,
src_hash,
checksum_hash,
@@ -1959,12 +1965,17 @@ pub fn new(
SourceFileHash::new_in_memory(checksum_hash_kind, src.as_bytes())
}
});
// Capture the original source length before normalization.
let unnormalized_source_len = u32::try_from(src.len()).map_err(|_| OffsetOverflowError)?;
if unnormalized_source_len > Self::MAX_FILE_SIZE {
return Err(OffsetOverflowError);
}
let normalized_pos = normalize_src(&mut src);
let stable_id = StableSourceFileId::from_filename_in_current_crate(&name);
let source_len = src.len();
let source_len = u32::try_from(source_len).map_err(|_| OffsetOverflowError)?;
if source_len > Self::MAX_FILE_SIZE {
let normalized_source_len = u32::try_from(src.len()).map_err(|_| OffsetOverflowError)?;
if normalized_source_len > Self::MAX_FILE_SIZE {
return Err(OffsetOverflowError);
}
@@ -1977,7 +1988,8 @@ pub fn new(
checksum_hash,
external_src: FreezeLock::frozen(ExternalSource::Unneeded),
start_pos: BytePos::from_u32(0),
source_len: RelativeBytePos::from_u32(source_len),
normalized_source_len: RelativeBytePos::from_u32(normalized_source_len),
unnormalized_source_len,
lines: FreezeLock::frozen(SourceFileLines::Lines(lines)),
multibyte_chars,
normalized_pos,
@@ -2161,7 +2173,7 @@ pub fn relative_position(&self, pos: BytePos) -> RelativeBytePos {
#[inline]
pub fn end_position(&self) -> BytePos {
self.absolute_position(self.source_len)
self.absolute_position(self.normalized_source_len)
}
/// Finds the line containing the given position. The return value is the
@@ -2197,7 +2209,7 @@ pub fn contains(&self, byte_pos: BytePos) -> bool {
#[inline]
pub fn is_empty(&self) -> bool {
self.source_len.to_u32() == 0
self.normalized_source_len.to_u32() == 0
}
/// Calculates the original byte position relative to the start of the file
+8 -6
View File
@@ -262,7 +262,7 @@ pub fn load_binary_file(&self, path: &Path) -> io::Result<(Arc<[u8]>, Span)> {
bytes,
Span::new(
file.start_pos,
BytePos(file.start_pos.0 + file.source_len.0),
BytePos(file.start_pos.0 + file.normalized_source_len.0),
SyntaxContext::root(),
None,
),
@@ -353,14 +353,15 @@ pub fn new_imported_source_file(
src_hash: SourceFileHash,
checksum_hash: Option<SourceFileHash>,
stable_id: StableSourceFileId,
source_len: u32,
normalized_source_len: u32,
unnormalized_source_len: u32,
cnum: CrateNum,
file_local_lines: FreezeLock<SourceFileLines>,
multibyte_chars: Vec<MultiByteChar>,
normalized_pos: Vec<NormalizedPos>,
metadata_index: u32,
) -> Arc<SourceFile> {
let source_len = RelativeBytePos::from_u32(source_len);
let normalized_source_len = RelativeBytePos::from_u32(normalized_source_len);
let source_file = SourceFile {
name: filename,
@@ -372,7 +373,8 @@ pub fn new_imported_source_file(
metadata_index,
}),
start_pos: BytePos(0),
source_len,
normalized_source_len,
unnormalized_source_len,
lines: file_local_lines,
multibyte_chars,
normalized_pos,
@@ -566,7 +568,7 @@ pub fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, Spa
let start_index = local_begin.pos.to_usize();
let end_index = local_end.pos.to_usize();
let source_len = local_begin.sf.source_len.to_usize();
let source_len = local_begin.sf.normalized_source_len.to_usize();
if start_index > end_index || end_index > source_len {
return Err(SpanSnippetError::MalformedForSourcemap(MalformedSourceMapPositions {
@@ -997,7 +999,7 @@ fn find_width_of_character_at_span(&self, sp: SpanData, forwards: bool) -> u32 {
return 1;
}
let source_len = local_begin.sf.source_len.to_usize();
let source_len = local_begin.sf.normalized_source_len.to_usize();
debug!("source_len=`{:?}`", source_len);
// Ensure indexes are also not malformed.
if start_index > end_index || end_index > source_len - 1 {
+4 -2
View File
@@ -230,7 +230,8 @@ fn t10() {
name,
src_hash,
checksum_hash,
source_len,
normalized_source_len,
unnormalized_source_len,
lines,
multibyte_chars,
normalized_pos,
@@ -243,7 +244,8 @@ fn t10() {
src_hash,
checksum_hash,
stable_id,
source_len.to_u32(),
normalized_source_len.to_u32(),
unnormalized_source_len,
CrateNum::ZERO,
FreezeLock::new(lines.read().clone()),
multibyte_chars,
+2
View File
@@ -1259,6 +1259,7 @@
into_async_iter_into_iter,
into_future,
into_iter,
into_try_type,
intra_doc_pointers,
intrinsics,
intrinsics_unaligned_volatile_load,
@@ -2280,6 +2281,7 @@
try_from_fn,
try_into,
try_trait_v2,
try_trait_v2_residual,
try_update,
tt,
tuple,
+14
View File
@@ -103,3 +103,17 @@ fn test_trim() {
assert_eq!(span(well_before, before).trim_start(other), None);
}
#[test]
fn test_unnormalized_source_length() {
let source = "\u{feff}hello\r\nferries\r\n".to_owned();
let sf = SourceFile::new(
FileName::Anon(Hash64::ZERO),
source,
SourceFileHashAlgorithm::Sha256,
Some(SourceFileHashAlgorithm::Sha256),
)
.unwrap();
assert_eq!(sf.unnormalized_source_len, 19);
assert_eq!(sf.normalized_source_len.0, 14);
}
@@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
metadata: TargetMetadata {
description: Some("ARM64 MinGW (Windows 10+), LLVM ABI".into()),
tier: Some(2),
host_tools: Some(false),
host_tools: Some(true),
std: Some(true),
},
pointer_width: 64,
@@ -5,5 +5,11 @@ pub(crate) fn target() -> Target {
base.rustc_abi = None; // overwrite the SSE2 ABI set by the base target
base.cpu = "pentium".into();
base.llvm_target = "i586-unknown-linux-gnu".into();
base.metadata = crate::spec::TargetMetadata {
description: Some("32-bit Linux (kernel 3.2, glibc 2.17+)".into()),
tier: Some(2),
host_tools: Some(false),
std: Some(true),
};
base
}
@@ -18,7 +18,7 @@ pub(crate) fn target() -> Target {
metadata: TargetMetadata {
description: Some("SPARC Solaris 11.4".into()),
tier: Some(2),
host_tools: Some(false),
host_tools: Some(true),
std: Some(true),
},
pointer_width: 64,
@@ -63,7 +63,7 @@ pub(crate) fn target() -> Target {
llvm_target: "wasm32-wasip2".into(),
metadata: TargetMetadata {
description: Some("WebAssembly".into()),
tier: Some(3),
tier: Some(2),
host_tools: Some(false),
std: Some(true),
},
@@ -15,6 +15,12 @@ pub(crate) fn target() -> Target {
// and this may grow over time as more features are supported.
let mut target = super::wasm32_wasip2::target();
target.llvm_target = "wasm32-wasip3".into();
target.metadata = crate::spec::TargetMetadata {
description: Some("WebAssembly".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(true),
};
target.options.env = Env::P3;
target
}
@@ -20,7 +20,7 @@ pub(crate) fn target() -> Target {
metadata: TargetMetadata {
description: Some("64-bit Solaris 11.4".into()),
tier: Some(2),
host_tools: Some(false),
host_tools: Some(true),
std: Some(true),
},
pointer_width: 64,
@@ -14,7 +14,7 @@ pub(crate) fn target() -> Target {
metadata: TargetMetadata {
description: Some("64-bit x86 MinGW (Windows 10+), LLVM ABI".into()),
tier: Some(2),
host_tools: Some(false),
host_tools: Some(true),
std: Some(true),
},
pointer_width: 64,
+41 -34
View File
@@ -4,9 +4,9 @@ version = 4
[[package]]
name = "addr2line"
version = "0.25.0"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43"
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
dependencies = [
"gimli",
"rustc-std-workspace-alloc",
@@ -49,9 +49,9 @@ dependencies = [
[[package]]
name = "cfg-if"
version = "1.0.1"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
dependencies = [
"rustc-std-workspace-core",
]
@@ -78,9 +78,9 @@ dependencies = [
[[package]]
name = "dlmalloc"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa3a2dbee57b69fbb5dbe852fa9c0925697fb0c7fbcb1593e90e5ffaedf13d51"
checksum = "06cdfe340b16dd990c54cce79743613fa09fbb16774f33a77c9fd196f8f3fa30"
dependencies = [
"cfg-if",
"libc",
@@ -109,9 +109,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.32.0"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@@ -393,9 +393,9 @@ dependencies = [
[[package]]
name = "vex-sdk"
version = "0.27.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f74fce61d7a7ba1589da9634c6305a72befb7cc9150c1f872d87d8060f32b9"
checksum = "79e5fe15afde1305478b35e2cb717fff59f485428534cf49cfdbfa4723379bf6"
dependencies = [
"rustc-std-workspace-core",
]
@@ -422,12 +422,18 @@ dependencies = [
]
[[package]]
name = "windows-sys"
version = "0.59.0"
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.52.6",
"windows-targets 0.53.5",
]
[[package]]
@@ -436,10 +442,11 @@ version = "0.0.0"
[[package]]
name = "windows-targets"
version = "0.52.6"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
@@ -452,57 +459,57 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "wit-bindgen"
version = "0.45.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
+2 -2
View File
@@ -725,9 +725,9 @@ pub fn into_inner(boxed: Self) -> T {
#[unstable(feature = "box_take", issue = "147212")]
pub fn take(boxed: Self) -> (T, Box<mem::MaybeUninit<T>, A>) {
unsafe {
let (raw, alloc) = Box::into_raw_with_allocator(boxed);
let (raw, alloc) = Box::into_non_null_with_allocator(boxed);
let value = raw.read();
let uninit = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc);
let uninit = Box::from_non_null_in(raw.cast_uninit(), alloc);
(value, uninit)
}
}
+4 -2
View File
@@ -1434,7 +1434,8 @@ pub fn split_off<Q: ?Sized + Ord>(&mut self, key: &Q) -> Self
///
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
/// or the iteration short-circuits, then the remaining elements will be retained.
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
/// Use `extract_if().for_each(drop)` if you do not need the returned iterator,
/// or [`retain`] with a negated predicate if you also do not need to restrict the range.
///
/// [`retain`]: BTreeMap::retain
///
@@ -1945,7 +1946,8 @@ fn default() -> Self {
/// An iterator produced by calling `extract_if` on BTreeMap.
#[stable(feature = "btree_extract_if", since = "1.91.0")]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain` or `extract_if().for_each(drop)` to remove and discard elements"]
pub struct ExtractIf<
'a,
K,
+4 -2
View File
@@ -1189,7 +1189,8 @@ pub fn split_off<Q: ?Sized + Ord>(&mut self, value: &Q) -> Self
///
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
/// or the iteration short-circuits, then the remaining elements will be retained.
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
/// Use `extract_if().for_each(drop)` if you do not need the returned iterator,
/// or [`retain`] with a negated predicate if you also do not need to restrict the range.
///
/// [`retain`]: BTreeSet::retain
/// # Examples
@@ -1547,7 +1548,8 @@ fn into_iter(self) -> Iter<'a, T> {
/// An iterator produced by calling `extract_if` on BTreeSet.
#[stable(feature = "btree_extract_if", since = "1.91.0")]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain` or `extract_if().for_each(drop)` to remove and discard elements"]
pub struct ExtractIf<
'a,
T,
+2 -1
View File
@@ -1943,7 +1943,8 @@ pub fn back_mut(&mut self) -> Option<&mut T> {
/// An iterator produced by calling `extract_if` on LinkedList.
#[stable(feature = "extract_if", since = "1.87.0")]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `extract_if().for_each(drop)` to remove and discard elements"]
pub struct ExtractIf<
'a,
T: 'a,
@@ -21,7 +21,8 @@
/// let iter: ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0);
/// ```
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain_mut` or `extract_if().for_each(drop)` to remove and discard elements"]
pub struct ExtractIf<
'a,
T,
@@ -676,7 +676,8 @@ unsafe fn handle_capacity_increase(&mut self, old_capacity: usize) {
///
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
/// or the iteration short-circuits, then the remaining elements will be retained.
/// Use [`retain_mut`] with a negated predicate if you do not need the returned iterator.
/// Use `extract_if().for_each(drop)` if you do not need the returned iterator,
/// or [`retain_mut`] with a negated predicate if you also do not need to restrict the range.
///
/// [`retain_mut`]: VecDeque::retain_mut
///
+2
View File
@@ -116,6 +116,7 @@
#![feature(exact_size_is_empty)]
#![feature(extend_one)]
#![feature(extend_one_unchecked)]
#![feature(fmt_arguments_from_str)]
#![feature(fmt_internals)]
#![feature(fn_traits)]
#![feature(formatting_options)]
@@ -146,6 +147,7 @@
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(temporary_niche_types)]
#![feature(transmutability)]
#![feature(trivial_clone)]
#![feature(trusted_fused)]
#![feature(trusted_len)]
+5 -20
View File
@@ -265,18 +265,11 @@
/// You can look at these with the [`as_ptr`], [`len`], and [`capacity`]
/// methods:
///
// FIXME Update this when vec_into_raw_parts is stabilized
/// ```
/// use std::mem;
///
/// let story = String::from("Once upon a time...");
///
/// // Prevent automatically dropping the String's data
/// let mut story = mem::ManuallyDrop::new(story);
///
/// let ptr = story.as_mut_ptr();
/// let len = story.len();
/// let capacity = story.capacity();
/// // Deconstruct the String into parts.
/// let (ptr, len, capacity) = story.into_raw_parts();
///
/// // story has nineteen bytes
/// assert_eq!(19, len);
@@ -932,7 +925,6 @@ pub fn from_utf16be_lossy(v: &[u8]) -> String {
/// # Examples
///
/// ```
/// #![feature(vec_into_raw_parts)]
/// let s = String::from("hello");
///
/// let (ptr, len, cap) = s.into_raw_parts();
@@ -941,7 +933,7 @@ pub fn from_utf16be_lossy(v: &[u8]) -> String {
/// assert_eq!(rebuilt, "hello");
/// ```
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
#[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")]
pub fn into_raw_parts(self) -> (*mut u8, usize, usize) {
self.vec.into_raw_parts()
}
@@ -970,19 +962,12 @@ pub fn into_raw_parts(self) -> (*mut u8, usize, usize) {
///
/// # Examples
///
// FIXME Update this when vec_into_raw_parts is stabilized
/// ```
/// use std::mem;
///
/// unsafe {
/// let s = String::from("hello");
///
/// // Prevent automatically dropping the String's data
/// let mut s = mem::ManuallyDrop::new(s);
///
/// let ptr = s.as_mut_ptr();
/// let len = s.len();
/// let capacity = s.capacity();
/// // Deconstruct the String into parts.
/// let (ptr, len, capacity) = s.into_raw_parts();
///
/// let s = String::from_raw_parts(ptr, len, capacity);
///
+2 -1
View File
@@ -16,7 +16,8 @@
/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0);
/// ```
#[stable(feature = "extract_if", since = "1.87.0")]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain_mut` or `extract_if().for_each(drop)` to remove and discard elements"]
pub struct ExtractIf<
'a,
T,
+101 -56
View File
@@ -82,7 +82,7 @@
#[cfg(not(no_global_oom_handling))]
use core::iter;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, TransmuteFrom};
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
use core::ptr::{self, NonNull};
use core::slice::{self, SliceIndex};
@@ -592,21 +592,13 @@ pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
///
/// # Examples
///
// FIXME Update this when vec_into_raw_parts is stabilized
/// ```
/// use std::ptr;
/// use std::mem;
///
/// let v = vec![1, 2, 3];
///
/// // Prevent running `v`'s destructor so we are in complete control
/// // of the allocation.
/// let mut v = mem::ManuallyDrop::new(v);
///
/// // Pull out the various important pieces of information about `v`
/// let p = v.as_mut_ptr();
/// let len = v.len();
/// let cap = v.capacity();
/// // Deconstruct the vector into parts.
/// let (p, len, cap) = v.into_raw_parts();
///
/// unsafe {
/// // Overwrite memory with 4, 5, 6
@@ -700,23 +692,13 @@ pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Sel
///
/// # Examples
///
// FIXME Update this when vec_into_raw_parts is stabilized
/// ```
/// #![feature(box_vec_non_null)]
///
/// use std::ptr::NonNull;
/// use std::mem;
///
/// let v = vec![1, 2, 3];
///
/// // Prevent running `v`'s destructor so we are in complete control
/// // of the allocation.
/// let mut v = mem::ManuallyDrop::new(v);
///
/// // Pull out the various important pieces of information about `v`
/// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) };
/// let len = v.len();
/// let cap = v.capacity();
/// // Deconstruct the vector into parts.
/// let (p, len, cap) = v.into_parts();
///
/// unsafe {
/// // Overwrite memory with 4, 5, 6
@@ -783,7 +765,6 @@ pub unsafe fn from_parts(ptr: NonNull<T>, length: usize, capacity: usize) -> Sel
/// # Examples
///
/// ```
/// #![feature(vec_into_raw_parts)]
/// let v: Vec<i32> = vec![-1, 0, 1];
///
/// let (ptr, len, cap) = v.into_raw_parts();
@@ -798,7 +779,7 @@ pub unsafe fn from_parts(ptr: NonNull<T>, length: usize, capacity: usize) -> Sel
/// assert_eq!(rebuilt, [4294967295, 0, 1]);
/// ```
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
#[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")]
pub fn into_raw_parts(self) -> (*mut T, usize, usize) {
let mut me = ManuallyDrop::new(self);
(me.as_mut_ptr(), me.len(), me.capacity())
@@ -823,7 +804,7 @@ pub fn into_raw_parts(self) -> (*mut T, usize, usize) {
/// # Examples
///
/// ```
/// #![feature(vec_into_raw_parts, box_vec_non_null)]
/// #![feature(box_vec_non_null)]
///
/// let v: Vec<i32> = vec![-1, 0, 1];
///
@@ -840,7 +821,6 @@ pub fn into_raw_parts(self) -> (*mut T, usize, usize) {
/// ```
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")]
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
pub fn into_parts(self) -> (NonNull<T>, usize, usize) {
let (ptr, len, capacity) = self.into_raw_parts();
// SAFETY: A `Vec` always has a non-null pointer.
@@ -996,29 +976,20 @@ pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserv
///
/// # Examples
///
// FIXME Update this when vec_into_raw_parts is stabilized
/// ```
/// #![feature(allocator_api)]
///
/// use std::alloc::System;
///
/// use std::ptr;
/// use std::mem;
///
/// let mut v = Vec::with_capacity_in(3, System);
/// v.push(1);
/// v.push(2);
/// v.push(3);
///
/// // Prevent running `v`'s destructor so we are in complete control
/// // of the allocation.
/// let mut v = mem::ManuallyDrop::new(v);
///
/// // Pull out the various important pieces of information about `v`
/// let p = v.as_mut_ptr();
/// let len = v.len();
/// let cap = v.capacity();
/// let alloc = v.allocator();
/// // Deconstruct the vector into parts.
/// let (p, len, cap, alloc) = v.into_raw_parts_with_alloc();
///
/// unsafe {
/// // Overwrite memory with 4, 5, 6
@@ -1116,29 +1087,18 @@ pub unsafe fn from_raw_parts_in(ptr: *mut T, length: usize, capacity: usize, all
///
/// # Examples
///
// FIXME Update this when vec_into_raw_parts is stabilized
/// ```
/// #![feature(allocator_api, box_vec_non_null)]
///
/// use std::alloc::System;
///
/// use std::ptr::NonNull;
/// use std::mem;
///
/// let mut v = Vec::with_capacity_in(3, System);
/// v.push(1);
/// v.push(2);
/// v.push(3);
///
/// // Prevent running `v`'s destructor so we are in complete control
/// // of the allocation.
/// let mut v = mem::ManuallyDrop::new(v);
///
/// // Pull out the various important pieces of information about `v`
/// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) };
/// let len = v.len();
/// let cap = v.capacity();
/// let alloc = v.allocator();
/// // Deconstruct the vector into parts.
/// let (p, len, cap, alloc) = v.into_parts_with_alloc();
///
/// unsafe {
/// // Overwrite memory with 4, 5, 6
@@ -1206,7 +1166,7 @@ pub unsafe fn from_parts_in(ptr: NonNull<T>, length: usize, capacity: usize, all
/// # Examples
///
/// ```
/// #![feature(allocator_api, vec_into_raw_parts)]
/// #![feature(allocator_api)]
///
/// use std::alloc::System;
///
@@ -1228,7 +1188,6 @@ pub unsafe fn from_parts_in(ptr: NonNull<T>, length: usize, capacity: usize, all
/// ```
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "allocator_api", issue = "32838")]
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) {
let mut me = ManuallyDrop::new(self);
let len = me.len();
@@ -1256,7 +1215,7 @@ pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) {
/// # Examples
///
/// ```
/// #![feature(allocator_api, vec_into_raw_parts, box_vec_non_null)]
/// #![feature(allocator_api, box_vec_non_null)]
///
/// use std::alloc::System;
///
@@ -1279,7 +1238,6 @@ pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) {
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "allocator_api", issue = "32838")]
// #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")]
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
pub fn into_parts_with_alloc(self) -> (NonNull<T>, usize, usize, A) {
let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc();
// SAFETY: A `Vec` always has a non-null pointer.
@@ -3243,6 +3201,92 @@ unsafe fn split_at_spare_mut_with_len(
// - `cap / N` fits the size of the allocated memory after shrinking
unsafe { Vec::from_raw_parts_in(ptr.cast(), len / N, cap / N, alloc) }
}
/// This clears out this `Vec` and recycles the allocation into a new `Vec`.
/// The item type of the resulting `Vec` needs to have the same size and
/// alignment as the item type of the original `Vec`.
///
/// # Examples
///
/// ```
/// #![feature(vec_recycle, transmutability)]
/// let a: Vec<u8> = vec![0; 100];
/// let capacity = a.capacity();
/// let addr = a.as_ptr().addr();
/// let b: Vec<i8> = a.recycle();
/// assert_eq!(b.len(), 0);
/// assert_eq!(b.capacity(), capacity);
/// assert_eq!(b.as_ptr().addr(), addr);
/// ```
///
/// The `Recyclable` bound prevents this method from being called when `T` and `U` have different sizes; e.g.:
///
/// ```compile_fail,E0277
/// #![feature(vec_recycle, transmutability)]
/// let vec: Vec<[u8; 2]> = Vec::new();
/// let _: Vec<[u8; 1]> = vec.recycle();
/// ```
/// ...or different alignments:
///
/// ```compile_fail,E0277
/// #![feature(vec_recycle, transmutability)]
/// let vec: Vec<[u16; 0]> = Vec::new();
/// let _: Vec<[u8; 0]> = vec.recycle();
/// ```
///
/// However, due to temporary implementation limitations of `Recyclable`,
/// this method is not yet callable when `T` or `U` are slices, trait objects,
/// or other exotic types; e.g.:
///
/// ```compile_fail,E0277
/// #![feature(vec_recycle, transmutability)]
/// # let inputs = ["a b c", "d e f"];
/// # fn process(_: &[&str]) {}
/// let mut storage: Vec<&[&str]> = Vec::new();
///
/// for input in inputs {
/// let mut buffer: Vec<&str> = storage.recycle();
/// buffer.extend(input.split(" "));
/// process(&buffer);
/// storage = buffer.recycle();
/// }
/// ```
#[unstable(feature = "vec_recycle", issue = "148227")]
#[expect(private_bounds)]
pub fn recycle<U>(mut self) -> Vec<U, A>
where
U: Recyclable<T>,
{
self.clear();
const {
// FIXME(const-hack, 146097): compare `Layout`s
assert!(size_of::<T>() == size_of::<U>());
assert!(align_of::<T>() == align_of::<U>());
};
let (ptr, length, capacity, alloc) = self.into_parts_with_alloc();
debug_assert_eq!(length, 0);
// SAFETY:
// - `ptr` and `alloc` were just returned from `self.into_raw_parts_with_alloc()`
// - `T` & `U` have the same layout, so `capacity` does not need to be changed and we can safely use `alloc.dealloc` later
// - the original vector was cleared, so there is no problem with "transmuting" the stored values
unsafe { Vec::from_parts_in(ptr.cast::<U>(), length, capacity, alloc) }
}
}
/// Denotes that an allocation of `From` can be recycled into an allocation of `Self`.
///
/// # Safety
///
/// `Self` is `Recyclable<From>` if `Layout::new::<Self>() == Layout::new::<From>()`.
unsafe trait Recyclable<From: Sized>: Sized {}
#[unstable_feature_bound(transmutability)]
// SAFETY: enforced by `TransmuteFrom`
unsafe impl<From, To> Recyclable<From> for To
where
for<'a> &'a MaybeUninit<To>: TransmuteFrom<&'a MaybeUninit<From>, { Assume::SAFETY }>,
for<'a> &'a MaybeUninit<From>: TransmuteFrom<&'a MaybeUninit<To>, { Assume::SAFETY }>,
{
}
impl<T: Clone, A: Allocator> Vec<T, A> {
@@ -3889,7 +3933,8 @@ pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoI
///
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
/// or the iteration short-circuits, then the remaining elements will be retained.
/// Use [`retain_mut`] with a negated predicate if you do not need the returned iterator.
/// Use `extract_if().for_each(drop)` if you do not need the returned iterator,
/// or [`retain_mut`] with a negated predicate if you also do not need to restrict the range.
///
/// [`retain_mut`]: Vec::retain_mut
///
+4 -4
View File
@@ -15,18 +15,18 @@
// actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the
// links to `CString` and `String` for now until a solution is developed
/// Representation of a borrowed C string.
/// A dynamically-sized view of a C string.
///
/// This type represents a borrowed reference to a nul-terminated
/// The type `&CStr` represents a reference to a borrowed nul-terminated
/// array of bytes. It can be constructed safely from a <code>&[[u8]]</code>
/// slice, or unsafely from a raw `*const c_char`. It can be expressed as a
/// literal in the form `c"Hello world"`.
///
/// The `CStr` can then be converted to a Rust <code>&[str]</code> by performing
/// The `&CStr` can then be converted to a Rust <code>&[str]</code> by performing
/// UTF-8 validation, or into an owned `CString`.
///
/// `&CStr` is to `CString` as <code>&[str]</code> is to `String`: the former
/// in each pair are borrowed references; the latter are owned
/// in each pair are borrowing references; the latter are owned
/// strings.
///
/// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)`
+15 -11
View File
@@ -734,17 +734,6 @@ pub unsafe fn new<const N: usize, const M: usize>(
unsafe { Arguments { template: mem::transmute(template), args: mem::transmute(args) } }
}
#[inline]
pub const fn from_str(s: &'static str) -> Arguments<'a> {
// SAFETY: This is the "static str" representation of fmt::Arguments; see above.
unsafe {
Arguments {
template: mem::transmute(s.as_ptr()),
args: mem::transmute(s.len() << 1 | 1),
}
}
}
// Same as `from_str`, but not const.
// Used by format_args!() expansion when arguments are inlined,
// e.g. format_args!("{}", 123), which is not allowed in const.
@@ -818,6 +807,21 @@ pub fn estimated_capacity(&self) -> usize {
}
impl<'a> Arguments<'a> {
/// Create a `fmt::Arguments` object for a single static string.
///
/// Formatting this `fmt::Arguments` will just produce the string as-is.
#[inline]
#[unstable(feature = "fmt_arguments_from_str", issue = "148905")]
pub const fn from_str(s: &'static str) -> Arguments<'a> {
// SAFETY: This is the "static str" representation of fmt::Arguments; see above.
unsafe {
Arguments {
template: mem::transmute(s.as_ptr()),
args: mem::transmute(s.len() << 1 | 1),
}
}
}
/// Gets the formatted string, if it has no arguments to be formatted at runtime.
///
/// This can be used to avoid allocations in some cases.
+2 -6
View File
@@ -769,13 +769,9 @@ pub const fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T
/// // in terms of converting the original inner type (`&i32`) to the new one (`Option<&i32>`),
/// // this has all the same caveats. Besides the information provided above, also consult the
/// // [`from_raw_parts`] documentation.
/// let (ptr, len, capacity) = v_clone.into_raw_parts();
/// let v_from_raw = unsafe {
// FIXME Update this when vec_into_raw_parts is stabilized
/// // Ensure the original vector is not dropped.
/// let mut v_clone = std::mem::ManuallyDrop::new(v_clone);
/// Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>,
/// v_clone.len(),
/// v_clone.capacity())
/// Vec::from_raw_parts(ptr.cast::<*mut Option<&i32>>(), len, capacity)
/// };
/// ```
///
+3 -3
View File
@@ -991,7 +991,7 @@ macro_rules! compile_error {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "format_args_macro"]
#[allow_internal_unsafe]
#[allow_internal_unstable(fmt_internals)]
#[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)]
#[rustc_builtin_macro]
#[macro_export]
macro_rules! format_args {
@@ -1005,7 +1005,7 @@ macro_rules! format_args {
///
/// This macro will be removed once `format_args` is allowed in const contexts.
#[unstable(feature = "const_format_args", issue = "none")]
#[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)]
#[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)]
#[rustc_builtin_macro]
#[macro_export]
macro_rules! const_format_args {
@@ -1020,7 +1020,7 @@ macro_rules! const_format_args {
reason = "`format_args_nl` is only for internal \
language use and is subject to change"
)]
#[allow_internal_unstable(fmt_internals)]
#[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)]
#[rustc_builtin_macro]
#[doc(hidden)]
#[macro_export]
+1
View File
@@ -1770,6 +1770,7 @@ pub fn rem_euclid(self, rhs: f128) -> f128 {
/// assert!(abs_difference <= f128::EPSILON);
///
/// assert_eq!(f128::powi(f128::NAN, 0), 1.0);
/// assert_eq!(f128::powi(0.0, 0), 1.0);
/// # }
/// ```
#[inline]
+1
View File
@@ -1745,6 +1745,7 @@ pub fn rem_euclid(self, rhs: f16) -> f16 {
/// assert!(abs_difference <= f16::EPSILON);
///
/// assert_eq!(f16::powi(f16::NAN, 0), 1.0);
/// assert_eq!(f16::powi(0.0, 0), 1.0);
/// # }
/// ```
#[inline]
+6
View File
@@ -1720,6 +1720,7 @@ pub const fn strict_abs(self) -> Self {
///
/// ```
#[doc = concat!("assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64));")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".checked_pow(0), Some(1));")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")]
/// ```
@@ -1761,6 +1762,7 @@ pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
///
/// ```
#[doc = concat!("assert_eq!(8", stringify!($SelfT), ".strict_pow(2), 64);")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".strict_pow(0), 1);")]
/// ```
///
/// The following panics because of overflow:
@@ -2033,6 +2035,7 @@ pub const fn saturating_div(self, rhs: Self) -> Self {
///
/// ```
#[doc = concat!("assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64);")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".saturating_pow(0), 1);")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX);")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);")]
/// ```
@@ -2377,6 +2380,7 @@ pub const fn unsigned_abs(self) -> $UnsignedT {
#[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81);")]
/// assert_eq!(3i8.wrapping_pow(5), -13);
/// assert_eq!(3i8.wrapping_pow(6), -39);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".wrapping_pow(0), 1);")]
/// ```
#[stable(feature = "no_panic_pow", since = "1.34.0")]
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
@@ -2967,6 +2971,7 @@ pub const fn overflowing_abs(self) -> (Self, bool) {
///
/// ```
#[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false));")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".overflowing_pow(0), (1, false));")]
/// assert_eq!(3i8.overflowing_pow(5), (-13, true));
/// ```
#[stable(feature = "no_panic_pow", since = "1.34.0")]
@@ -3010,6 +3015,7 @@ pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) {
#[doc = concat!("let x: ", stringify!($SelfT), " = 2; // or any other integer type")]
///
/// assert_eq!(x.pow(5), 32);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".pow(0), 1);")]
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
+6
View File
@@ -2055,6 +2055,7 @@ pub const fn shr_exact(self, rhs: u32) -> Option<$SelfT> {
///
/// ```
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32));")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".checked_pow(0), Some(1));")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")]
/// ```
#[stable(feature = "no_panic_pow", since = "1.34.0")]
@@ -2095,6 +2096,7 @@ pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
///
/// ```
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".strict_pow(5), 32);")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".strict_pow(0), 1);")]
/// ```
///
/// The following panics because of overflow:
@@ -2269,6 +2271,7 @@ pub const fn saturating_div(self, rhs: Self) -> Self {
///
/// ```
#[doc = concat!("assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64);")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".saturating_pow(0), 1);")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);")]
/// ```
#[stable(feature = "no_panic_pow", since = "1.34.0")]
@@ -2578,6 +2581,7 @@ pub const fn wrapping_shr(self, rhs: u32) -> Self {
/// ```
#[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243);")]
/// assert_eq!(3u8.wrapping_pow(6), 217);
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".wrapping_pow(0), 1);")]
/// ```
#[stable(feature = "no_panic_pow", since = "1.34.0")]
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
@@ -3252,6 +3256,7 @@ pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
///
/// ```
#[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false));")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".overflowing_pow(0), (1, false));")]
/// assert_eq!(3u8.overflowing_pow(6), (217, true));
/// ```
#[stable(feature = "no_panic_pow", since = "1.34.0")]
@@ -3293,6 +3298,7 @@ pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) {
///
/// ```
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".pow(5), 32);")]
#[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".pow(0), 1);")]
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]
+16 -3
View File
@@ -359,11 +359,24 @@ pub fn from_yeet<T, Y>(yeeted: Y) -> T
/// and in the other direction,
/// `<Result<Infallible, E> as Residual<T>>::TryType = Result<T, E>`.
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
#[rustc_const_unstable(feature = "const_try", issue = "74935")]
pub const trait Residual<O> {
#[rustc_const_unstable(feature = "const_try_residual", issue = "91285")]
pub const trait Residual<O>: Sized {
/// The "return" type of this meta-function.
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
type TryType: Try<Output = O, Residual = Self>;
type TryType: [const] Try<Output = O, Residual = Self>;
}
/// Used in `try {}` blocks so the type produced in the `?` desugaring
/// depends on the residual type `R` and the output type of the block `O`,
/// but importantly not on the contextual type the way it would be if
/// we called `<_ as FromResidual>::from_residual(r)` directly.
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
// needs to be `pub` to avoid `private type` errors
#[expect(unreachable_pub)]
#[inline] // FIXME: force would be nice, but fails -- see #148915
#[lang = "into_try_type"]
pub fn residual_into_try_type<R: Residual<O>, O>(r: R) -> <R as Residual<O>>::TryType {
FromResidual::from_residual(r)
}
#[unstable(feature = "pub_crate_should_not_need_unstable_attr", issue = "none")]
+3 -4
View File
@@ -1531,9 +1531,8 @@ mod prim_usize {}
/// `&mut T` references can be freely coerced into `&T` references with the same referent type, and
/// references with longer lifetimes can be freely coerced into references with shorter ones.
///
/// Reference equality by address, instead of comparing the values pointed to, is accomplished via
/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while
/// [`PartialEq`] compares values.
/// [`PartialEq`] will compare referenced values. It is possible to compare the reference address
/// using reference-pointer coercion and raw pointer equality via [`ptr::eq`].
///
/// ```
/// use std::ptr;
@@ -1648,7 +1647,7 @@ mod prim_usize {}
/// For the other direction, things are more complicated: when unsafe code passes arguments
/// to safe functions or returns values from safe functions, they generally must *at least*
/// not violate these invariants. The full requirements are stronger, as the reference generally
/// must point to data that is safe to use at type `T`.
/// must point to data that is safe to use as type `T`.
///
/// It is not decided yet whether unsafe code may violate these invariants temporarily on internal
/// data. As a consequence, unsafe code which violates these invariants temporarily on internal data
+1 -37
View File
@@ -1352,40 +1352,6 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
/// assert_eq!(x, [7, 8, 3, 4]);
/// assert_eq!(y, [1, 2, 9]);
/// ```
///
/// # Const evaluation limitations
///
/// If this function is invoked during const-evaluation, the current implementation has a small (and
/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y`
/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may
/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the
/// future.
///
/// The limitation is illustrated by the following example:
///
/// ```
/// use std::mem::size_of;
/// use std::ptr;
///
/// const { unsafe {
/// const PTR_SIZE: usize = size_of::<*const i32>();
/// let mut data1 = [0u8; PTR_SIZE];
/// let mut data2 = [0u8; PTR_SIZE];
/// // Store a pointer in `data1`.
/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42);
/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks.
/// // This call will fail, because the pointer in `data1` crosses the boundary
/// // between several of the 1-byte chunks that are being swapped here.
/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE);
/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size
/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between
/// // two chunks.
/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1);
/// // Read the pointer from `data2` and dereference it.
/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned();
/// assert!(*ptr == 42);
/// } }
/// ```
#[inline]
#[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
#[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")]
@@ -1414,9 +1380,7 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
const_eval_select!(
@capture[T] { x: *mut T, y: *mut T, count: usize }:
if const {
// At compile-time we want to always copy this in chunks of `T`, to ensure that if there
// are pointers inside `T` we will copy them in one go rather than trying to copy a part
// of a pointer (which would not work).
// At compile-time we don't need all the special code below.
// SAFETY: Same preconditions as this function
unsafe { swap_nonoverlapping_const(x, y, count) }
} else {
+52 -9
View File
@@ -3,6 +3,7 @@
};
use crate::num::NonZero;
use crate::range::{Range, RangeFrom, RangeInclusive, legacy};
use crate::{intrinsics, mem};
/// By-value [`Range`] iterator.
#[unstable(feature = "new_range_api", issue = "125687")]
@@ -168,7 +169,7 @@ pub fn remainder(self) -> Option<RangeInclusive<A>> {
}
}
#[unstable(feature = "trusted_random_access", issue = "none")]
#[unstable(feature = "new_range_api", issue = "125687")]
impl<A: Step> Iterator for IterRangeInclusive<A> {
type Item = A;
@@ -293,32 +294,74 @@ impl ExactSizeIterator for IterRangeInclusive<$t> { }
/// By-value [`RangeFrom`] iterator.
#[unstable(feature = "new_range_api", issue = "125687")]
#[derive(Debug, Clone)]
pub struct IterRangeFrom<A>(legacy::RangeFrom<A>);
pub struct IterRangeFrom<A> {
start: A,
/// Whether the first element of the iterator has yielded.
/// Only used when overflow checks are enabled.
first: bool,
}
impl<A> IterRangeFrom<A> {
impl<A: Step> IterRangeFrom<A> {
/// Returns the remainder of the range being iterated over.
#[inline]
#[rustc_inherit_overflow_checks]
pub fn remainder(self) -> RangeFrom<A> {
RangeFrom { start: self.0.start }
if intrinsics::overflow_checks() {
if !self.first {
return RangeFrom { start: Step::forward(self.start, 1) };
}
}
RangeFrom { start: self.start }
}
}
#[unstable(feature = "trusted_random_access", issue = "none")]
#[unstable(feature = "new_range_api", issue = "125687")]
impl<A: Step> Iterator for IterRangeFrom<A> {
type Item = A;
#[inline]
#[rustc_inherit_overflow_checks]
fn next(&mut self) -> Option<A> {
self.0.next()
if intrinsics::overflow_checks() {
if self.first {
self.first = false;
return Some(self.start.clone());
}
self.start = Step::forward(self.start.clone(), 1);
return Some(self.start.clone());
}
let n = Step::forward(self.start.clone(), 1);
Some(mem::replace(&mut self.start, n))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
(usize::MAX, None)
}
#[inline]
#[rustc_inherit_overflow_checks]
fn nth(&mut self, n: usize) -> Option<A> {
self.0.nth(n)
if intrinsics::overflow_checks() {
if self.first {
self.first = false;
let plus_n = Step::forward(self.start.clone(), n);
self.start = plus_n.clone();
return Some(plus_n);
}
let plus_n = Step::forward(self.start.clone(), n);
self.start = Step::forward(plus_n.clone(), 1);
return Some(self.start.clone());
}
let plus_n = Step::forward(self.start.clone(), n);
self.start = Step::forward(plus_n.clone(), 1);
Some(plus_n)
}
}
@@ -334,6 +377,6 @@ impl<A: Step> IntoIterator for RangeFrom<A> {
type IntoIter = IterRangeFrom<A>;
fn into_iter(self) -> Self::IntoIter {
IterRangeFrom(self.into())
IterRangeFrom { start: self.start, first: true }
}
}
-1
View File
@@ -350,7 +350,6 @@ fn find_map<B, F>(&mut self, mut f: F) -> Option<B>
// because this simple implementation generates less LLVM IR and is
// faster to compile. Also, the `assume` avoids a bounds check.
#[inline]
#[rustc_inherit_overflow_checks]
fn position<P>(&mut self, mut predicate: P) -> Option<usize> where
Self: Sized,
P: FnMut(Self::Item) -> bool,
+4 -5
View File
@@ -945,13 +945,12 @@ struct S {
assert!(*s1.0.ptr == 666);
assert!(*s2.0.ptr == 1);
// Swap them back, again as an array.
// FIXME(#146291): we should be swapping back at type `u8` but that currently does not work.
// Swap them back, byte-for-byte
unsafe {
ptr::swap_nonoverlapping(
ptr::from_mut(&mut s1).cast::<T>(),
ptr::from_mut(&mut s2).cast::<T>(),
1,
ptr::from_mut(&mut s1).cast::<u8>(),
ptr::from_mut(&mut s2).cast::<u8>(),
size_of::<A>(),
);
}
+2 -1
View File
@@ -1685,7 +1685,8 @@ pub(super) fn iter(&self) -> Iter<'_, K, V> {
/// let iter = map.extract_if(|_k, v| *v % 2 == 0);
/// ```
#[stable(feature = "hash_extract_if", since = "1.88.0")]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain` to remove and discard elements"]
pub struct ExtractIf<'a, K, V, F> {
base: base::ExtractIf<'a, K, V, F>,
}
+2
View File
@@ -1391,6 +1391,8 @@ pub struct Drain<'a, K: 'a> {
/// let mut extract_ifed = a.extract_if(|v| v % 2 == 0);
/// ```
#[stable(feature = "hash_extract_if", since = "1.88.0")]
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain` to remove and discard elements"]
pub struct ExtractIf<'a, K, F> {
base: base::ExtractIf<'a, K, F>,
}
+41 -1
View File
@@ -330,7 +330,7 @@
stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout},
util::{Empty, Repeat, Sink, empty, repeat, sink},
};
use crate::mem::take;
use crate::mem::{MaybeUninit, take};
use crate::ops::{Deref, DerefMut};
use crate::{cmp, fmt, slice, str, sys};
@@ -1242,6 +1242,46 @@ fn take(self, limit: u64) -> Take<Self>
{
Take { inner: self, len: limit, limit }
}
/// Read and return a fixed array of bytes from this source.
///
/// This function uses an array sized based on a const generic size known at compile time. You
/// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference
/// determine the number of bytes needed based on how the return value gets used. For instance,
/// this function works well with functions like [`u64::from_le_bytes`] to turn an array of
/// bytes into an integer of the same size.
///
/// Like `read_exact`, if this function encounters an "end of file" before reading the desired
/// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
///
/// ```
/// #![feature(read_array)]
/// use std::io::Cursor;
/// use std::io::prelude::*;
///
/// fn main() -> std::io::Result<()> {
/// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]);
/// let x = u64::from_le_bytes(buf.read_array()?);
/// let y = u32::from_be_bytes(buf.read_array()?);
/// let z = u16::from_be_bytes(buf.read_array()?);
/// assert_eq!(x, 0x807060504030201);
/// assert_eq!(y, 0x9080706);
/// assert_eq!(z, 0x504);
/// Ok(())
/// }
/// ```
#[unstable(feature = "read_array", issue = "148848")]
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]>
where
Self: Sized,
{
let mut buf = [MaybeUninit::uninit(); N];
let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice());
self.read_buf_exact(borrowed_buf.unfilled())?;
// Guard against incorrect `read_buf_exact` implementations.
assert_eq!(borrowed_buf.len(), N);
Ok(unsafe { MaybeUninit::array_assume_init(buf) })
}
}
/// Reads all bytes from a [reader][Read] into a new [`String`].
+1 -1
View File
@@ -348,6 +348,7 @@
#![feature(int_from_ascii)]
#![feature(ip)]
#![feature(lazy_get)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_slice)]
#![feature(maybe_uninit_write_slice)]
#![feature(panic_can_unwind)]
@@ -381,7 +382,6 @@
#![feature(try_reserve_kind)]
#![feature(try_with_capacity)]
#![feature(unique_rc_arc)]
#![feature(vec_into_raw_parts)]
#![feature(wtf8_internals)]
// tidy-alphabetical-end
//
+1
View File
@@ -37,6 +37,7 @@ impl f128 {
///
/// assert_eq!(f128::powf(1.0, f128::NAN), 1.0);
/// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0);
/// assert_eq!(f128::powf(0.0, 0.0), 1.0);
/// # }
/// ```
#[inline]
+1
View File
@@ -37,6 +37,7 @@ impl f16 {
///
/// assert_eq!(f16::powf(1.0, f16::NAN), 1.0);
/// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0);
/// assert_eq!(f16::powf(0.0, 0.0), 1.0);
/// # }
/// ```
#[inline]
+2
View File
@@ -308,6 +308,7 @@ pub fn rem_euclid(self, rhs: f32) -> f32 {
/// assert!(abs_difference <= 1e-5);
///
/// assert_eq!(f32::powi(f32::NAN, 0), 1.0);
/// assert_eq!(f32::powi(0.0, 0), 1.0);
/// ```
#[rustc_allow_incoherent_impl]
#[must_use = "method returns a new number and does not mutate the original value"]
@@ -333,6 +334,7 @@ pub fn powi(self, n: i32) -> f32 {
///
/// assert_eq!(f32::powf(1.0, f32::NAN), 1.0);
/// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0);
/// assert_eq!(f32::powf(0.0, 0.0), 1.0);
/// ```
#[rustc_allow_incoherent_impl]
#[must_use = "method returns a new number and does not mutate the original value"]
+2
View File
@@ -308,6 +308,7 @@ pub fn rem_euclid(self, rhs: f64) -> f64 {
/// assert!(abs_difference <= 1e-14);
///
/// assert_eq!(f64::powi(f64::NAN, 0), 1.0);
/// assert_eq!(f64::powi(0.0, 0), 1.0);
/// ```
#[rustc_allow_incoherent_impl]
#[must_use = "method returns a new number and does not mutate the original value"]
@@ -333,6 +334,7 @@ pub fn powi(self, n: i32) -> f64 {
///
/// assert_eq!(f64::powf(1.0, f64::NAN), 1.0);
/// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0);
/// assert_eq!(f64::powf(0.0, 0.0), 1.0);
/// ```
#[rustc_allow_incoherent_impl]
#[must_use = "method returns a new number and does not mutate the original value"]
+5
View File
@@ -80,6 +80,9 @@ pub trait CommandExt: Sealed {
/// or acquiring a mutex are not guaranteed to work (due to
/// other threads perhaps still running when the `fork` was run).
///
/// Note that the list of allocating functions includes [`Error::new`] and
/// [`Error::other`]. To signal a non-trivial error, prefer [`panic!`].
///
/// For further details refer to the [POSIX fork() specification]
/// and the equivalent documentation for any targeted
/// platform, especially the requirements around *async-signal-safety*.
@@ -102,6 +105,8 @@ pub trait CommandExt: Sealed {
/// [POSIX fork() specification]:
/// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
/// [`std::env`]: mod@crate::env
/// [`Error::new`]: crate::io::Error::new
/// [`Error::other`]: crate::io::Error::other
#[stable(feature = "process_pre_exec", since = "1.34.0")]
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
where
+37 -6
View File
@@ -18,6 +18,9 @@
pub struct FileAttr {
attr: u64,
size: u64,
accessed: SystemTime,
modified: SystemTime,
created: SystemTime,
}
pub struct ReadDir(!);
@@ -33,7 +36,10 @@ pub struct OpenOptions {
}
#[derive(Copy, Clone, Debug, Default)]
pub struct FileTimes {}
pub struct FileTimes {
accessed: Option<SystemTime>,
modified: Option<SystemTime>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
// Bool indicates if file is readonly
@@ -60,15 +66,15 @@ pub fn file_type(&self) -> FileType {
}
pub fn modified(&self) -> io::Result<SystemTime> {
unsupported()
Ok(self.modified)
}
pub fn accessed(&self) -> io::Result<SystemTime> {
unsupported()
Ok(self.accessed)
}
pub fn created(&self) -> io::Result<SystemTime> {
unsupported()
Ok(self.created)
}
}
@@ -92,8 +98,13 @@ const fn to_attr(&self) -> u64 {
}
impl FileTimes {
pub fn set_accessed(&mut self, _t: SystemTime) {}
pub fn set_modified(&mut self, _t: SystemTime) {}
pub fn set_accessed(&mut self, t: SystemTime) {
self.accessed = Some(t);
}
pub fn set_modified(&mut self, t: SystemTime) {
self.modified = Some(t);
}
}
impl FileType {
@@ -394,6 +405,7 @@ mod uefi_fs {
use crate::path::Path;
use crate::ptr::NonNull;
use crate::sys::helpers;
use crate::sys::time::{self, SystemTime};
pub(crate) struct File(NonNull<file::Protocol>);
@@ -541,4 +553,23 @@ pub(crate) fn mkdir(path: &Path) -> io::Result<()> {
Ok(())
}
/// EDK2 FAT driver uses EFI_UNSPECIFIED_TIMEZONE to represent localtime. So for proper
/// conversion to SystemTime, we use the current time to get the timezone in such cases.
#[expect(dead_code)]
fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> SystemTime {
time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
time::system_time_internal::now().unwrap().timezone
} else {
time.timezone
};
SystemTime::from_uefi(time)
}
/// Convert to UEFI Time with the current timezone.
#[expect(dead_code)]
fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
let now = time::system_time_internal::now().unwrap();
time.to_uefi_loose(now.timezone, now.daylight)
}
}

Some files were not shown because too many files have changed in this diff Show More