From d15a98b87815850ef9dd11564e896b6f19b724c3 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 22 Feb 2020 14:03:46 +0100 Subject: [PATCH 01/28] Stabilize const for integer {to,from}_{be,le,ne}_bytes methods All of these functions can be implemented simply and naturally as const functions, e.g. u32::from_le_bytes can be implemented as (bytes[0] as u32) | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24 So stabilizing the constness will not expose that internally they are implemented using transmute which is not const in stable. --- src/libcore/lib.rs | 1 - src/libcore/num/mod.rs | 28 +++++++++++-------- .../ui/consts/const-int-conversion-rpass.rs | 2 -- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index bca96b77812c8..00909094cd0f5 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -131,7 +131,6 @@ #![feature(rtm_target_feature)] #![feature(f16c_target_feature)] #![feature(hexagon_target_feature)] -#![feature(const_int_conversion)] #![feature(const_transmute)] #![feature(structural_match)] #![feature(abi_unadjusted)] diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 6f55e7c8be8ca..22897680567e2 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2195,7 +2195,7 @@ let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); assert_eq!(bytes, ", $be_bytes, "); ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { self.to_be().to_ne_bytes() @@ -2215,7 +2215,7 @@ let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); assert_eq!(bytes, ", $le_bytes, "); ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { self.to_le().to_ne_bytes() @@ -2250,7 +2250,8 @@ assert_eq!( ); ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] + #[allow_internal_unstable(const_transmute)] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { // SAFETY: integers are plain old datatypes so we can always transmute them to @@ -2284,7 +2285,7 @@ fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), } ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { Self::from_be(Self::from_ne_bytes(bytes)) @@ -2317,7 +2318,7 @@ fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), } ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { Self::from_le(Self::from_ne_bytes(bytes)) @@ -2360,7 +2361,8 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), } ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] + #[allow_internal_unstable(const_transmute)] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { // SAFETY: integers are plain old datatypes so we can always transmute to them @@ -4132,7 +4134,7 @@ let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); assert_eq!(bytes, ", $be_bytes, "); ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { self.to_be().to_ne_bytes() @@ -4152,7 +4154,7 @@ let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); assert_eq!(bytes, ", $le_bytes, "); ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { self.to_le().to_ne_bytes() @@ -4187,7 +4189,8 @@ assert_eq!( ); ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] + #[allow_internal_unstable(const_transmute)] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { // SAFETY: integers are plain old datatypes so we can always transmute them to @@ -4221,7 +4224,7 @@ fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), } ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { Self::from_be(Self::from_ne_bytes(bytes)) @@ -4254,7 +4257,7 @@ fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), } ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] #[inline] pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { Self::from_le(Self::from_ne_bytes(bytes)) @@ -4297,7 +4300,8 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), } ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_unstable(feature = "const_int_conversion", issue = "53718")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] + #[allow_internal_unstable(const_transmute)] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { // SAFETY: integers are plain old datatypes so we can always transmute to them diff --git a/src/test/ui/consts/const-int-conversion-rpass.rs b/src/test/ui/consts/const-int-conversion-rpass.rs index d52dbbae1e7b5..6484169dd9ae1 100644 --- a/src/test/ui/consts/const-int-conversion-rpass.rs +++ b/src/test/ui/consts/const-int-conversion-rpass.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(const_int_conversion)] - const REVERSE: u32 = 0x12345678_u32.reverse_bits(); const FROM_BE_BYTES: i32 = i32::from_be_bytes([0x12, 0x34, 0x56, 0x78]); const FROM_LE_BYTES: i32 = i32::from_le_bytes([0x12, 0x34, 0x56, 0x78]); From 87f0dc63a864c6d9a3c34aa4052762dbcd316c91 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Wed, 26 Feb 2020 11:59:37 +0100 Subject: [PATCH 02/28] use unions instead of transmute and add const safety comments --- src/libcore/num/mod.rs | 44 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 22897680567e2..b64abc319360e 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2251,12 +2251,19 @@ assert_eq!( ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] - #[allow_internal_unstable(const_transmute)] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[allow_internal_unstable(const_fn_union)] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + #[repr(C)] + union Bytes { + val: $SelfT, + bytes: [u8; mem::size_of::<$SelfT>()], + } // SAFETY: integers are plain old datatypes so we can always transmute them to // arrays of bytes - unsafe { mem::transmute(self) } + unsafe { Bytes { val: self }.bytes } } } @@ -2362,11 +2369,18 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] - #[allow_internal_unstable(const_transmute)] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[allow_internal_unstable(const_fn_union)] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + #[repr(C)] + union Bytes { + val: $SelfT, + bytes: [u8; mem::size_of::<$SelfT>()], + } // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { mem::transmute(bytes) } + unsafe { Bytes { bytes }.val } } } } @@ -4190,12 +4204,19 @@ assert_eq!( ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] - #[allow_internal_unstable(const_transmute)] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[allow_internal_unstable(const_fn_union)] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + #[repr(C)] + union Bytes { + val: $SelfT, + bytes: [u8; mem::size_of::<$SelfT>()], + } // SAFETY: integers are plain old datatypes so we can always transmute them to // arrays of bytes - unsafe { mem::transmute(self) } + unsafe { Bytes { val: self }.bytes } } } @@ -4301,11 +4322,18 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), ```"), #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.43.0")] - #[allow_internal_unstable(const_transmute)] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[allow_internal_unstable(const_fn_union)] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + #[repr(C)] + union Bytes { + val: $SelfT, + bytes: [u8; mem::size_of::<$SelfT>()], + } // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { mem::transmute(bytes) } + unsafe { Bytes { bytes }.val } } } } From 503026b622d1095770178fbe606a4ba783216992 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 3 Nov 2019 17:21:37 +0100 Subject: [PATCH 03/28] mem::zeroed/uninit: panic on types that do not permit zero-initialization --- src/libcore/intrinsics.rs | 10 ++ src/libcore/mem/mod.rs | 6 + src/librustc/ty/layout.rs | 30 ----- src/librustc_codegen_ssa/mir/block.rs | 31 ++++- src/librustc_index/vec.rs | 2 +- src/librustc_target/abi/mod.rs | 115 ++++++++++++++++++ src/librustc_target/lib.rs | 3 + src/librustc_typeck/check/intrinsic.rs | 5 +- .../intrinsics/panic-uninitialized-zeroed.rs | 113 +++++++++++++++++ .../never_type/panic-uninitialized-zeroed.rs | 102 ---------------- 10 files changed, 280 insertions(+), 137 deletions(-) create mode 100644 src/test/ui/intrinsics/panic-uninitialized-zeroed.rs delete mode 100644 src/test/ui/never_type/panic-uninitialized-zeroed.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 94928211a978a..c02dabcacde85 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -721,6 +721,16 @@ extern "rust-intrinsic" { /// This will statically either panic, or do nothing. pub fn panic_if_uninhabited(); + /// A guard for unsafe functions that cannot ever be executed if `T` does not permit + /// zero-initialization: This will statically either panic, or do nothing. + #[cfg(not(bootstrap))] + pub fn panic_if_zero_invalid(); + + /// A guard for unsafe functions that cannot ever be executed if `T` has invalid + /// bit patterns: This will statically either panic, or do nothing. + #[cfg(not(bootstrap))] + pub fn panic_if_any_invalid(); + /// Gets a reference to a static `Location` indicating where it was called. #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")] pub fn caller_location() -> &'static crate::panic::Location<'static>; diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 9eb151cf528a5..124eb37fff17e 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -495,6 +495,9 @@ pub const fn needs_drop() -> bool { #[allow(deprecated)] #[rustc_diagnostic_item = "mem_zeroed"] pub unsafe fn zeroed() -> T { + #[cfg(not(bootstrap))] + intrinsics::panic_if_zero_invalid::(); + #[cfg(bootstrap)] intrinsics::panic_if_uninhabited::(); intrinsics::init() } @@ -528,6 +531,9 @@ pub unsafe fn zeroed() -> T { #[allow(deprecated)] #[rustc_diagnostic_item = "mem_uninitialized"] pub unsafe fn uninitialized() -> T { + #[cfg(not(bootstrap))] + intrinsics::panic_if_any_invalid::(); + #[cfg(bootstrap)] intrinsics::panic_if_uninhabited::(); intrinsics::uninit() } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index e8bf2eb9a12c9..6b5f540e5b830 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1907,36 +1907,6 @@ impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { } } -pub trait MaybeResult { - type Error; - - fn from(x: Result) -> Self; - fn to_result(self) -> Result; -} - -impl MaybeResult for T { - type Error = !; - - fn from(x: Result) -> Self { - let Ok(x) = x; - x - } - fn to_result(self) -> Result { - Ok(self) - } -} - -impl MaybeResult for Result { - type Error = E; - - fn from(x: Result) -> Self { - x - } - fn to_result(self) -> Result { - self - } -} - pub type TyLayout<'tcx> = ::rustc_target::abi::TyLayout<'tcx, Ty<'tcx>>; impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index a1b54607b809e..923e2486ace68 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -521,11 +521,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } // Emit a panic or a no-op for `panic_if_uninhabited`. - if intrinsic == Some("panic_if_uninhabited") { + // These are intrinsics that compile to panics so that we can get a message + // which mentions the offending type, even from a const context. + #[derive(Debug, PartialEq)] + enum PanicIntrinsic { IfUninhabited, IfZeroInvalid, IfAnyInvalid }; + let panic_intrinsic = intrinsic.and_then(|i| match i { + "panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited), + "panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid), + "panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid), + _ => None + }); + if let Some(intrinsic) = panic_intrinsic { + use PanicIntrinsic::*; let ty = instance.unwrap().substs.type_at(0); let layout = bx.layout_of(ty); - if layout.abi.is_uninhabited() { - let msg_str = format!("Attempted to instantiate uninhabited type {}", ty); + let do_panic = match intrinsic { + IfUninhabited => layout.abi.is_uninhabited(), + IfZeroInvalid => // We unwrap as the error type is `!`. + !layout.might_permit_raw_init(&bx, /*zero:*/ true).unwrap(), + IfAnyInvalid => // We unwrap as the error type is `!`. + !layout.might_permit_raw_init(&bx, /*zero:*/ false).unwrap(), + }; + if do_panic { + let msg_str = if layout.abi.is_uninhabited() { + // Use this error even for the other intrinsics as it is more precise. + format!("attempted to instantiate uninhabited type `{}`", ty) + } else if intrinsic == IfZeroInvalid { + format!("attempted to zero-initialize type `{}`, which is invalid", ty) + } else { + format!("attempted to leave type `{}` uninitialized, which is invalid", ty) + }; let msg = bx.const_str(Symbol::intern(&msg_str)); let location = self.get_caller_location(&mut bx, span).immediate(); diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs index 1dfe97238a3df..7020939fa20b2 100644 --- a/src/librustc_index/vec.rs +++ b/src/librustc_index/vec.rs @@ -196,7 +196,7 @@ macro_rules! newtype_index { #[inline] fn index(self) -> usize { - usize::from(self) + self.as_usize() } } diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index edd0ba46f7565..13a0eb66f32a5 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -919,6 +919,7 @@ impl<'a, Ty> Deref for TyLayout<'a, Ty> { } } +/// Trait for context types that can compute layouts of things. pub trait LayoutOf { type Ty; type TyLayout; @@ -929,6 +930,39 @@ pub trait LayoutOf { } } +/// The `TyLayout` above will always be a `MaybeResult>`. +/// We can't add the bound due to the lifetime, but this trait is still useful when +/// writing code that's generic over the `LayoutOf` impl. +pub trait MaybeResult { + type Error; + + fn from(x: Result) -> Self; + fn to_result(self) -> Result; +} + +impl MaybeResult for T { + type Error = !; + + fn from(x: Result) -> Self { + let Ok(x) = x; + x + } + fn to_result(self) -> Result { + Ok(self) + } +} + +impl MaybeResult for Result { + type Error = E; + + fn from(x: Result) -> Self { + x + } + fn to_result(self) -> Result { + self + } +} + #[derive(Copy, Clone, PartialEq, Eq)] pub enum PointerKind { /// Most general case, we know no restrictions to tell LLVM. @@ -969,6 +1003,9 @@ impl<'a, Ty> TyLayout<'a, Ty> { { Ty::for_variant(self, cx, variant_index) } + + /// Callers might want to use `C: LayoutOf>` + /// to allow recursion (see `might_permit_zero_init` below for an example). pub fn field(self, cx: &C, i: usize) -> C::TyLayout where Ty: TyLayoutMethods<'a, C>, @@ -976,6 +1013,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { { Ty::field(self, cx, i) } + pub fn pointee_info_at(self, cx: &C, offset: Size) -> Option where Ty: TyLayoutMethods<'a, C>, @@ -999,4 +1037,81 @@ impl<'a, Ty> TyLayout<'a, Ty> { Abi::Aggregate { sized } => sized && self.size.bytes() == 0, } } + + /// Determines if this type permits "raw" initialization by just transmuting some + /// memory into an instance of `T`. + /// `zero` indicates if the memory is zero-initialized, or alternatively + /// left entirely uninitialized. + /// This is conservative: in doubt, it will answer `true`. + pub fn might_permit_raw_init( + self, + cx: &C, + zero: bool, + ) -> Result + where + Self: Copy, + Ty: TyLayoutMethods<'a, C>, + C: LayoutOf> + { + let scalar_allows_raw_init = move |s: &Scalar| -> bool { + let range = &s.valid_range; + if zero { + // The range must contain 0. + range.contains(&0) || + (*range.start() > *range.end()) // wrap-around allows 0 + } else { + // The range must include all values. + *range.start() == range.end().wrapping_add(1) + } + }; + + // Abi is the most informative here. + let res = match &self.abi { + Abi::Uninhabited => false, // definitely UB + Abi::Scalar(s) => scalar_allows_raw_init(s), + Abi::ScalarPair(s1, s2) => + scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), + Abi::Vector { element: s, count } => + *count == 0 || scalar_allows_raw_init(s), + Abi::Aggregate { .. } => { + match self.variants { + Variants::Multiple { .. } => + if zero { + // FIXME: could we identify the variant with discriminant 0, check that? + true + } else { + // FIXME: This needs to have some sort of discriminant, + // which cannot be undef. But for now we are conservative. + true + }, + Variants::Single { .. } => { + // For aggregates, recurse. + match self.fields { + FieldPlacement::Union(..) => true, // An all-0 unit is fine. + FieldPlacement::Array { .. } => + // FIXME: The widely use smallvec 0.6 creates uninit arrays + // with any element type, so let us not (yet) complain about that. + // count == 0 || + // self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? + true, + FieldPlacement::Arbitrary { ref offsets, .. } => { + let mut res = true; + // Check that all fields accept zero-init. + for idx in 0..offsets.len() { + let field = self.field(cx, idx).to_result()?; + if !field.might_permit_raw_init(cx, zero)? { + res = false; + break; + } + } + res + } + } + } + } + } + }; + trace!("might_permit_raw_init({:?}, zero={}) = {}", self.details, zero, res); + Ok(res) + } } diff --git a/src/librustc_target/lib.rs b/src/librustc_target/lib.rs index 71150e74f70d4..3c397eb444d1d 100644 --- a/src/librustc_target/lib.rs +++ b/src/librustc_target/lib.rs @@ -10,6 +10,9 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(bool_to_option)] #![feature(nll)] +#![feature(never_type)] +#![feature(associated_type_bounds)] +#![feature(exhaustive_patterns)] #[macro_use] extern crate log; diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 3572eda5c1399..321932fb193a0 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -147,7 +147,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ), "rustc_peek" => (1, vec![param(0)], param(0)), "caller_location" => (0, vec![], tcx.caller_location_ty()), - "panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()), + "panic_if_uninhabited" | + "panic_if_zero_invalid" | + "panic_if_any_invalid" => + (1, Vec::new(), tcx.mk_unit()), "init" => (1, Vec::new(), param(0)), "uninit" => (1, Vec::new(), param(0)), "forget" => (1, vec![param(0)], tcx.mk_unit()), diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs new file mode 100644 index 0000000000000..937f949a7b00b --- /dev/null +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -0,0 +1,113 @@ +// run-pass +// ignore-wasm32-bare compiled with panic=abort by default + +// This test checks panic emitted from `mem::{uninitialized,zeroed}`. + +#![feature(never_type)] +#![allow(deprecated, invalid_value)] + +use std::{mem, panic}; +use std::ptr::NonNull; + +#[allow(dead_code)] +struct Foo { + x: u8, + y: !, +} + +enum Bar {} + +#[allow(dead_code)] +enum OneVariant { Variant(i32) } + +fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { + let err = panic::catch_unwind(op).err(); + assert_eq!( + err.as_ref().and_then(|a| a.downcast_ref::()).map(|s| &**s), + Some(msg) + ); +} + +fn main() { + unsafe { + // Uninitialized types + test_panic_msg( + || mem::uninitialized::(), + "attempted to instantiate uninhabited type `!`" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to instantiate uninhabited type `!`" + ); + test_panic_msg( + || mem::MaybeUninit::::uninit().assume_init(), + "attempted to instantiate uninhabited type `!`" + ); + + test_panic_msg( + || mem::uninitialized::(), + "attempted to instantiate uninhabited type `Foo`" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to instantiate uninhabited type `Foo`" + ); + test_panic_msg( + || mem::MaybeUninit::::uninit().assume_init(), + "attempted to instantiate uninhabited type `Foo`" + ); + + test_panic_msg( + || mem::uninitialized::(), + "attempted to instantiate uninhabited type `Bar`" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to instantiate uninhabited type `Bar`" + ); + test_panic_msg( + || mem::MaybeUninit::::uninit().assume_init(), + "attempted to instantiate uninhabited type `Bar`" + ); + + // Types that do not like zero-initialziation + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `fn()` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to zero-initialize type `fn()`, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::<*const dyn Send>(), + "attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::zeroed::<*const dyn Send>(), + "attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::<(NonNull, u32, u32)>(), + "attempted to leave type `(std::ptr::NonNull, u32, u32)` uninitialized, \ + which is invalid" + ); + test_panic_msg( + || mem::zeroed::<(NonNull, u32, u32)>(), + "attempted to zero-initialize type `(std::ptr::NonNull, u32, u32)`, \ + which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `bool` uninitialized, which is invalid" + ); + + // Some things that should work. + let _val = mem::zeroed::(); + let _val = mem::zeroed::(); + let _val = mem::zeroed::>(); + } +} diff --git a/src/test/ui/never_type/panic-uninitialized-zeroed.rs b/src/test/ui/never_type/panic-uninitialized-zeroed.rs deleted file mode 100644 index e0c30160b9e94..0000000000000 --- a/src/test/ui/never_type/panic-uninitialized-zeroed.rs +++ /dev/null @@ -1,102 +0,0 @@ -// run-pass -// ignore-wasm32-bare compiled with panic=abort by default -// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results -// in a runtime panic. - -#![feature(never_type)] -#![allow(deprecated, invalid_value)] - -use std::{mem, panic}; - -#[allow(dead_code)] -struct Foo { - x: u8, - y: !, -} - -enum Bar {} - -fn main() { - unsafe { - assert_eq!( - panic::catch_unwind(|| { - mem::uninitialized::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type !" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::zeroed::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type !" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::MaybeUninit::::uninit().assume_init() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type !" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::uninitialized::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Foo" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::zeroed::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Foo" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::MaybeUninit::::uninit().assume_init() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Foo" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::uninitialized::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Bar" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::zeroed::() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Bar" - })), - Some(true) - ); - - assert_eq!( - panic::catch_unwind(|| { - mem::MaybeUninit::::uninit().assume_init() - }).err().and_then(|a| a.downcast_ref::().map(|s| { - s == "Attempted to instantiate uninhabited type Bar" - })), - Some(true) - ); - } -} From 6fd909b2b1dd2789e0f1af79283c6105fa502ca3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 6 Nov 2019 11:07:00 +0100 Subject: [PATCH 04/28] reference tracking issue --- src/librustc_target/abi/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 13a0eb66f32a5..4e4ef46909095 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -1077,10 +1077,11 @@ impl<'a, Ty> TyLayout<'a, Ty> { match self.variants { Variants::Multiple { .. } => if zero { - // FIXME: could we identify the variant with discriminant 0, check that? + // FIXME(#66151): + // could we identify the variant with discriminant 0, check that? true } else { - // FIXME: This needs to have some sort of discriminant, + // FIXME(#66151): This needs to have some sort of discriminant, // which cannot be undef. But for now we are conservative. true }, @@ -1089,7 +1090,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { match self.fields { FieldPlacement::Union(..) => true, // An all-0 unit is fine. FieldPlacement::Array { .. } => - // FIXME: The widely use smallvec 0.6 creates uninit arrays + // FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays // with any element type, so let us not (yet) complain about that. // count == 0 || // self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? From d78c4aa62ec0382d85e88b44cce2c087c5357f53 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Nov 2019 12:19:44 +0100 Subject: [PATCH 05/28] use valid_range_exclusive for correct overflow handling --- src/librustc_target/abi/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 4e4ef46909095..c48e7de6b0486 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -1051,17 +1051,20 @@ impl<'a, Ty> TyLayout<'a, Ty> { where Self: Copy, Ty: TyLayoutMethods<'a, C>, - C: LayoutOf> + C: LayoutOf> + HasDataLayout { let scalar_allows_raw_init = move |s: &Scalar| -> bool { - let range = &s.valid_range; if zero { + let range = &s.valid_range; // The range must contain 0. range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0 } else { - // The range must include all values. - *range.start() == range.end().wrapping_add(1) + // The range must include all values. `valid_range_exclusive` handles + // the wrap-around using target arithmetic; with wrap-around then the full + // range is one where `start == end`. + let range = s.valid_range_exclusive(cx); + range.start == range.end } }; From df6a3a0ed216a8548130f9b431964531a1bc7633 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Nov 2019 12:24:26 +0100 Subject: [PATCH 06/28] test some more things that should not panic --- .../intrinsics/panic-uninitialized-zeroed.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 937f949a7b00b..9c869947bfa10 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -6,8 +6,11 @@ #![feature(never_type)] #![allow(deprecated, invalid_value)] -use std::{mem, panic}; -use std::ptr::NonNull; +use std::{ + mem::{self, MaybeUninit}, + panic, + ptr::NonNull, +}; #[allow(dead_code)] struct Foo { @@ -40,7 +43,7 @@ fn main() { "attempted to instantiate uninhabited type `!`" ); test_panic_msg( - || mem::MaybeUninit::::uninit().assume_init(), + || MaybeUninit::::uninit().assume_init(), "attempted to instantiate uninhabited type `!`" ); @@ -53,7 +56,7 @@ fn main() { "attempted to instantiate uninhabited type `Foo`" ); test_panic_msg( - || mem::MaybeUninit::::uninit().assume_init(), + || MaybeUninit::::uninit().assume_init(), "attempted to instantiate uninhabited type `Foo`" ); @@ -66,7 +69,7 @@ fn main() { "attempted to instantiate uninhabited type `Bar`" ); test_panic_msg( - || mem::MaybeUninit::::uninit().assume_init(), + || MaybeUninit::::uninit().assume_init(), "attempted to instantiate uninhabited type `Bar`" ); @@ -109,5 +112,11 @@ fn main() { let _val = mem::zeroed::(); let _val = mem::zeroed::(); let _val = mem::zeroed::>(); + let _val = mem::zeroed::>>(); + let _val = mem::uninitialized::>(); + + // We don't panic for these just to be conservative. They are UB as of now (2019-11-09). + let _val = mem::uninitialized::(); + let _val = mem::uninitialized::<*const ()>(); } } From b133d6776fde22e944eb7f266ee165ffcf7cdb09 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 13 Nov 2019 09:00:29 +0100 Subject: [PATCH 07/28] make it even more conservative, and note some FIXMEs --- src/librustc_codegen_ssa/mir/block.rs | 2 ++ src/librustc_target/abi/mod.rs | 18 +++++++++++++----- .../intrinsics/panic-uninitialized-zeroed.rs | 2 ++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 923e2486ace68..ae3d8442add67 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -526,6 +526,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[derive(Debug, PartialEq)] enum PanicIntrinsic { IfUninhabited, IfZeroInvalid, IfAnyInvalid }; let panic_intrinsic = intrinsic.and_then(|i| match i { + // FIXME: Move to symbols instead of strings. "panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited), "panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid), "panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid), @@ -555,6 +556,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let location = self.get_caller_location(&mut bx, span).immediate(); // Obtain the panic entry point. + // FIXME: dedup this with `codegen_assert_terminator` above. let def_id = common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem); let instance = ty::Instance::mono(bx.tcx(), def_id); diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index c48e7de6b0486..71f97ca85832c 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -1043,6 +1043,10 @@ impl<'a, Ty> TyLayout<'a, Ty> { /// `zero` indicates if the memory is zero-initialized, or alternatively /// left entirely uninitialized. /// This is conservative: in doubt, it will answer `true`. + /// + /// FIXME: Once we removed all the conservatism, we could alternatively + /// create an all-0/all-undef constant and run the vonst value validator to see if + /// this is a valid value for the given type. pub fn might_permit_raw_init( self, cx: &C, @@ -1095,11 +1099,14 @@ impl<'a, Ty> TyLayout<'a, Ty> { FieldPlacement::Array { .. } => // FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays // with any element type, so let us not (yet) complain about that. - // count == 0 || - // self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? + /* count == 0 || + self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? */ true, - FieldPlacement::Arbitrary { ref offsets, .. } => { - let mut res = true; + FieldPlacement::Arbitrary { .. } => { + // FIXME(#66151) cargo depends on sized-chunks 0.3.0 which + // has some illegal zero-initialization, so let us not (yet) + // complain about aggregates either. + /* let mut res = true; // Check that all fields accept zero-init. for idx in 0..offsets.len() { let field = self.field(cx, idx).to_result()?; @@ -1108,7 +1115,8 @@ impl<'a, Ty> TyLayout<'a, Ty> { break; } } - res + res */ + true } } } diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 9c869947bfa10..a1b2a1af2c434 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -92,6 +92,7 @@ fn main() { "attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid" ); + /* FIXME(#66151) we conservatively do not error here yet. test_panic_msg( || mem::uninitialized::<(NonNull, u32, u32)>(), "attempted to leave type `(std::ptr::NonNull, u32, u32)` uninitialized, \ @@ -102,6 +103,7 @@ fn main() { "attempted to zero-initialize type `(std::ptr::NonNull, u32, u32)`, \ which is invalid" ); + */ test_panic_msg( || mem::uninitialized::(), From 6e66f586a0da51fbd4cafa2e1da914cf07c65503 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 16 Feb 2020 22:45:28 +0100 Subject: [PATCH 08/28] fmt --- src/librustc_codegen_ssa/mir/block.rs | 16 +++++++----- src/librustc_target/abi/mod.rs | 34 ++++++++++++-------------- src/librustc_typeck/check/intrinsic.rs | 7 +++--- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index ae3d8442add67..7c5a17d43b6da 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -524,13 +524,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // These are intrinsics that compile to panics so that we can get a message // which mentions the offending type, even from a const context. #[derive(Debug, PartialEq)] - enum PanicIntrinsic { IfUninhabited, IfZeroInvalid, IfAnyInvalid }; + enum PanicIntrinsic { + IfUninhabited, + IfZeroInvalid, + IfAnyInvalid, + }; let panic_intrinsic = intrinsic.and_then(|i| match i { // FIXME: Move to symbols instead of strings. "panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited), "panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid), "panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid), - _ => None + _ => None, }); if let Some(intrinsic) = panic_intrinsic { use PanicIntrinsic::*; @@ -538,10 +542,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = bx.layout_of(ty); let do_panic = match intrinsic { IfUninhabited => layout.abi.is_uninhabited(), - IfZeroInvalid => // We unwrap as the error type is `!`. - !layout.might_permit_raw_init(&bx, /*zero:*/ true).unwrap(), - IfAnyInvalid => // We unwrap as the error type is `!`. - !layout.might_permit_raw_init(&bx, /*zero:*/ false).unwrap(), + // We unwrap as the error type is `!`. + IfZeroInvalid => !layout.might_permit_raw_init(&bx, /*zero:*/ true).unwrap(), + // We unwrap as the error type is `!`. + IfAnyInvalid => !layout.might_permit_raw_init(&bx, /*zero:*/ false).unwrap(), }; if do_panic { let msg_str = if layout.abi.is_uninhabited() { diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index 71f97ca85832c..c043d87f69f9d 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -1047,22 +1047,17 @@ impl<'a, Ty> TyLayout<'a, Ty> { /// FIXME: Once we removed all the conservatism, we could alternatively /// create an all-0/all-undef constant and run the vonst value validator to see if /// this is a valid value for the given type. - pub fn might_permit_raw_init( - self, - cx: &C, - zero: bool, - ) -> Result + pub fn might_permit_raw_init(self, cx: &C, zero: bool) -> Result where Self: Copy, Ty: TyLayoutMethods<'a, C>, - C: LayoutOf> + HasDataLayout + C: LayoutOf> + HasDataLayout, { let scalar_allows_raw_init = move |s: &Scalar| -> bool { if zero { let range = &s.valid_range; // The range must contain 0. - range.contains(&0) || - (*range.start() > *range.end()) // wrap-around allows 0 + range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0 } else { // The range must include all values. `valid_range_exclusive` handles // the wrap-around using target arithmetic; with wrap-around then the full @@ -1076,13 +1071,11 @@ impl<'a, Ty> TyLayout<'a, Ty> { let res = match &self.abi { Abi::Uninhabited => false, // definitely UB Abi::Scalar(s) => scalar_allows_raw_init(s), - Abi::ScalarPair(s1, s2) => - scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), - Abi::Vector { element: s, count } => - *count == 0 || scalar_allows_raw_init(s), + Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), + Abi::Vector { element: s, count } => *count == 0 || scalar_allows_raw_init(s), Abi::Aggregate { .. } => { match self.variants { - Variants::Multiple { .. } => + Variants::Multiple { .. } => { if zero { // FIXME(#66151): // could we identify the variant with discriminant 0, check that? @@ -1091,17 +1084,20 @@ impl<'a, Ty> TyLayout<'a, Ty> { // FIXME(#66151): This needs to have some sort of discriminant, // which cannot be undef. But for now we are conservative. true - }, + } + } Variants::Single { .. } => { // For aggregates, recurse. match self.fields { FieldPlacement::Union(..) => true, // An all-0 unit is fine. FieldPlacement::Array { .. } => - // FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays - // with any element type, so let us not (yet) complain about that. - /* count == 0 || - self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? */ - true, + // FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays + // with any element type, so let us not (yet) complain about that. + /* count == 0 || + self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? */ + { + true + } FieldPlacement::Arbitrary { .. } => { // FIXME(#66151) cargo depends on sized-chunks 0.3.0 which // has some illegal zero-initialization, so let us not (yet) diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 321932fb193a0..d2a358c3e09b8 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -147,10 +147,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ), "rustc_peek" => (1, vec![param(0)], param(0)), "caller_location" => (0, vec![], tcx.caller_location_ty()), - "panic_if_uninhabited" | - "panic_if_zero_invalid" | - "panic_if_any_invalid" => - (1, Vec::new(), tcx.mk_unit()), + "panic_if_uninhabited" | "panic_if_zero_invalid" | "panic_if_any_invalid" => { + (1, Vec::new(), tcx.mk_unit()) + } "init" => (1, Vec::new(), param(0)), "uninit" => (1, Vec::new(), param(0)), "forget" => (1, vec![param(0)], tcx.mk_unit()), From 729f4cd9ae9a619677afaa4c389e000c03e5b22b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 17 Feb 2020 09:21:02 +0100 Subject: [PATCH 09/28] we cannot short-circuit just becuase the Abi seems harmless also add tests for ScalarPair enums --- src/librustc_target/abi/mod.rs | 63 ++++--------------- .../intrinsics/panic-uninitialized-zeroed.rs | 48 +++++++++++++- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index c043d87f69f9d..aa9474233a4ea 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -1045,7 +1045,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { /// This is conservative: in doubt, it will answer `true`. /// /// FIXME: Once we removed all the conservatism, we could alternatively - /// create an all-0/all-undef constant and run the vonst value validator to see if + /// create an all-0/all-undef constant and run the const value validator to see if /// this is a valid value for the given type. pub fn might_permit_raw_init(self, cx: &C, zero: bool) -> Result where @@ -1067,59 +1067,22 @@ impl<'a, Ty> TyLayout<'a, Ty> { } }; - // Abi is the most informative here. - let res = match &self.abi { + // Check the ABI. + let valid = match &self.abi { Abi::Uninhabited => false, // definitely UB Abi::Scalar(s) => scalar_allows_raw_init(s), Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), Abi::Vector { element: s, count } => *count == 0 || scalar_allows_raw_init(s), - Abi::Aggregate { .. } => { - match self.variants { - Variants::Multiple { .. } => { - if zero { - // FIXME(#66151): - // could we identify the variant with discriminant 0, check that? - true - } else { - // FIXME(#66151): This needs to have some sort of discriminant, - // which cannot be undef. But for now we are conservative. - true - } - } - Variants::Single { .. } => { - // For aggregates, recurse. - match self.fields { - FieldPlacement::Union(..) => true, // An all-0 unit is fine. - FieldPlacement::Array { .. } => - // FIXME(#66151): The widely use smallvec 0.6 creates uninit arrays - // with any element type, so let us not (yet) complain about that. - /* count == 0 || - self.field(cx, 0).to_result()?.might_permit_raw_init(cx, zero)? */ - { - true - } - FieldPlacement::Arbitrary { .. } => { - // FIXME(#66151) cargo depends on sized-chunks 0.3.0 which - // has some illegal zero-initialization, so let us not (yet) - // complain about aggregates either. - /* let mut res = true; - // Check that all fields accept zero-init. - for idx in 0..offsets.len() { - let field = self.field(cx, idx).to_result()?; - if !field.might_permit_raw_init(cx, zero)? { - res = false; - break; - } - } - res */ - true - } - } - } - } - } + Abi::Aggregate { .. } => true, // Cannot be excluded *right now*. }; - trace!("might_permit_raw_init({:?}, zero={}) = {}", self.details, zero, res); - Ok(res) + if !valid { + // This is definitely not okay. + trace!("might_permit_raw_init({:?}, zero={}): not valid", self.details, zero); + return Ok(false); + } + + // If we have not found an error yet, we need to recursively descend. + // FIXME(#66151): For now, we are conservative and do not do this. + Ok(true) } } diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index a1b2a1af2c434..be0df0ea2541c 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -7,9 +7,10 @@ #![allow(deprecated, invalid_value)] use std::{ - mem::{self, MaybeUninit}, + mem::{self, MaybeUninit, ManuallyDrop}, panic, ptr::NonNull, + num, }; #[allow(dead_code)] @@ -23,6 +24,18 @@ enum Bar {} #[allow(dead_code)] enum OneVariant { Variant(i32) } +// An enum with ScalarPair layout +#[allow(dead_code)] +enum LR { + Left(i64), + Right(i64), +} +#[allow(dead_code, non_camel_case_types)] +enum LR_NonZero { + Left(num::NonZeroI64), + Right(num::NonZeroI64), +} + fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { let err = panic::catch_unwind(op).err(); assert_eq!( @@ -33,7 +46,7 @@ fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { fn main() { unsafe { - // Uninitialized types + // Uninhabited types test_panic_msg( || mem::uninitialized::(), "attempted to instantiate uninhabited type `!`" @@ -93,6 +106,26 @@ fn main() { ); /* FIXME(#66151) we conservatively do not error here yet. + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `LR_NonZero` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::zeroed::(), + "attempted to zero-initialize type `LR_NonZero`, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::>(), + "attempted to leave type `std::mem::ManuallyDrop` uninitialized, \ + which is invalid" + ); + test_panic_msg( + || mem::zeroed::>(), + "attempted to zero-initialize type `std::mem::ManuallyDrop`, \ + which is invalid" + ); + test_panic_msg( || mem::uninitialized::<(NonNull, u32, u32)>(), "attempted to leave type `(std::ptr::NonNull, u32, u32)` uninitialized, \ @@ -105,13 +138,24 @@ fn main() { ); */ + // Types that can be zero, but not uninit. test_panic_msg( || mem::uninitialized::(), "attempted to leave type `bool` uninitialized, which is invalid" ); + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `LR` uninitialized, which is invalid" + ); + test_panic_msg( + || mem::uninitialized::>(), + "attempted to leave type `std::mem::ManuallyDrop` uninitialized, which is invalid" + ); // Some things that should work. let _val = mem::zeroed::(); + let _val = mem::zeroed::(); + let _val = mem::zeroed::>(); let _val = mem::zeroed::(); let _val = mem::zeroed::>(); let _val = mem::zeroed::>>(); From bfe593e03a0f73ee52ac666be17e963957ef628f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 29 Feb 2020 09:29:51 +0100 Subject: [PATCH 10/28] clarify a comment in the test --- src/test/ui/intrinsics/panic-uninitialized-zeroed.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index be0df0ea2541c..02f8ecaa4eec5 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -161,7 +161,9 @@ fn main() { let _val = mem::zeroed::>>(); let _val = mem::uninitialized::>(); - // We don't panic for these just to be conservative. They are UB as of now (2019-11-09). + // These are UB because they have not been officially blessed, but we await the resolution + // of before doing + // anything about that. let _val = mem::uninitialized::(); let _val = mem::uninitialized::<*const ()>(); } From 7c84e451d533f12d3a1a7b35ca66bdd02ecf17fc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 29 Feb 2020 09:40:40 +0100 Subject: [PATCH 11/28] move panic intrinsic codegen to helper function --- src/librustc_codegen_ssa/mir/block.rs | 158 +++++++++++++++----------- 1 file changed, 92 insertions(+), 66 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 7c5a17d43b6da..c8d352cd2dd98 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -434,6 +434,89 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup); } + /// Returns `true` if this is indeed a panic intrinsic and codegen is done. + fn codegen_panic_intrinsic( + &mut self, + helper: &TerminatorCodegenHelper<'tcx>, + bx: &mut Bx, + intrinsic: Option<&str>, + instance: Option>, + span: Span, + destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, + cleanup: Option, + ) -> bool { + // Emit a panic or a no-op for `panic_if_uninhabited`. + // These are intrinsics that compile to panics so that we can get a message + // which mentions the offending type, even from a const context. + #[derive(Debug, PartialEq)] + enum PanicIntrinsic { + IfUninhabited, + IfZeroInvalid, + IfAnyInvalid, + }; + let panic_intrinsic = intrinsic.and_then(|i| match i { + // FIXME: Move to symbols instead of strings. + "panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited), + "panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid), + "panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid), + _ => None, + }); + if let Some(intrinsic) = panic_intrinsic { + use PanicIntrinsic::*; + let ty = instance.unwrap().substs.type_at(0); + let layout = bx.layout_of(ty); + let do_panic = match intrinsic { + IfUninhabited => layout.abi.is_uninhabited(), + // We unwrap as the error type is `!`. + IfZeroInvalid => !layout.might_permit_raw_init(bx, /*zero:*/ true).unwrap(), + // We unwrap as the error type is `!`. + IfAnyInvalid => !layout.might_permit_raw_init(bx, /*zero:*/ false).unwrap(), + }; + if do_panic { + let msg_str = if layout.abi.is_uninhabited() { + // Use this error even for the other intrinsics as it is more precise. + format!("attempted to instantiate uninhabited type `{}`", ty) + } else if intrinsic == IfZeroInvalid { + format!("attempted to zero-initialize type `{}`, which is invalid", ty) + } else { + format!("attempted to leave type `{}` uninitialized, which is invalid", ty) + }; + let msg = bx.const_str(Symbol::intern(&msg_str)); + let location = self.get_caller_location(bx, span).immediate(); + + // Obtain the panic entry point. + // FIXME: dedup this with `codegen_assert_terminator` above. + let def_id = + common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = FnAbi::of_instance(bx, instance, &[]); + let llfn = bx.get_fn_addr(instance); + + if let Some((_, target)) = destination.as_ref() { + helper.maybe_sideeffect(self.mir, bx, &[*target]); + } + // Codegen the actual panic invoke/call. + helper.do_call( + self, + bx, + fn_abi, + llfn, + &[msg.0, msg.1, location], + destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), + cleanup, + ); + } else { + // a NOP + let target = destination.as_ref().unwrap().1; + helper.maybe_sideeffect(self.mir, bx, &[target]); + helper.funclet_br(self, bx, target) + } + true + } else { + false + } + } + fn codegen_call_terminator( &mut self, helper: TerminatorCodegenHelper<'tcx>, @@ -520,72 +603,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("`miri_start_panic` should never end up in compiled code"); } - // Emit a panic or a no-op for `panic_if_uninhabited`. - // These are intrinsics that compile to panics so that we can get a message - // which mentions the offending type, even from a const context. - #[derive(Debug, PartialEq)] - enum PanicIntrinsic { - IfUninhabited, - IfZeroInvalid, - IfAnyInvalid, - }; - let panic_intrinsic = intrinsic.and_then(|i| match i { - // FIXME: Move to symbols instead of strings. - "panic_if_uninhabited" => Some(PanicIntrinsic::IfUninhabited), - "panic_if_zero_invalid" => Some(PanicIntrinsic::IfZeroInvalid), - "panic_if_any_invalid" => Some(PanicIntrinsic::IfAnyInvalid), - _ => None, - }); - if let Some(intrinsic) = panic_intrinsic { - use PanicIntrinsic::*; - let ty = instance.unwrap().substs.type_at(0); - let layout = bx.layout_of(ty); - let do_panic = match intrinsic { - IfUninhabited => layout.abi.is_uninhabited(), - // We unwrap as the error type is `!`. - IfZeroInvalid => !layout.might_permit_raw_init(&bx, /*zero:*/ true).unwrap(), - // We unwrap as the error type is `!`. - IfAnyInvalid => !layout.might_permit_raw_init(&bx, /*zero:*/ false).unwrap(), - }; - if do_panic { - let msg_str = if layout.abi.is_uninhabited() { - // Use this error even for the other intrinsics as it is more precise. - format!("attempted to instantiate uninhabited type `{}`", ty) - } else if intrinsic == IfZeroInvalid { - format!("attempted to zero-initialize type `{}`, which is invalid", ty) - } else { - format!("attempted to leave type `{}` uninitialized, which is invalid", ty) - }; - let msg = bx.const_str(Symbol::intern(&msg_str)); - let location = self.get_caller_location(&mut bx, span).immediate(); - - // Obtain the panic entry point. - // FIXME: dedup this with `codegen_assert_terminator` above. - let def_id = - common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem); - let instance = ty::Instance::mono(bx.tcx(), def_id); - let fn_abi = FnAbi::of_instance(&bx, instance, &[]); - let llfn = bx.get_fn_addr(instance); - - if let Some((_, target)) = destination.as_ref() { - helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); - } - // Codegen the actual panic invoke/call. - helper.do_call( - self, - &mut bx, - fn_abi, - llfn, - &[msg.0, msg.1, location], - destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), - cleanup, - ); - } else { - // a NOP - let target = destination.as_ref().unwrap().1; - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); - helper.funclet_br(self, &mut bx, target) - } + if self.codegen_panic_intrinsic( + &helper, + &mut bx, + intrinsic, + instance, + span, + destination, + cleanup, + ) { return; } From a09c33e36205991bb633a848d1e9c7604ae43ce8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 29 Feb 2020 09:41:59 +0100 Subject: [PATCH 12/28] move pattern to fn argument --- src/librustc_target/abi/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index aa9474233a4ea..d8788611878a0 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -943,8 +943,7 @@ pub trait MaybeResult { impl MaybeResult for T { type Error = !; - fn from(x: Result) -> Self { - let Ok(x) = x; + fn from(Ok(x): Result) -> Self { x } fn to_result(self) -> Result { From cbf5f7d23c4c0c91580245f36fda3ba1b6a12069 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 29 Feb 2020 12:28:32 +0000 Subject: [PATCH 13/28] Use TypeRelating for instantiating query responses --- .../infer/canonical/query_response.rs | 85 +++++++++++++++++-- .../type-annotation-with-hrtb.rs | 33 +++++++ 2 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs diff --git a/src/librustc_infer/infer/canonical/query_response.rs b/src/librustc_infer/infer/canonical/query_response.rs index f4196e576054a..2873618cfadc1 100644 --- a/src/librustc_infer/infer/canonical/query_response.rs +++ b/src/librustc_infer/infer/canonical/query_response.rs @@ -12,14 +12,15 @@ use crate::infer::canonical::{ Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, OriginalQueryValues, QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, }; +use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::infer::InferCtxtBuilder; -use crate::infer::{InferCtxt, InferOk, InferResult}; +use crate::infer::{InferCtxt, InferCtxtBuilder, InferOk, InferResult, NLLRegionVariableOrigin}; use crate::traits::query::{Fallible, NoSolution}; -use crate::traits::TraitEngine; +use crate::traits::{DomainGoal, TraitEngine}; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use rustc::arena::ArenaAllocatable; use rustc::ty::fold::TypeFoldable; +use rustc::ty::relate::TypeRelation; use rustc::ty::subst::{GenericArg, GenericArgKind}; use rustc::ty::{self, BoundVar, Ty, TyCtxt}; use rustc_data_structures::captures::Captures; @@ -304,13 +305,31 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { - let ok = self.at(cause, param_env).eq(v1, v2)?; - obligations.extend(ok.into_obligations()); + TypeRelating::new( + self, + QueryTypeRelatingDelegate { + infcx: self, + param_env, + cause, + obligations: &mut obligations, + }, + ty::Variance::Invariant, + ) + .relate(&v1, &v2)?; } (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { - let ok = self.at(cause, param_env).eq(v1, v2)?; - obligations.extend(ok.into_obligations()); + TypeRelating::new( + self, + QueryTypeRelatingDelegate { + infcx: self, + param_env, + cause, + obligations: &mut obligations, + }, + ty::Variance::Invariant, + ) + .relate(&v1, &v2)?; } _ => { @@ -656,3 +675,55 @@ pub fn make_query_region_constraints<'tcx>( QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } } + +struct QueryTypeRelatingDelegate<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + obligations: &'a mut Vec>, + param_env: ty::ParamEnv<'tcx>, + cause: &'a ObligationCause<'tcx>, +} + +impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { + fn create_next_universe(&mut self) -> ty::UniverseIndex { + self.infcx.create_next_universe() + } + + fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> { + let origin = NLLRegionVariableOrigin::Existential { from_forall }; + self.infcx.next_nll_region_var(origin) + } + + fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> { + self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder)) + } + + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { + self.infcx.next_nll_region_var_in_universe( + NLLRegionVariableOrigin::Existential { from_forall: false }, + universe, + ) + } + + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { + self.obligations.push(Obligation { + cause: self.cause.clone(), + param_env: self.param_env, + predicate: ty::Predicate::RegionOutlives(ty::Binder::dummy(ty::OutlivesPredicate( + sup, sub, + ))), + recursion_depth: 0, + }); + } + + fn push_domain_goal(&mut self, _: DomainGoal<'tcx>) { + bug!("should never be invoked with eager normalization") + } + + fn normalization() -> NormalizationStrategy { + NormalizationStrategy::Eager + } + + fn forbid_inference_vars() -> bool { + true + } +} diff --git a/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs b/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs new file mode 100644 index 0000000000000..1f7c060386bd0 --- /dev/null +++ b/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs @@ -0,0 +1,33 @@ +// Regression test for issue #69490 + +// check-pass + +pub trait Trait { + const S: &'static str; +} + +impl Trait<()> for T +where + T: for<'a> Trait<&'a ()>, +{ + // Use of `T::S` here caused an ICE + const S: &'static str = T::S; +} + +// Some similar cases that didn't ICE: + +impl<'a, T> Trait<()> for (T,) +where + T: Trait<&'a ()>, +{ + const S: &'static str = T::S; +} + +impl Trait<()> for [T; 1] +where + T: Trait fn(&'a ())>, +{ + const S: &'static str = T::S; +} + +fn main() {} From 85cbabba63e472a349fcee45c31fe19e956a1610 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 1 Mar 2020 14:56:42 -0800 Subject: [PATCH 14/28] Implement nth, last, and count for iter::Copied --- src/libcore/iter/adapters/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index 3b8edc2ad6177..dcc1fb13bbcad 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -173,6 +173,18 @@ impl<'a, I, T: 'a> Iterator for Copied { self.it.fold(init, copy_fold(f)) } + + fn nth(&mut self, n: usize) -> Option { + self.it.nth(n).copied() + } + + fn last(self) -> Option { + self.it.last().copied() + } + + fn count(self) -> usize { + self.it.count() + } } #[stable(feature = "iter_copied", since = "1.36.0")] From 011fa9107f405149d006a4208ffb8503214e82ce Mon Sep 17 00:00:00 2001 From: Dodo Date: Mon, 2 Mar 2020 21:05:14 +0100 Subject: [PATCH 15/28] const forget tests --- src/libcore/tests/lib.rs | 1 + src/libcore/tests/mem.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 991458db5b72b..cc341c939803e 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -40,6 +40,7 @@ #![feature(never_type)] #![feature(unwrap_infallible)] #![feature(leading_trailing_ones)] +#![feature(const_forget)] extern crate test; diff --git a/src/libcore/tests/mem.rs b/src/libcore/tests/mem.rs index 59588d97787b7..4841be5fc7107 100644 --- a/src/libcore/tests/mem.rs +++ b/src/libcore/tests/mem.rs @@ -129,3 +129,21 @@ fn test_discriminant_send_sync() { is_send_sync::>(); is_send_sync::>(); } + +#[test] +fn test_const_forget() { + const fn test_const_forget(x: T) { + forget(x); + } + + // Writing this function signature without const-forget + // triggers compiler errors: + // 1) That we use a non-const fn inside a const fn + // 2) without the forget, it complains about the destructor of Box + const fn const_forget_box(mut x: Box) { + forget(x); + } + + const _: () = test_const_forget(0i32); + const _: () = test_const_forget(Vec::>>::new()); +} \ No newline at end of file From 5f4af546d4fd19dd1238aaf2fd1026237b39916f Mon Sep 17 00:00:00 2001 From: Dodo Date: Mon, 2 Mar 2020 21:40:35 +0100 Subject: [PATCH 16/28] An enter as last character pleases tidy it seems --- src/libcore/tests/mem.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/tests/mem.rs b/src/libcore/tests/mem.rs index 4841be5fc7107..9ed2323e2873e 100644 --- a/src/libcore/tests/mem.rs +++ b/src/libcore/tests/mem.rs @@ -146,4 +146,4 @@ fn test_const_forget() { const _: () = test_const_forget(0i32); const _: () = test_const_forget(Vec::>>::new()); -} \ No newline at end of file +} From a674e1c88c632c237d7e56fbb6972b46c39a1fb0 Mon Sep 17 00:00:00 2001 From: Dodo Date: Mon, 2 Mar 2020 23:43:50 +0100 Subject: [PATCH 17/28] remove unused mut, restructure the test --- src/libcore/tests/mem.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcore/tests/mem.rs b/src/libcore/tests/mem.rs index 9ed2323e2873e..8337ab103419f 100644 --- a/src/libcore/tests/mem.rs +++ b/src/libcore/tests/mem.rs @@ -132,18 +132,18 @@ fn test_discriminant_send_sync() { #[test] fn test_const_forget() { - const fn test_const_forget(x: T) { - forget(x); - } + const _: () = forget(0i32); + const _: () = forget(Vec::>>::new()); // Writing this function signature without const-forget // triggers compiler errors: // 1) That we use a non-const fn inside a const fn // 2) without the forget, it complains about the destructor of Box - const fn const_forget_box(mut x: Box) { + const fn const_forget_box(x: Box) { forget(x); } - const _: () = test_const_forget(0i32); - const _: () = test_const_forget(Vec::>>::new()); + // Call the forget_box at runtime, + // as we can't const-construct a box yet. + const_forget_box(Box::new(0i32)); } From 6d03bbd480ef9bef09321fbfdc00229a03091b11 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sun, 8 Mar 2020 14:24:32 +0100 Subject: [PATCH 18/28] constify `mem::discriminant` --- src/libcore/intrinsics.rs | 1 + src/libcore/lib.rs | 1 + src/libcore/mem/mod.rs | 3 ++- src/librustc_mir/interpret/intrinsics.rs | 5 +++++ src/librustc_span/symbol.rs | 1 + src/test/ui/consts/const_discriminant.rs | 22 ++++++++++++++++++++++ 6 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/consts/const_discriminant.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index a889eff75c044..63c5d782e6566 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1852,6 +1852,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::mem::discriminant`](../../std/mem/fn.discriminant.html) + #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub fn discriminant_value(v: &T) -> u64; /// Rust's "try catch" construct which invokes the function pointer `f` with diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 41fb4a77c7ae8..cac61c2c674ad 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -72,6 +72,7 @@ #![feature(concat_idents)] #![feature(const_ascii_ctype_on_intrinsics)] #![feature(const_alloc_layout)] +#![feature(const_discriminant)] #![feature(const_if_match)] #![feature(const_loop)] #![feature(const_checked_int_methods)] diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 90144d11dc9d1..c23d0adab5c0d 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -864,6 +864,7 @@ impl fmt::Debug for Discriminant { /// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3))); /// ``` #[stable(feature = "discriminant_value", since = "1.21.0")] -pub fn discriminant(v: &T) -> Discriminant { +#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] +pub const fn discriminant(v: &T) -> Discriminant { Discriminant(intrinsics::discriminant_value(v), PhantomData) } diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 891afbf437f2b..1e5ed76c467b4 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -216,6 +216,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; self.write_scalar(val, dest)?; } + sym::discriminant_value => { + let place = self.deref_operand(args[0])?; + let discr_val = self.read_discriminant(place.into())?.0; + self.write_scalar(Scalar::from_uint(discr_val, dest.layout.size), dest)?; + } sym::unchecked_shl | sym::unchecked_shr | sym::unchecked_add diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index c39f9f360c027..0d37e9c2c7b08 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -265,6 +265,7 @@ symbols! { derive, diagnostic, direct, + discriminant_value, doc, doc_alias, doc_cfg, diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs new file mode 100644 index 0000000000000..5a32ff2915849 --- /dev/null +++ b/src/test/ui/consts/const_discriminant.rs @@ -0,0 +1,22 @@ +// run-pass +#![feature(const_discriminant)] + +use std::mem::{discriminant, Discriminant}; + +enum Test { + A(u8), + B, + C { a: u8, b: u8 }, +} + +const TEST_A: Discriminant = discriminant(&Test::A(5)); +const TEST_A_OTHER: Discriminant = discriminant(&Test::A(17)); +const TEST_B: Discriminant = discriminant(&Test::B); + +fn main() { + assert_eq!(TEST_A, TEST_A_OTHER); + assert_eq!(TEST_A, discriminant(&Test::A(17))); + assert_eq!(TEST_B, discriminant(&Test::B)); + assert_ne!(TEST_A, TEST_B); + assert_ne!(TEST_B, discriminant(&Test::C { a: 42, b: 7 })); +} From 22f2385a738827fb682bc72bb9e1d794bb436672 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sun, 8 Mar 2020 15:12:46 +0100 Subject: [PATCH 19/28] prevent potential promotion in const_discriminant --- src/test/ui/consts/const_discriminant.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs index 5a32ff2915849..564434bb6fa36 100644 --- a/src/test/ui/consts/const_discriminant.rs +++ b/src/test/ui/consts/const_discriminant.rs @@ -3,6 +3,8 @@ use std::mem::{discriminant, Discriminant}; +fn identity(x: T) -> T { x } + enum Test { A(u8), B, @@ -15,8 +17,8 @@ const TEST_B: Discriminant = discriminant(&Test::B); fn main() { assert_eq!(TEST_A, TEST_A_OTHER); - assert_eq!(TEST_A, discriminant(&Test::A(17))); - assert_eq!(TEST_B, discriminant(&Test::B)); + assert_eq!(TEST_A, discriminant(identity(&Test::A(17)))); + assert_eq!(TEST_B, discriminant(identity(&Test::B))); assert_ne!(TEST_A, TEST_B); - assert_ne!(TEST_B, discriminant(&Test::C { a: 42, b: 7 })); + assert_ne!(TEST_B, discriminant(identity(&Test::C { a: 42, b: 7 }))); } From 6bbb9b86c4d4be72a92676e2d51dcc8032e1fe3e Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sun, 8 Mar 2020 17:04:02 +0100 Subject: [PATCH 20/28] test discriminant of enum with uninhabited variant --- src/test/ui/consts/const_discriminant.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs index 564434bb6fa36..9aac3c4532f47 100644 --- a/src/test/ui/consts/const_discriminant.rs +++ b/src/test/ui/consts/const_discriminant.rs @@ -15,10 +15,21 @@ const TEST_A: Discriminant = discriminant(&Test::A(5)); const TEST_A_OTHER: Discriminant = discriminant(&Test::A(17)); const TEST_B: Discriminant = discriminant(&Test::B); +enum Void {} + +enum SingleVariant { + V, + Never(Void), +} + +const TEST_V: Discriminant = discriminant(&SingleVariant::V); + fn main() { assert_eq!(TEST_A, TEST_A_OTHER); assert_eq!(TEST_A, discriminant(identity(&Test::A(17)))); assert_eq!(TEST_B, discriminant(identity(&Test::B))); assert_ne!(TEST_A, TEST_B); assert_ne!(TEST_B, discriminant(identity(&Test::C { a: 42, b: 7 }))); + + assert_eq!(TEST_V, discriminant(identity(&SingleVariant::V))); } From 4b724e82d2b1065eea10a96fafb19e1390edf0d7 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sun, 8 Mar 2020 18:56:15 +0100 Subject: [PATCH 21/28] allow dead code in discriminant test --- src/test/ui/consts/const_discriminant.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs index 9aac3c4532f47..c67be3c3713f5 100644 --- a/src/test/ui/consts/const_discriminant.rs +++ b/src/test/ui/consts/const_discriminant.rs @@ -1,5 +1,6 @@ // run-pass #![feature(const_discriminant)] +#![allow(dead_code)] use std::mem::{discriminant, Discriminant}; From 314da73797ede5da3ed658208aa31d6aab9cfbf0 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 9 Mar 2020 10:12:44 +0100 Subject: [PATCH 22/28] discrimant test must not be inlined! --- src/test/ui/consts/const_discriminant.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs index c67be3c3713f5..55e2918c85f93 100644 --- a/src/test/ui/consts/const_discriminant.rs +++ b/src/test/ui/consts/const_discriminant.rs @@ -4,6 +4,7 @@ use std::mem::{discriminant, Discriminant}; +#[inline(never)] fn identity(x: T) -> T { x } enum Test { From 906bb8d0e8bbd026fc933fe3f5b829be5e4ac69e Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 10 Mar 2020 01:51:08 +0800 Subject: [PATCH 23/28] fix #62456 --- src/librustc_typeck/check/expr.rs | 18 +++++++++++++----- .../ui/const-generics/issues/issue-62456.rs | 9 +++++++++ .../const-generics/issues/issue-62456.stderr | 16 ++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/const-generics/issues/issue-62456.rs create mode 100644 src/test/ui/const-generics/issues/issue-62456.stderr diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 7570d9d4b28ac..859a219c95a21 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -18,6 +18,7 @@ use crate::type_error_struct; use crate::util::common::ErrorReported; use rustc::middle::lang_items; +use rustc::mir::interpret::ErrorHandled; use rustc::ty; use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc::ty::Ty; @@ -1039,11 +1040,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if element_ty.references_error() { - tcx.types.err - } else if let Ok(count) = count { - tcx.mk_ty(ty::Array(t, count)) - } else { - tcx.types.err + return tcx.types.err; + } + match count { + Ok(count) => tcx.mk_ty(ty::Array(t, count)), + Err(ErrorHandled::TooGeneric) => { + self.tcx.sess.span_err( + tcx.def_span(count_def_id), + "array lengths can't depend on generic parameters", + ); + tcx.types.err + } + Err(ErrorHandled::Reported) => tcx.types.err, } } diff --git a/src/test/ui/const-generics/issues/issue-62456.rs b/src/test/ui/const-generics/issues/issue-62456.rs new file mode 100644 index 0000000000000..c5e6fe9104bc9 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62456.rs @@ -0,0 +1,9 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn foo() { + let _ = [0u64; N + 1]; + //~^ ERROR array lengths can't depend on generic parameters +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-62456.stderr b/src/test/ui/const-generics/issues/issue-62456.stderr new file mode 100644 index 0000000000000..9cdccf8407c9b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62456.stderr @@ -0,0 +1,16 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/issue-62456.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error: array lengths can't depend on generic parameters + --> $DIR/issue-62456.rs:5:20 + | +LL | let _ = [0u64; N + 1]; + | ^^^^^ + +error: aborting due to previous error + From 0a0c850d732666b1205a6f567c38e05239c65848 Mon Sep 17 00:00:00 2001 From: YI Date: Tue, 10 Mar 2020 10:54:48 +0800 Subject: [PATCH 24/28] fix test failure due to earlier emitted error --- .../ui/issues/issue-69602-type-err-during-codegen-ice.rs | 1 + .../issues/issue-69602-type-err-during-codegen-ice.stderr | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs index 2c5257ce063cb..d060f26fb2a08 100644 --- a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs +++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs @@ -19,4 +19,5 @@ impl TraitB for B { //~ ERROR not all trait items implemented, missing: `MyA` fn main() { let _ = [0; B::VALUE]; + //~^ ERROR array lengths can't depend on generic parameters } diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr index 8ae0f8b804c93..c6b2b4d27a208 100644 --- a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr +++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr @@ -13,7 +13,13 @@ LL | type MyA: TraitA; LL | impl TraitB for B { | ^^^^^^^^^^^^^^^^^ missing `MyA` in implementation -error: aborting due to 2 previous errors +error: array lengths can't depend on generic parameters + --> $DIR/issue-69602-type-err-during-codegen-ice.rs:21:17 + | +LL | let _ = [0; B::VALUE]; + | ^^^^^^^^ + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0046, E0437. For more information about an error, try `rustc --explain E0046`. From 7b3e3ff39aa45103a6f8432466f8078970866142 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Tue, 10 Mar 2020 12:46:22 +0100 Subject: [PATCH 25/28] explain the use of a custom identity function --- src/test/ui/consts/const_discriminant.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs index 55e2918c85f93..1ad5134e71c52 100644 --- a/src/test/ui/consts/const_discriminant.rs +++ b/src/test/ui/consts/const_discriminant.rs @@ -4,6 +4,9 @@ use std::mem::{discriminant, Discriminant}; +// `discriminant(const_expr)` may get const-propagated. +// As we want to check that const-eval is equal to ordinary exection, +// we wrap `const_expr` with a function which is not const to prevent this. #[inline(never)] fn identity(x: T) -> T { x } From 4d16c217b8074784b1faad07f5a284e4a6999b53 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 10 Mar 2020 16:20:47 +0100 Subject: [PATCH 26/28] Matrix::push: recursively expand or-patterns --- src/librustc_mir_build/hair/pattern/_match.rs | 6 ++++- ...ve-been-expanded-earlier-non-exhaustive.rs | 9 +++++++ ...een-expanded-earlier-non-exhaustive.stderr | 25 +++++++++++++++++++ ...69875-should-have-been-expanded-earlier.rs | 9 +++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs create mode 100644 src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr create mode 100644 src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index 71aefa85c99fd..37ad5f5ea4e38 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -480,7 +480,11 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. crate fn push(&mut self, row: PatStack<'p, 'tcx>) { if let Some(rows) = row.expand_or_pat() { - self.0.extend(rows); + for row in rows { + // We recursively expand the or-patterns of the new rows. + // This is necessary as we might have `0 | (1 | 2)` or e.g., `x @ 0 | x @ (1 | 2)`. + self.push(row) + } } else { self.0.push(row); } diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs new file mode 100644 index 0000000000000..59533cefea64c --- /dev/null +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs @@ -0,0 +1,9 @@ +#![feature(or_patterns)] + +fn main() { + let 0 | (1 | 2) = 0; //~ ERROR refutable pattern in local binding + match 0 { + //~^ ERROR non-exhaustive patterns + 0 | (1 | 2) => {} + } +} diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr new file mode 100644 index 0000000000000..58286e87869a4 --- /dev/null +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -0,0 +1,25 @@ +error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:4:9 + | +LL | let 0 | (1 | 2) = 0; + | ^^^^^^^^^^^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html +help: you might want to use `if let` to ignore the variant that isn't matched + | +LL | if let 0 | (1 | 2) = 0 { /* */ } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0004]: non-exhaustive patterns: `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:5:11 + | +LL | match 0 { + | ^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0004, E0005. +For more information about an error, try `rustc --explain E0004`. diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs new file mode 100644 index 0000000000000..1de563dedbf18 --- /dev/null +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs @@ -0,0 +1,9 @@ +// check-pass + +#![feature(or_patterns)] + +fn main() { + let 0 | (1 | _) = 0; + if let 0 | (1 | 2) = 0 {} + if let x @ 0 | x @ (1 | 2) = 0 {} +} From 69aaed872cec119b1034c52151d00fb5308e7407 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Fri, 6 Mar 2020 10:37:23 +0100 Subject: [PATCH 27/28] Make Point `Copy` in arithmetic documentation Small composite types like `Point { x: i32, y: i32}` are plain old data and we should encourage users to derive `Copy` on them. This changes the semantics of the edited examples slightly: instead of consuming the operands during addition, it will copy them. This is desired behaviour. Co-Authored-By: Jake Goulding --- src/libcore/ops/arith.rs | 10 +++++----- src/libcore/ops/mod.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcore/ops/arith.rs b/src/libcore/ops/arith.rs index 59a72799e2567..e9ec81394e32d 100644 --- a/src/libcore/ops/arith.rs +++ b/src/libcore/ops/arith.rs @@ -13,7 +13,7 @@ /// ``` /// use std::ops::Add; /// -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, Copy, Clone, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -42,7 +42,7 @@ /// ``` /// use std::ops::Add; /// -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, Copy, Clone, PartialEq)] /// struct Point { /// x: T, /// y: T, @@ -115,7 +115,7 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// ``` /// use std::ops::Sub; /// -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, Copy, Clone, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -657,7 +657,7 @@ neg_impl_numeric! { isize i8 i16 i32 i64 i128 f32 f64 } /// ``` /// use std::ops::AddAssign; /// -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, Copy, Clone, PartialEq)] /// struct Point { /// x: i32, /// y: i32, @@ -715,7 +715,7 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } /// ``` /// use std::ops::SubAssign; /// -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, Copy, Clone, PartialEq)] /// struct Point { /// x: i32, /// y: i32, diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index 77b92b6ccbdaa..e3e5934b44be1 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -42,7 +42,7 @@ //! ```rust //! use std::ops::{Add, Sub}; //! -//! #[derive(Debug, PartialEq)] +//! #[derive(Debug, Copy, Clone, PartialEq)] //! struct Point { //! x: i32, //! y: i32, From 6b27e8d2a631183babfb25c5aac008b263ba4978 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 10 Mar 2020 21:02:15 +0300 Subject: [PATCH 28/28] parse: Tweak the function parameter edition check Move anon-params tests to ui/anon-params. --- src/librustc_parse/parser/item.rs | 4 +--- .../ui/{ => anon-params}/anon-params-denied-2018.rs | 0 .../{ => anon-params}/anon-params-denied-2018.stderr | 0 .../{ => anon-params}/anon-params-deprecated.fixed | 0 .../ui/{ => anon-params}/anon-params-deprecated.rs | 0 .../{ => anon-params}/anon-params-deprecated.stderr | 0 .../ui/anon-params/anon-params-edition-hygiene.rs | 10 ++++++++++ .../auxiliary/anon-params-edition-hygiene.rs | 12 ++++++++++++ 8 files changed, 23 insertions(+), 3 deletions(-) rename src/test/ui/{ => anon-params}/anon-params-denied-2018.rs (100%) rename src/test/ui/{ => anon-params}/anon-params-denied-2018.stderr (100%) rename src/test/ui/{ => anon-params}/anon-params-deprecated.fixed (100%) rename src/test/ui/{ => anon-params}/anon-params-deprecated.rs (100%) rename src/test/ui/{ => anon-params}/anon-params-deprecated.stderr (100%) create mode 100644 src/test/ui/anon-params/anon-params-edition-hygiene.rs create mode 100644 src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 126686c8defbf..a9c4de04c0a2e 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -1544,9 +1544,7 @@ impl<'a> Parser<'a> { let is_name_required = match self.token.kind { token::DotDotDot => false, - // FIXME: Consider using interpolated token for this edition check, - // it should match the intent of edition hygiene better. - _ => req_name(self.token.uninterpolate().span.edition()), + _ => req_name(self.token.span.edition()), }; let (pat, ty) = if is_name_required || self.is_named_param() { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); diff --git a/src/test/ui/anon-params-denied-2018.rs b/src/test/ui/anon-params/anon-params-denied-2018.rs similarity index 100% rename from src/test/ui/anon-params-denied-2018.rs rename to src/test/ui/anon-params/anon-params-denied-2018.rs diff --git a/src/test/ui/anon-params-denied-2018.stderr b/src/test/ui/anon-params/anon-params-denied-2018.stderr similarity index 100% rename from src/test/ui/anon-params-denied-2018.stderr rename to src/test/ui/anon-params/anon-params-denied-2018.stderr diff --git a/src/test/ui/anon-params-deprecated.fixed b/src/test/ui/anon-params/anon-params-deprecated.fixed similarity index 100% rename from src/test/ui/anon-params-deprecated.fixed rename to src/test/ui/anon-params/anon-params-deprecated.fixed diff --git a/src/test/ui/anon-params-deprecated.rs b/src/test/ui/anon-params/anon-params-deprecated.rs similarity index 100% rename from src/test/ui/anon-params-deprecated.rs rename to src/test/ui/anon-params/anon-params-deprecated.rs diff --git a/src/test/ui/anon-params-deprecated.stderr b/src/test/ui/anon-params/anon-params-deprecated.stderr similarity index 100% rename from src/test/ui/anon-params-deprecated.stderr rename to src/test/ui/anon-params/anon-params-deprecated.stderr diff --git a/src/test/ui/anon-params/anon-params-edition-hygiene.rs b/src/test/ui/anon-params/anon-params-edition-hygiene.rs new file mode 100644 index 0000000000000..14e11c5696f4a --- /dev/null +++ b/src/test/ui/anon-params/anon-params-edition-hygiene.rs @@ -0,0 +1,10 @@ +// check-pass +// edition:2018 +// aux-build:anon-params-edition-hygiene.rs + +#[macro_use] +extern crate anon_params_edition_hygiene; + +generate_trait_2015!(u8); + +fn main() {} diff --git a/src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs b/src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs new file mode 100644 index 0000000000000..aa4221becc24f --- /dev/null +++ b/src/test/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs @@ -0,0 +1,12 @@ +// edition:2015 + +#[macro_export] +macro_rules! generate_trait_2015 { + ($Type: ident) => { + trait Trait { + fn method($Type) {} + } + }; +} + +fn main() {}