From 0b6a44cb44d668504ebac2c69d33718c1bec8af4 Mon Sep 17 00:00:00 2001 From: Michael Bradshaw Date: Thu, 4 Oct 2018 16:43:44 -0700 Subject: [PATCH 1/5] Make core::mem::MaybeUninit::zeroed a const fn --- src/libcore/mem.rs | 8 ++++++ src/librustc_mir/interpret/intrinsics.rs | 3 +++ src/librustc_mir/transform/qualify_consts.rs | 1 + src/test/run-pass/const-maybe-init-zeroed.rs | 26 ++++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 src/test/run-pass/const-maybe-init-zeroed.rs diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 27ee9556bd089..5cda02c946ce3 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1039,6 +1039,14 @@ impl MaybeUninit { /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. #[unstable(feature = "maybe_uninit", issue = "53491")] + #[cfg(not(stage0))] + pub const fn zeroed() -> MaybeUninit { + MaybeUninit { value: unsafe { intrinsics::init() } } + } + + #[unstable(feature = "maybe_uninit", issue = "53491")] + #[cfg(stage0)] + /// Ceci n'est pas la documentation pub fn zeroed() -> MaybeUninit { let mut u = MaybeUninit::::uninitialized(); unsafe { diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 5fee49ba2fcf2..2b0f5db1ff5d8 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -150,6 +150,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> } self.write_scalar(val, dest)?; } + "init" => { + self.force_allocation(dest)?; + } "transmute" => { // Go through an allocation, to make sure the completely different layouts // do not pose a problem. (When the user transmutes through a union, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 17fe78d325cf1..ceba1439e38af 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -839,6 +839,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" + | "init" // no need to check feature gates, intrinsics are only callable from the // libstd or with forever unstable feature gates => is_const_fn = true, diff --git a/src/test/run-pass/const-maybe-init-zeroed.rs b/src/test/run-pass/const-maybe-init-zeroed.rs new file mode 100644 index 0000000000000..fb8bb2ee0b965 --- /dev/null +++ b/src/test/run-pass/const-maybe-init-zeroed.rs @@ -0,0 +1,26 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(maybe_uninit)] + +use std::mem; + +fn main() { + const UNIT: mem::MaybeUninit<()> = mem::MaybeUninit::zeroed(); + let bytes: [u8; 0] = unsafe { mem::transmute(UNIT) }; + assert_eq!(bytes, [0u8; 0]); + + const STRING: mem::MaybeUninit = mem::MaybeUninit::zeroed(); + let bytes: [u8; mem::size_of::()] = unsafe { mem::transmute(STRING) }; + assert_eq!(bytes, [0u8; mem::size_of::()]); + + const U8: mem::MaybeUninit = mem::MaybeUninit::zeroed(); + let bytes: [u8; 1] = unsafe { mem::transmute(U8) }; + assert_eq!(bytes, [0u8; 1]); +} From 255f7233ad40e96d7433f870869788c9bc0e6af2 Mon Sep 17 00:00:00 2001 From: Michael Bradshaw Date: Fri, 5 Oct 2018 06:23:50 -0700 Subject: [PATCH 2/5] Add a feature gate for MaybeUninit::zeroed's constness --- src/libcore/mem.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 5cda02c946ce3..854de8d0f0317 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1039,22 +1039,11 @@ impl MaybeUninit { /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. #[unstable(feature = "maybe_uninit", issue = "53491")] - #[cfg(not(stage0))] + #[rustc_const_unstable(feature = "const_maybe_uninit_zeroed")] pub const fn zeroed() -> MaybeUninit { MaybeUninit { value: unsafe { intrinsics::init() } } } - #[unstable(feature = "maybe_uninit", issue = "53491")] - #[cfg(stage0)] - /// Ceci n'est pas la documentation - pub fn zeroed() -> MaybeUninit { - let mut u = MaybeUninit::::uninitialized(); - unsafe { - u.as_mut_ptr().write_bytes(0u8, 1); - } - u - } - /// Set the value of the `MaybeUninit`. This overwrites any previous value without dropping it. #[unstable(feature = "maybe_uninit", issue = "53491")] pub fn set(&mut self, val: T) { From b18f9468b4a4abc1b3bb28ffc8ea01ea7bc5fc5f Mon Sep 17 00:00:00 2001 From: Michael Bradshaw Date: Fri, 5 Oct 2018 06:51:26 -0700 Subject: [PATCH 3/5] Fix the emulation of the init intrinsic Taken from https://github.com/solson/miri/blob/317f90525a9620d9957b3bf37f8d605f50a8c889/src/intrinsic.rs#L238-L260 --- src/librustc_mir/interpret/intrinsics.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 2b0f5db1ff5d8..46626e66a4688 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -151,7 +151,27 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.write_scalar(val, dest)?; } "init" => { - self.force_allocation(dest)?; + // Check fast path: we don't want to force an allocation in case the destination is a simple value, + // but we also do not want to create a new allocation with 0s and then copy that over. + if !dest.layout.is_zst() { // notzhing to do for ZST + match dest.layout.abi { + layout::Abi::Scalar(ref s) => { + let x = Scalar::from_int(0, s.value.size(&self)); + self.write_value(Value::Scalar(x.into()), dest)?; + } + layout::Abi::ScalarPair(ref s1, ref s2) => { + let x = Scalar::from_int(0, s1.value.size(&self)); + let y = Scalar::from_int(0, s2.value.size(&self)); + self.write_value(Value::ScalarPair(x.into(), y.into()), dest)?; + } + _ => { + // Do it in memory + let mplace = self.force_allocation(dest)?; + assert!(mplace.extra.is_none()); + self.memory.write_repeat(mplace.ptr, 0, dest.layout.size)?; + } + } + } } "transmute" => { // Go through an allocation, to make sure the completely different layouts From fde2e91bb1ee99b05036cf8d9294b4adbc8c515f Mon Sep 17 00:00:00 2001 From: Michael Bradshaw Date: Fri, 5 Oct 2018 08:06:17 -0700 Subject: [PATCH 4/5] Wrap to 100 columns --- src/librustc_mir/interpret/intrinsics.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 46626e66a4688..1959be331d229 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -151,8 +151,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.write_scalar(val, dest)?; } "init" => { - // Check fast path: we don't want to force an allocation in case the destination is a simple value, - // but we also do not want to create a new allocation with 0s and then copy that over. + // Check fast path: we don't want to force an allocation in case the destination is + // a simple value, but we also do not want to create a new allocation with 0s and + // then copy that over. if !dest.layout.is_zst() { // notzhing to do for ZST match dest.layout.abi { layout::Abi::Scalar(ref s) => { From e5eb54c4a8875bfae9d78c6d4a2543a50b3780e4 Mon Sep 17 00:00:00 2001 From: Michael Bradshaw Date: Fri, 5 Oct 2018 15:06:23 -0700 Subject: [PATCH 5/5] Make core::ptr::write_bytes a const fn gated behind const_write_bytes Use this to implement MaybeUninit::zeroed, and make the init intrinsic no longer const --- src/libcore/lib.rs | 1 + src/libcore/mem.rs | 18 +++++++- src/librustc_mir/interpret/intrinsics.rs | 41 ++++++++----------- src/librustc_mir/transform/qualify_consts.rs | 14 ++++++- src/libsyntax/feature_gate.rs | 3 ++ src/test/run-pass/const-maybe-init-zeroed.rs | 2 +- .../feature-gate-const_write_bytes.rs | 18 ++++++++ .../feature-gate-const_write_bytes.stderr | 11 +++++ 8 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 src/test/ui/feature-gates/feature-gate-const_write_bytes.rs create mode 100644 src/test/ui/feature-gates/feature-gate-const_write_bytes.stderr diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 184ed19da9524..1e9dff108ab77 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -128,6 +128,7 @@ #![feature(const_transmute)] #![feature(reverse_bits)] #![feature(non_exhaustive)] +#![cfg_attr(not(stage0), feature(const_write_bytes))] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 854de8d0f0317..e27452f99f342 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1040,8 +1040,24 @@ impl MaybeUninit { /// It is your responsibility to make sure `T` gets dropped if it got initialized. #[unstable(feature = "maybe_uninit", issue = "53491")] #[rustc_const_unstable(feature = "const_maybe_uninit_zeroed")] + #[cfg(not(stage0))] pub const fn zeroed() -> MaybeUninit { - MaybeUninit { value: unsafe { intrinsics::init() } } + let mut u = MaybeUninit::::uninitialized(); + unsafe { + (&mut *u.value as *mut T).write_bytes(0u8, 1); + } + u + } + + #[unstable(feature = "maybe_uninit", issue = "53491")] + #[cfg(stage0)] + /// Ceci n'est pas la documentation + pub fn zeroed() -> MaybeUninit { + let mut u = MaybeUninit::::uninitialized(); + unsafe { + u.as_mut_ptr().write_bytes(0u8, 1); + } + u } /// Set the value of the `MaybeUninit`. This overwrites any previous value without dropping it. diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 1959be331d229..92004b71a0a4b 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -150,30 +150,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> } self.write_scalar(val, dest)?; } - "init" => { - // Check fast path: we don't want to force an allocation in case the destination is - // a simple value, but we also do not want to create a new allocation with 0s and - // then copy that over. - if !dest.layout.is_zst() { // notzhing to do for ZST - match dest.layout.abi { - layout::Abi::Scalar(ref s) => { - let x = Scalar::from_int(0, s.value.size(&self)); - self.write_value(Value::Scalar(x.into()), dest)?; - } - layout::Abi::ScalarPair(ref s1, ref s2) => { - let x = Scalar::from_int(0, s1.value.size(&self)); - let y = Scalar::from_int(0, s2.value.size(&self)); - self.write_value(Value::ScalarPair(x.into(), y.into()), dest)?; - } - _ => { - // Do it in memory - let mplace = self.force_allocation(dest)?; - assert!(mplace.extra.is_none()); - self.memory.write_repeat(mplace.ptr, 0, dest.layout.size)?; - } - } - } - } "transmute" => { // Go through an allocation, to make sure the completely different layouts // do not pose a problem. (When the user transmutes through a union, @@ -181,6 +157,23 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let dest = self.force_allocation(dest)?; self.copy_op(args[0], dest.into())?; } + "write_bytes" => { + let ptr = self.read_value(args[0])?; + let mplace = self.ref_to_mplace(ptr)?; + let val = self.read_scalar(args[1])?.to_u8()?; + let count = self.read_scalar(args[2])?.to_usize(&self.memory)?; + if mplace.layout.is_zst() || count == 0 { + return Ok(true); + } + if let Some(byte_count) = mplace.layout.size.checked_mul(count, &self.memory) { + self.memory.write_repeat(mplace.ptr, val, byte_count)?; + } else { + return err!(Intrinsic( + format!("Overflowing computing `count * size_of::()` ({} * {}) in {}", + count, mplace.layout.size.bytes(), intrinsic_name), + )); + } + } _ => return Ok(false), } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index ceba1439e38af..7830f88df23be 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -839,7 +839,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" - | "init" // no need to check feature gates, intrinsics are only callable from the // libstd or with forever unstable feature gates => is_const_fn = true, @@ -859,6 +858,19 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } } + "write_bytes" => { + if self.tcx.sess.features_untracked().const_write_bytes { + is_const_fn = true; + } else if self.mode != Mode::Fn { + // Using write_bytes in a const expression requires the feature + // gate. + emit_feature_err( + &self.tcx.sess.parse_sess, "const_write_bytes", + self.span, GateIssue::Language, + &format!("The use of std::ptr::write_bytes() \ + is gated in {}s", self.mode)); + } + } name if name.starts_with("simd_shuffle") => { is_shuffle = true; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index adbe2f9d4393f..d7b72050e906b 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -243,6 +243,9 @@ declare_features! ( // Allows panicking during const eval (produces compile-time errors) (active, const_panic, "1.30.0", Some(51999), None), + // Allows writing repeated bytes to a pointer during const eval. + (active, const_write_bytes, "1.31.0", Some(53491), None), + // Allows using #[prelude_import] on glob `use` items. // // rustc internal diff --git a/src/test/run-pass/const-maybe-init-zeroed.rs b/src/test/run-pass/const-maybe-init-zeroed.rs index fb8bb2ee0b965..5276804357fd1 100644 --- a/src/test/run-pass/const-maybe-init-zeroed.rs +++ b/src/test/run-pass/const-maybe-init-zeroed.rs @@ -7,7 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(maybe_uninit)] +#![feature(maybe_uninit, const_maybe_uninit_zeroed)] use std::mem; diff --git a/src/test/ui/feature-gates/feature-gate-const_write_bytes.rs b/src/test/ui/feature-gates/feature-gate-const_write_bytes.rs new file mode 100644 index 0000000000000..570dc99906474 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-const_write_bytes.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_fn, const_let)] + +fn main() {} + +const unsafe fn foo(u: *mut u32) { + std::ptr::write_bytes(u, 0u8, 1); + //~^ ERROR The use of std::ptr::write_bytes() is gated in constant functions (see issue #53491) +} diff --git a/src/test/ui/feature-gates/feature-gate-const_write_bytes.stderr b/src/test/ui/feature-gates/feature-gate-const_write_bytes.stderr new file mode 100644 index 0000000000000..91536a287bd41 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-const_write_bytes.stderr @@ -0,0 +1,11 @@ +error[E0658]: The use of std::ptr::write_bytes() is gated in constant functions (see issue #53491) + --> $DIR/feature-gate-const_write_bytes.rs:16:5 + | +LL | std::ptr::write_bytes(u, 0u8, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(const_write_bytes)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`.