mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 20:46:07 +03:00
Merge pull request #4699 from rust-lang/rustup-2025-11-17
Automatic Rustup
This commit is contained in:
+1
-1
@@ -48,7 +48,7 @@ no_llvm_build
|
||||
/llvm/
|
||||
/mingw-build/
|
||||
/build
|
||||
/build-rust-analyzer/
|
||||
/build-rust-analyzer
|
||||
/dist/
|
||||
/unicode-downloads
|
||||
/target
|
||||
|
||||
+29
-29
@@ -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]]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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, ®istry);
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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) = ©.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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
-16
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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);
|
||||
///
|
||||
|
||||
@@ -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
@@ -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
|
||||
///
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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`].
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user