From ecacc7534b6bf50205c37c89402565b82d95a257 Mon Sep 17 00:00:00 2001 From: Tim Vermeulen Date: Sat, 19 Sep 2020 10:26:46 +0200 Subject: [PATCH 01/15] Add advance_by and advance_back_by --- library/core/src/iter/adapters/mod.rs | 10 ++++ library/core/src/iter/traits/double_ended.rs | 54 +++++++++++++++--- library/core/src/iter/traits/iterator.rs | 52 ++++++++++++++--- library/core/tests/iter.rs | 60 ++++++++++++++++++++ library/core/tests/lib.rs | 1 + 5 files changed, 161 insertions(+), 16 deletions(-) diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index ab27fe15a8e..7cddc6773ff 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -124,6 +124,11 @@ where self.iter.size_hint() } + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + self.iter.advance_back_by(n) + } + #[inline] fn nth(&mut self, n: usize) -> Option<::Item> { self.iter.nth_back(n) @@ -164,6 +169,11 @@ where self.iter.next() } + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + self.iter.advance_by(n) + } + #[inline] fn nth_back(&mut self, n: usize) -> Option<::Item> { self.iter.nth(n) diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index a025bc8b560..a5cfc022859 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -91,6 +91,46 @@ pub trait DoubleEndedIterator: Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; + /// Advances the iterator from the back by `n` elements. + /// + /// `advance_back_by` is the reverse version of [`advance_by`]. This method will + /// eagerly skip `n` elements starting from the back by calling [`next_back`] up + /// to `n` times until [`None`] is encountered. + /// + /// `advance_back_by(n)` will return [`Ok(())`] if the iterator successfully advances by + /// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number of + /// elements the iterator is advanced by before running out of elements (i.e. the length + /// of the iterator). Note that `k` is always less than `n`. + /// + /// Calling `advance_back_by(0)` does not consume any elements and always returns [`Ok(())`]. + /// + /// [`advance_by`]: Iterator::advance_by + /// [`next_back`]: DoubleEndedIterator::next_back + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_advance_by)] + /// + /// let a = [3, 4, 5, 6]; + /// let mut iter = a.iter(); + /// + /// assert_eq!(iter.advance_back_by(2), Ok(())); + /// assert_eq!(iter.next_back(), Some(&4)); + /// assert_eq!(iter.advance_back_by(0), Ok(())); + /// assert_eq!(iter.advance_back_by(100), Err(1)); // only `&3` was skipped + /// ``` + #[inline] + #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "none")] + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + for i in 0..n { + self.next_back().ok_or(i)?; + } + Ok(()) + } + /// Returns the `n`th element from the end of the iterator. /// /// This is essentially the reversed version of [`nth`]. Although like most indexing @@ -135,14 +175,9 @@ pub trait DoubleEndedIterator: Iterator { /// ``` #[inline] #[stable(feature = "iter_nth_back", since = "1.37.0")] - fn nth_back(&mut self, mut n: usize) -> Option { - for x in self.rev() { - if n == 0 { - return Some(x); - } - n -= 1; - } - None + fn nth_back(&mut self, n: usize) -> Option { + self.advance_back_by(n).ok()?; + self.next_back() } /// This is the reverse version of [`try_fold()`]: it takes elements @@ -323,6 +358,9 @@ impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I { fn next_back(&mut self) -> Option { (**self).next_back() } + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + (**self).advance_back_by(n) + } fn nth_back(&mut self, n: usize) -> Option { (**self).nth_back(n) } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index b8a09f822b6..8ff4c273fcd 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -284,6 +284,44 @@ pub trait Iterator { self.fold(None, some) } + /// Advances the iterator by `n` elements. + /// + /// This method will eagerly skip `n` elements by calling [`next`] up to `n` + /// times until [`None`] is encountered. + /// + /// `advance_by(n)` will return [`Ok(())`] if the iterator successfully advances by + /// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number + /// of elements the iterator is advanced by before running out of elements (i.e. the + /// length of the iterator). Note that `k` is always less than `n`. + /// + /// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`]. + /// + /// [`next`]: Iterator::next + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_advance_by)] + /// + /// let a = [1, 2, 3, 4]; + /// let mut iter = a.iter(); + /// + /// assert_eq!(iter.advance_by(2), Ok(())); + /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.advance_by(0), Ok(())); + /// assert_eq!(iter.advance_by(100), Err(1)); // only `&4` was skipped + /// ``` + #[inline] + #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "none")] + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + for i in 0..n { + self.next().ok_or(i)?; + } + Ok(()) + } + /// Returns the `n`th element of the iterator. /// /// Like most indexing operations, the count starts from zero, so `nth(0)` @@ -325,14 +363,9 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - fn nth(&mut self, mut n: usize) -> Option { - while let Some(x) = self.next() { - if n == 0 { - return Some(x); - } - n -= 1; - } - None + fn nth(&mut self, n: usize) -> Option { + self.advance_by(n).ok()?; + self.next() } /// Creates an iterator starting at the same point, but stepping by @@ -3262,6 +3295,9 @@ impl Iterator for &mut I { fn size_hint(&self) -> (usize, Option) { (**self).size_hint() } + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + (**self).advance_by(n) + } fn nth(&mut self, n: usize) -> Option { (**self).nth(n) } diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index 00e3972c42f..7a8450116fb 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -1473,6 +1473,66 @@ fn test_iterator_rev_nth() { assert_eq!(v.iter().rev().nth(v.len()), None); } +#[test] +fn test_iterator_advance_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter(); + assert_eq!(iter.advance_by(i), Ok(())); + assert_eq!(iter.next().unwrap(), &v[i]); + assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().advance_by(v.len()), Ok(())); + assert_eq!(v.iter().advance_by(100), Err(v.len())); +} + +#[test] +fn test_iterator_advance_back_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter(); + assert_eq!(iter.advance_back_by(i), Ok(())); + assert_eq!(iter.next_back().unwrap(), &v[v.len() - 1 - i]); + assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().advance_back_by(v.len()), Ok(())); + assert_eq!(v.iter().advance_back_by(100), Err(v.len())); +} + +#[test] +fn test_iterator_rev_advance_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter().rev(); + assert_eq!(iter.advance_by(i), Ok(())); + assert_eq!(iter.next().unwrap(), &v[v.len() - 1 - i]); + assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().rev().advance_by(v.len()), Ok(())); + assert_eq!(v.iter().rev().advance_by(100), Err(v.len())); +} + +#[test] +fn test_iterator_rev_advance_back_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter().rev(); + assert_eq!(iter.advance_back_by(i), Ok(())); + assert_eq!(iter.next_back().unwrap(), &v[i]); + assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().rev().advance_back_by(v.len()), Ok(())); + assert_eq!(v.iter().rev().advance_back_by(100), Err(v.len())); +} + #[test] fn test_iterator_last() { let v: &[_] = &[0, 1, 2, 3, 4]; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index a5b1b51e06c..fd3f71f8786 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -38,6 +38,7 @@ #![feature(slice_partition_dedup)] #![feature(int_error_matching)] #![feature(array_value_iter)] +#![feature(iter_advance_by)] #![feature(iter_partition_in_place)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)] From c29a29cba099aef1e05c83970decdcbedaabd69a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 25 Sep 2020 12:07:46 -0700 Subject: [PATCH 02/15] Defer Apple SDKROOT detection to link time. --- compiler/rustc_codegen_ssa/src/back/link.rs | 84 +++++++++++++ .../src/spec/aarch64_apple_ios.rs | 4 +- .../src/spec/aarch64_apple_tvos.rs | 4 +- .../rustc_target/src/spec/apple_sdk_base.rs | 116 +----------------- .../rustc_target/src/spec/armv7_apple_ios.rs | 4 +- .../rustc_target/src/spec/armv7s_apple_ios.rs | 4 +- .../rustc_target/src/spec/i386_apple_ios.rs | 4 +- .../rustc_target/src/spec/x86_64_apple_ios.rs | 4 +- .../src/spec/x86_64_apple_ios_macabi.rs | 4 +- .../src/spec/x86_64_apple_tvos.rs | 4 +- 10 files changed, 104 insertions(+), 128 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index faeb727202c..408a4a0cfed 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1289,6 +1289,7 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) cmd.args(args); } cmd.args(&sess.opts.debugging_opts.pre_link_args); + add_apple_sdk(cmd, sess, flavor); } /// Add a link script embedded in the target, if applicable. @@ -2083,3 +2084,86 @@ fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { config::Lto::No | config::Lto::ThinLocal => false, } } + +fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { + let arch = &sess.target.target.arch; + let os = &sess.target.target.target_os; + let llvm_target = &sess.target.target.llvm_target; + if sess.target.target.target_vendor != "apple" + || !matches!(os.as_str(), "ios" | "tvos") + || flavor != LinkerFlavor::Gcc + { + return; + } + let sdk_name = match (arch.as_str(), os.as_str()) { + ("aarch64", "tvos") => "appletvos", + ("x86_64", "tvos") => "appletvsimulator", + ("arm", "ios") => "iphoneos", + ("aarch64", "ios") => "iphoneos", + ("x86", "ios") => "iphonesimulator", + ("x86_64", "ios") if llvm_target.contains("macabi") => "macosx10.15", + ("x86_64", "ios") => "iphonesimulator", + _ => { + sess.err(&format!("unsupported arch `{}` for os `{}`", arch, os)); + return; + } + }; + let sdk_root = match get_apple_sdk_root(sdk_name) { + Ok(s) => s, + Err(e) => { + sess.err(&e); + return; + } + }; + let arch_name = llvm_target.split('-').next().expect("LLVM target must have a hyphen"); + cmd.args(&["-arch", arch_name, "-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); +} + +fn get_apple_sdk_root(sdk_name: &str) -> Result { + // Following what clang does + // (https://github.com/llvm/llvm-project/blob/ + // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) + // to allow the SDK path to be set. (For clang, xcrun sets + // SDKROOT; for rustc, the user or build system can set it, or we + // can fall back to checking for xcrun on PATH.) + if let Ok(sdkroot) = env::var("SDKROOT") { + let p = Path::new(&sdkroot); + match sdk_name { + // Ignore `SDKROOT` if it's clearly set for the wrong platform. + "appletvos" + if sdkroot.contains("TVSimulator.platform") + || sdkroot.contains("MacOSX.platform") => {} + "appletvsimulator" + if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {} + "iphoneos" + if sdkroot.contains("iPhoneSimulator.platform") + || sdkroot.contains("MacOSX.platform") => {} + "iphonesimulator" + if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => { + } + "macosx10.15" + if sdkroot.contains("iPhoneOS.platform") + || sdkroot.contains("iPhoneSimulator.platform") => {} + // Ignore `SDKROOT` if it's not a valid path. + _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} + _ => return Ok(sdkroot), + } + } + let res = + Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( + |output| { + if output.status.success() { + Ok(String::from_utf8(output.stdout).unwrap()) + } else { + let error = String::from_utf8(output.stderr); + let error = format!("process exit with error: {}", error.unwrap()); + Err(io::Error::new(io::ErrorKind::Other, &error[..])) + } + }, + ); + + match res { + Ok(output) => Ok(output.trim().to_string()), + Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)), + } +} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs index 21dcec8d5e3..168cd01878e 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::Arm64, AppleOS::iOS)?; + let base = opts(Arch::Arm64); Ok(Target { llvm_target: "arm64-apple-ios".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs index 2b0cd6cabf8..5e2cab0df1e 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::Arm64, AppleOS::tvOS)?; + let base = opts(Arch::Arm64); Ok(Target { llvm_target: "arm64-apple-tvos".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index 0d0a0da9d1c..e34277d5af0 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -1,8 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::env; -use std::io; -use std::path::Path; -use std::process::Command; +use crate::spec::TargetOptions; use Arch::*; #[allow(non_camel_case_types)] @@ -16,108 +12,6 @@ pub enum Arch { X86_64_macabi, } -#[allow(non_camel_case_types)] -#[derive(Copy, Clone)] -pub enum AppleOS { - tvOS, - iOS, -} - -impl Arch { - pub fn to_string(self) -> &'static str { - match self { - Armv7 => "armv7", - Armv7s => "armv7s", - Arm64 => "arm64", - I386 => "i386", - X86_64 => "x86_64", - X86_64_macabi => "x86_64", - } - } -} - -pub fn get_sdk_root(sdk_name: &str) -> Result { - // Following what clang does - // (https://github.com/llvm/llvm-project/blob/ - // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) - // to allow the SDK path to be set. (For clang, xcrun sets - // SDKROOT; for rustc, the user or build system can set it, or we - // can fall back to checking for xcrun on PATH.) - if let Ok(sdkroot) = env::var("SDKROOT") { - let p = Path::new(&sdkroot); - match sdk_name { - // Ignore `SDKROOT` if it's clearly set for the wrong platform. - "appletvos" - if sdkroot.contains("TVSimulator.platform") - || sdkroot.contains("MacOSX.platform") => {} - "appletvsimulator" - if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {} - "iphoneos" - if sdkroot.contains("iPhoneSimulator.platform") - || sdkroot.contains("MacOSX.platform") => {} - "iphonesimulator" - if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => { - } - "macosx10.15" - if sdkroot.contains("iPhoneOS.platform") - || sdkroot.contains("iPhoneSimulator.platform") => {} - // Ignore `SDKROOT` if it's not a valid path. - _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} - _ => return Ok(sdkroot), - } - } - let res = - Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( - |output| { - if output.status.success() { - Ok(String::from_utf8(output.stdout).unwrap()) - } else { - let error = String::from_utf8(output.stderr); - let error = format!("process exit with error: {}", error.unwrap()); - Err(io::Error::new(io::ErrorKind::Other, &error[..])) - } - }, - ); - - match res { - Ok(output) => Ok(output.trim().to_string()), - Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)), - } -} - -fn build_pre_link_args(arch: Arch, os: AppleOS) -> Result { - let sdk_name = match (arch, os) { - (Arm64, AppleOS::tvOS) => "appletvos", - (X86_64, AppleOS::tvOS) => "appletvsimulator", - (Armv7, AppleOS::iOS) => "iphoneos", - (Armv7s, AppleOS::iOS) => "iphoneos", - (Arm64, AppleOS::iOS) => "iphoneos", - (I386, AppleOS::iOS) => "iphonesimulator", - (X86_64, AppleOS::iOS) => "iphonesimulator", - (X86_64_macabi, AppleOS::iOS) => "macosx10.15", - _ => unreachable!(), - }; - - let arch_name = arch.to_string(); - - let sdk_root = get_sdk_root(sdk_name)?; - - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - "-arch".to_string(), - arch_name.to_string(), - "-isysroot".to_string(), - sdk_root.clone(), - "-Wl,-syslibroot".to_string(), - sdk_root, - ], - ); - - Ok(args) -} - fn target_cpu(arch: Arch) -> String { match arch { Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher @@ -137,15 +31,13 @@ fn link_env_remove(arch: Arch) -> Vec { } } -pub fn opts(arch: Arch, os: AppleOS) -> Result { - let pre_link_args = build_pre_link_args(arch, os)?; - Ok(TargetOptions { +pub fn opts(arch: Arch) -> TargetOptions { + TargetOptions { cpu: target_cpu(arch), executables: true, - pre_link_args, link_env_remove: link_env_remove(arch), has_elf_tls: false, eliminate_frame_pointer: false, ..super::apple_base::opts() - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_apple_ios.rs b/compiler/rustc_target/src/spec/armv7_apple_ios.rs index 393843526a8..6dafcc2c345 100644 --- a/compiler/rustc_target/src/spec/armv7_apple_ios.rs +++ b/compiler/rustc_target/src/spec/armv7_apple_ios.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::Armv7, AppleOS::iOS)?; + let base = opts(Arch::Armv7); Ok(Target { llvm_target: "armv7-apple-ios".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs index 998a7b2e164..d6c99c4ade6 100644 --- a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs +++ b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::Armv7s, AppleOS::iOS)?; + let base = opts(Arch::Armv7s); Ok(Target { llvm_target: "armv7s-apple-ios".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/i386_apple_ios.rs b/compiler/rustc_target/src/spec/i386_apple_ios.rs index a121d49769d..6cb209ab1c0 100644 --- a/compiler/rustc_target/src/spec/i386_apple_ios.rs +++ b/compiler/rustc_target/src/spec/i386_apple_ios.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::I386, AppleOS::iOS)?; + let base = opts(Arch::I386); Ok(Target { llvm_target: "i386-apple-ios".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs index cfcf856836b..fd3e4e2f57b 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::X86_64, AppleOS::iOS)?; + let base = opts(Arch::X86_64); Ok(Target { llvm_target: "x86_64-apple-ios".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index c42d0911725..4cfbd9eba06 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::X86_64_macabi, AppleOS::iOS)?; + let base = opts(Arch::X86_64_macabi); Ok(Target { llvm_target: "x86_64-apple-ios13.0-macabi".to_string(), target_endian: "little".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs index a56062c0b2b..664a3ed8816 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -1,8 +1,8 @@ -use super::apple_sdk_base::{opts, AppleOS, Arch}; +use super::apple_sdk_base::{opts, Arch}; use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { - let base = opts(Arch::X86_64, AppleOS::iOS)?; + let base = opts(Arch::X86_64); Ok(Target { llvm_target: "x86_64-apple-tvos".to_string(), target_endian: "little".to_string(), From de725fe56f338a2ceedf5dd9df5bcd86a24f8f53 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 25 Sep 2020 12:09:06 -0700 Subject: [PATCH 03/15] Remove note about missing ios/tvos targets in platform docs. I misunderstood how this works, and I have fixed the linux builds to support ios/tvos. --- src/doc/rustc/src/platform-support.md | 17 ++++++++--------- src/tools/tier-check/src/main.rs | 2 -- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 9b2474a95df..6c605f045e5 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -57,7 +57,7 @@ Specifically, these platforms are required to have each of the following: target | std | host | notes -------|-----|------|------- -`aarch64-apple-ios` | ✓[^apple] | | ARM64 iOS +`aarch64-apple-ios` | ✓ | | ARM64 iOS `aarch64-fuchsia` | ✓ | | ARM64 Fuchsia `aarch64-linux-android` | ✓ | | ARM64 Android `aarch64-pc-windows-msvc` | ✓ | | ARM64 Windows MSVC @@ -122,7 +122,7 @@ target | std | host | notes `wasm32-unknown-emscripten` | ✓ | | WebAssembly via Emscripten `wasm32-unknown-unknown` | ✓ | | WebAssembly `wasm32-wasi` | ✓ | | WebAssembly with WASI -`x86_64-apple-ios` | ✓[^apple] | | 64-bit x86 iOS +`x86_64-apple-ios` | ✓ | | 64-bit x86 iOS `x86_64-fortanix-unknown-sgx` | ✓ | | [Fortanix ABI] for 64-bit Intel SGX `x86_64-fuchsia` | ✓ | | 64-bit Fuchsia `x86_64-linux-android` | ✓ | | 64-bit x86 Android @@ -146,7 +146,7 @@ not available. target | std | host | notes -------|-----|------|------- `aarch64-apple-darwin` | ? | | ARM64 macOS -`aarch64-apple-tvos` | *[^apple] | | ARM64 tvOS +`aarch64-apple-tvos` | * | | ARM64 tvOS `aarch64-unknown-cloudabi` | ✓ | | ARM64 CloudABI `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD `aarch64-unknown-hermit` | ? | | @@ -158,16 +158,16 @@ target | std | host | notes `armv4t-unknown-linux-gnueabi` | ? | | `armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD `armv6-unknown-netbsd-eabihf` | ? | | -`armv7-apple-ios` | ✓[^apple] | | ARMv7 iOS, Cortex-a8 +`armv7-apple-ios` | ✓ | | ARMv7 iOS, Cortex-a8 `armv7-unknown-cloudabi-eabihf` | ✓ | | ARMv7 CloudABI, hardfloat `armv7-unknown-freebsd` | ✓ | ✓ | ARMv7 FreeBSD `armv7-unknown-netbsd-eabihf` | ? | | `armv7-wrs-vxworks-eabihf` | ? | | `armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat -`armv7s-apple-ios` | ✓[^apple] | | +`armv7s-apple-ios` | ✓ | | `avr-unknown-gnu-atmega328` | ✗ | | AVR. Requires `-Z build-std=core` `hexagon-unknown-linux-musl` | ? | | -`i386-apple-ios` | ✓[^apple] | | 32-bit x86 iOS +`i386-apple-ios` | ✓ | | 32-bit x86 iOS `i686-apple-darwin` | ✓ | ✓ | 32-bit OSX (10.7+, Lion+) `i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support `i686-unknown-cloudabi` | ✓ | | 32-bit CloudABI @@ -203,8 +203,8 @@ target | std | host | notes `thumbv7a-uwp-windows-msvc` | ✓ | | `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7a Linux with NEON, MUSL `thumbv4t-none-eabi` | * | | ARMv4T T32 -`x86_64-apple-ios-macabi` | ✓[^apple] | | Apple Catalyst -`x86_64-apple-tvos` | *[^apple] | | x86 64-bit tvOS +`x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst +`x86_64-apple-tvos` | * | | x86 64-bit tvOS `x86_64-linux-kernel` | * | | Linux kernel modules `x86_64-pc-solaris` | ? | | `x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support @@ -221,4 +221,3 @@ target | std | host | notes `x86_64-wrs-vxworks` | ? | | [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets -[^apple]: These targets are only available on macOS. diff --git a/src/tools/tier-check/src/main.rs b/src/tools/tier-check/src/main.rs index b8d60a5e2fe..6a492bbff4d 100644 --- a/src/tools/tier-check/src/main.rs +++ b/src/tools/tier-check/src/main.rs @@ -25,8 +25,6 @@ fn main() { let doc_targets: HashSet<_> = doc_targets_md .lines() .filter(|line| line.starts_with('`') && line.contains('|')) - // These platforms only exist on macos. - .filter(|line| !line.contains("[^apple]") || cfg!(target_os = "macos")) .map(|line| line.split('`').skip(1).next().expect("expected target code span")) .collect(); From 7420d7a98044ea1bf727ab369cbc77232dd99694 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 26 Sep 2020 19:12:38 -0700 Subject: [PATCH 04/15] Move add_apple_sdk out of add_pre_link_args. --- compiler/rustc_codegen_ssa/src/back/link.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 408a4a0cfed..db61c726801 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1289,7 +1289,6 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) cmd.args(args); } cmd.args(&sess.opts.debugging_opts.pre_link_args); - add_apple_sdk(cmd, sess, flavor); } /// Add a link script embedded in the target, if applicable. @@ -1525,6 +1524,9 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT add_pre_link_args(cmd, sess, flavor); + // NO-OPT-OUT, OBJECT-FILES-NO + add_apple_sdk(cmd, sess, flavor); + // NO-OPT-OUT add_link_script(cmd, sess, tmpdir, crate_type); From a4783debe021c6d13ce3cf5167efe98c5baeaa26 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 28 Sep 2020 19:44:23 +0200 Subject: [PATCH 05/15] const evaluatable: improve `TooGeneric` handling --- .../src/traits/const_evaluatable.rs | 153 ++++++++++++++++-- .../src/traits/error_reporting/mod.rs | 18 +-- .../src/traits/fulfill.rs | 13 +- .../cross_crate_predicate.rs | 8 +- .../cross_crate_predicate.stderr | 44 +++-- .../infer-too-generic.rs | 24 +++ .../ui/const-generics/issues/issue-76595.rs | 1 - .../const-generics/issues/issue-76595.stderr | 13 +- 8 files changed, 199 insertions(+), 75 deletions(-) create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0cfcaca9060..3828cf4d302 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -23,6 +23,9 @@ use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; +use std::cmp; + +/// Check if a given constant can be evaluated. pub fn is_const_evaluatable<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, def: ty::WithOptConstParam, @@ -32,24 +35,88 @@ pub fn is_const_evaluatable<'cx, 'tcx>( ) -> Result<(), ErrorHandled> { debug!("is_const_evaluatable({:?}, {:?})", def, substs); if infcx.tcx.features().const_evaluatable_checked { - if let Some(ct) = AbstractConst::new(infcx.tcx, def, substs)? { - for pred in param_env.caller_bounds() { - match pred.skip_binders() { - ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { - debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs); - if b_def == def && b_substs == substs { - debug!("is_const_evaluatable: caller_bound ~~> ok"); - return Ok(()); - } else if AbstractConst::new(infcx.tcx, b_def, b_substs)? - .map_or(false, |b_ct| try_unify(infcx.tcx, ct, b_ct)) - { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(()); + let tcx = infcx.tcx; + match AbstractConst::new(tcx, def, substs)? { + // We are looking at a generic abstract constant. + Some(ct) => { + for pred in param_env.caller_bounds() { + match pred.skip_binders() { + ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { + debug!( + "is_const_evaluatable: caller_bound={:?}, {:?}", + b_def, b_substs + ); + if b_def == def && b_substs == substs { + debug!("is_const_evaluatable: caller_bound ~~> ok"); + return Ok(()); + } else if AbstractConst::new(tcx, b_def, b_substs)? + .map_or(false, |b_ct| try_unify(tcx, ct, b_ct)) + { + debug!("is_const_evaluatable: abstract_const ~~> ok"); + return Ok(()); + } + } + _ => {} // don't care + } + } + + // We were unable to unify the abstract constant with + // a constant found in the caller bounds, there are + // now three possible cases here. + // + // - The substs are concrete enough that we can simply + // try and evaluate the given constant. + // - The abstract const still references an inference + // variable, in this case we return `TooGeneric`. + // - The abstract const references a generic parameter, + // this means that we emit an error here. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum FailureKind { + MentionsInfer, + MentionsParam, + Concrete, + } + let mut failure_kind = FailureKind::Concrete; + walk_abstract_const(tcx, ct, |node| match node { + Node::Leaf(leaf) => { + let leaf = leaf.subst(tcx, ct.substs); + if leaf.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if leaf.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); } } - _ => {} // don't care + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => (), + }); + + match failure_kind { + FailureKind::MentionsInfer => { + return Err(ErrorHandled::TooGeneric); + } + FailureKind::MentionsParam => { + // FIXME(const_evaluatable_checked): Better error message. + infcx + .tcx + .sess + .struct_span_err(span, "unconstrained generic constant") + .span_help( + tcx.def_span(def.did), + "consider adding a `where` bound for this expression", + ) + .emit(); + return Err(ErrorHandled::Reported(ErrorReported)); + } + FailureKind::Concrete => { + // Dealt with below by the same code which handles this + // without the feature gate. + } } } + None => { + // If we are dealing with a concrete constant, we can + // reuse the old code path and try to evaluate + // the constant. + } } } @@ -95,7 +162,36 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } debug!(?concrete, "is_const_evaluatable"); - concrete.map(drop) + match concrete { + Err(ErrorHandled::TooGeneric) if !substs.has_infer_types_or_consts() => { + // FIXME(const_evaluatable_checked): We really should move + // emitting this error message to fulfill instead. For + // now this is easier. + // + // This is not a problem without `const_evaluatable_checked` as + // all `ConstEvaluatable` predicates have to be fulfilled for compilation + // to succeed. + // + // @lcnr: We already emit an error for things like + // `fn test() -> [0 - N]` eagerly here, + // so until we fix this I don't really care. + + let mut err = infcx + .tcx + .sess + .struct_span_err(span, "constant expression depends on a generic parameter"); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(const_evaluatable_checked)` this case should not + // be reachable. + err.note("this may fail depending on what value the parameter takes"); + err.emit(); + Err(ErrorHandled::Reported(ErrorReported)) + } + c => c.map(drop), + } } /// A tree representing an anonymous constant. @@ -421,6 +517,33 @@ pub(super) fn try_unify_abstract_consts<'tcx>( // on `ErrorReported`. } +fn walk_abstract_const<'tcx, F>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, mut f: F) +where + F: FnMut(Node<'tcx>), +{ + recurse(tcx, ct, &mut f); + fn recurse<'tcx>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, f: &mut dyn FnMut(Node<'tcx>)) { + let root = ct.root(); + f(root); + match root { + Node::Leaf(_) => (), + Node::Binop(_, l, r) => { + recurse(tcx, ct.subtree(l), f); + recurse(tcx, ct.subtree(r), f); + } + Node::UnaryOp(_, v) => { + recurse(tcx, ct.subtree(v), f); + } + Node::FunctionCall(func, args) => { + recurse(tcx, ct.subtree(func), f); + for &arg in args { + recurse(tcx, ct.subtree(arg), f); + } + } + } + } +} + /// Tries to unify two abstract constants using structural equality. pub(super) fn try_unify<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 1b234a1535c..cb3de57cfed 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -745,25 +745,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let violations = self.tcx.object_safety_violations(did); report_object_safety_error(self.tcx, span, did, violations) } - ConstEvalFailure(ErrorHandled::TooGeneric) => { - // In this instance, we have a const expression containing an unevaluated - // generic parameter. We have no idea whether this expression is valid or - // not (e.g. it might result in an error), but we don't want to just assume - // that it's okay, because that might result in post-monomorphisation time - // errors. The onus is really on the caller to provide values that it can - // prove are well-formed. - let mut err = self - .tcx - .sess - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - err.note("this may fail depending on what value the parameter takes"); - err + bug!("too generic should have been handled in `is_const_evaluatable`"); } - // Already reported in the query. ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => { // FIXME(eddyb) remove this once `ErrorReported` becomes a proof token. diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7e5be8276f7..ccbe93e039d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -496,6 +496,13 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { obligation.cause.span, ) { Ok(()) => ProcessResult::Changed(vec![]), + Err(ErrorHandled::TooGeneric) => { + pending_obligation.stalled_on = substs + .iter() + .filter_map(|ty| TyOrConstInferVar::maybe_from_generic_arg(ty)) + .collect(); + ProcessResult::Unchanged + } Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), } } @@ -537,8 +544,10 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { Err(ErrorHandled::TooGeneric) => { stalled_on.append( &mut substs - .types() - .filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty)) + .iter() + .filter_map(|arg| { + TyOrConstInferVar::maybe_from_generic_arg(arg) + }) .collect(), ); Err(ErrorHandled::TooGeneric) diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs index 52b89cfa045..e3a4d9a96aa 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs @@ -5,10 +5,10 @@ extern crate const_evaluatable_lib; fn user() { let _ = const_evaluatable_lib::test1::(); - //~^ ERROR constant expression depends - //~| ERROR constant expression depends - //~| ERROR constant expression depends - //~| ERROR constant expression depends + //~^ ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant + //~| ERROR unconstrained generic constant } fn main() {} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr index 4af68118be3..8a298b47fff 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr @@ -1,54 +1,50 @@ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10 + | +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | LL | [u8; std::mem::size_of::() - 1]: Sized, - | ---------------------------- required by this bound in `test1` - | - = note: this may fail depending on what value the parameter takes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27 + | +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] - | ---------------------------- required by this bound in `test1` - | - = note: this may fail depending on what value the parameter takes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10 + | +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | LL | [u8; std::mem::size_of::() - 1]: Sized, - | ---------------------------- required by this bound in `test1` - | - = note: this may fail depending on what value the parameter takes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: constant expression depends on a generic parameter +error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - ::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27 + | +help: consider adding a `where` bound for this expression + --> $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] - | ---------------------------- required by this bound in `test1` - | - = note: this may fail depending on what value the parameter takes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs b/src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs new file mode 100644 index 00000000000..cad06ea4004 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/infer-too-generic.rs @@ -0,0 +1,24 @@ +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +use std::{mem, ptr}; + +fn split_first(arr: [T; N]) -> (T, [T; N - 1]) +where + [T; N - 1]: Sized, +{ + let arr = mem::ManuallyDrop::new(arr); + unsafe { + let head = ptr::read(&arr[0]); + let tail = ptr::read(&arr[1..] as *const [T] as *const [T; N - 1]); + (head, tail) + } +} + +fn main() { + let arr = [0, 1, 2, 3, 4]; + let (head, tail) = split_first(arr); + assert_eq!(head, 0); + assert_eq!(tail, [1, 2, 3, 4]); +} diff --git a/src/test/ui/const-generics/issues/issue-76595.rs b/src/test/ui/const-generics/issues/issue-76595.rs index 0a16ca181f5..9fdbbff66e9 100644 --- a/src/test/ui/const-generics/issues/issue-76595.rs +++ b/src/test/ui/const-generics/issues/issue-76595.rs @@ -14,5 +14,4 @@ fn test() where Bool<{core::mem::size_of::() > 4}>: True { fn main() { test::<2>(); //~^ ERROR wrong number of type - //~| ERROR constant expression depends } diff --git a/src/test/ui/const-generics/issues/issue-76595.stderr b/src/test/ui/const-generics/issues/issue-76595.stderr index bbc81693fc0..f258d297718 100644 --- a/src/test/ui/const-generics/issues/issue-76595.stderr +++ b/src/test/ui/const-generics/issues/issue-76595.stderr @@ -4,17 +4,6 @@ error[E0107]: wrong number of type arguments: expected 1, found 0 LL | test::<2>(); | ^^^^^^^^^ expected 1 type argument -error: constant expression depends on a generic parameter - --> $DIR/issue-76595.rs:15:5 - | -LL | fn test() where Bool<{core::mem::size_of::() > 4}>: True { - | ------------------------------- required by this bound in `test` -... -LL | test::<2>(); - | ^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0107`. From db5b70f193dcc08476ec1bef8dc1863d508eea1e Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 28 Sep 2020 20:21:44 +0200 Subject: [PATCH 06/15] move candidate_from_obligation_no_cache --- .../src/traits/select/candidate_assembly.rs | 162 +++++++++++++++++- .../src/traits/select/mod.rs | 155 ----------------- 2 files changed, 161 insertions(+), 156 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a4943231dfd..9cb5c232646 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -7,14 +7,19 @@ //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use rustc_hir as hir; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TypeFoldable}; use rustc_target::spec::abi::Abi; +use crate::traits::coherence::Conflict; use crate::traits::{util, SelectionResult}; +use crate::traits::{Overflow, Unimplemented}; use super::BuiltinImplConditions; +use super::IntercrateAmbiguityCause; +use super::OverflowError; use super::SelectionCandidate::{self, *}; -use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub(super) fn candidate_from_obligation<'o>( @@ -62,6 +67,161 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidate } + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let Some(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, &c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let (trait_desc, self_desc) = with_no_trimmed_paths(|| { + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + (trait_desc, self_desc) + }); + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; + + debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError) => Err(Overflow), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; + + debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + let needs_infer = stack.obligation.predicate.needs_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.references_error() { + debug!("no results for error type, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) + } + pub(super) fn assemble_candidates<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 57f1fedacbe..114dc79c44f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1029,161 +1029,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(candidate)) } - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let Some(conflict) = self.is_knowable(stack) { - debug!("coherence stage: not knowable"); - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - - for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, &c)?.may_apply() { - no_candidates_apply = false; - break; - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let (trait_desc, self_desc) = with_no_trimmed_paths(|| { - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - (trait_desc, self_desc) - }); - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let mut candidates = candidate_set.vec; - - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl Vec { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError) => Err(Overflow), - }) - .flat_map(Result::transpose) - .collect::, _>>()?; - - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - let needs_infer = stack.obligation.predicate.needs_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - candidates.swap_remove(i); - } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - // If there's an error type, 'downgrade' our result from - // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid - // emitting additional spurious errors, since we're guaranteed - // to have emitted at least one. - if stack.obligation.references_error() { - debug!("no results for error type, treating as ambiguous"); - return Ok(None); - } - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) - } - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { debug!("is_knowable(intercrate={:?})", self.intercrate); From 5829560a68d4bf5e29edfed0f9545d24e87ac3f5 Mon Sep 17 00:00:00 2001 From: Jacob Hughes Date: Thu, 24 Sep 2020 18:10:56 -0400 Subject: [PATCH 07/15] Rename AllocErr to AllocError --- library/alloc/src/alloc.rs | 20 ++++++++++---------- library/alloc/src/raw_vec/tests.rs | 6 +++--- library/alloc/src/rc.rs | 4 ++-- library/alloc/src/sync.rs | 4 ++-- library/core/src/alloc/mod.rs | 26 +++++++++++++------------- library/core/src/ptr/non_null.rs | 2 +- library/std/src/alloc.rs | 20 ++++++++++---------- library/std/src/error.rs | 4 ++-- 8 files changed, 43 insertions(+), 43 deletions(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 8b8cdbf2525..75158eefcac 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -145,13 +145,13 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { impl Global { #[inline] - fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocErr> { + fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), // SAFETY: `layout` is non-zero in size, size => unsafe { let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, size)) }, } @@ -165,7 +165,7 @@ impl Global { old_layout: Layout, new_layout: Layout, zeroed: bool, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -183,7 +183,7 @@ impl Global { intrinsics::assume(new_size >= old_layout.size()); let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } @@ -208,12 +208,12 @@ impl Global { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for Global { #[inline] - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, false) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, true) } @@ -232,7 +232,7 @@ unsafe impl AllocRef for Global { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } @@ -243,7 +243,7 @@ unsafe impl AllocRef for Global { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } @@ -254,7 +254,7 @@ unsafe impl AllocRef for Global { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -273,7 +273,7 @@ unsafe impl AllocRef for Global { intrinsics::assume(new_size <= old_layout.size()); let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index e4c8b3709df..cb4fe1b46cd 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -3,7 +3,7 @@ use std::cell::Cell; #[test] fn allocator_param() { - use crate::alloc::AllocErr; + use crate::alloc::AllocError; // Writing a test of integration between third-party // allocators and `RawVec` is a little tricky because the `RawVec` @@ -21,10 +21,10 @@ fn allocator_param() { fuel: Cell, } unsafe impl AllocRef for BoundedAlloc { - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { let size = layout.size(); if size > self.fuel.get() { - return Err(AllocErr); + return Err(AllocError); } match Global.alloc(layout) { ok @ Ok(_) => { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index f998e49dcfc..5dbc42cc97d 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -247,7 +247,7 @@ use core::pin::Pin; use core::ptr::{self, NonNull}; use core::slice::from_raw_parts_mut; -use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocError, AllocRef, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::string::String; use crate::vec::Vec; @@ -996,7 +996,7 @@ impl Rc { /// and must return back a (potentially fat)-pointer for the `RcBox`. unsafe fn allocate_for_layout( value_layout: Layout, - allocate: impl FnOnce(Layout) -> Result, AllocErr>, + allocate: impl FnOnce(Layout) -> Result, AllocError>, mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox, ) -> *mut RcBox { // Calculate layout using the given value layout. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6a240fbb42a..3d7411c79dc 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -21,7 +21,7 @@ use core::slice::from_raw_parts_mut; use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; -use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocError, AllocRef, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::is_dangling; @@ -969,7 +969,7 @@ impl Arc { /// and must return back a (potentially fat)-pointer for the `ArcInner`. unsafe fn allocate_for_layout( value_layout: Layout, - allocate: impl FnOnce(Layout) -> Result, AllocErr>, + allocate: impl FnOnce(Layout) -> Result, AllocError>, mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, ) -> *mut ArcInner { // Calculate layout using the given value layout. diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index f9eb8981bbf..94ba6d4233f 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -13,17 +13,17 @@ pub use self::layout::{Layout, LayoutErr}; use crate::fmt; use crate::ptr::{self, NonNull}; -/// The `AllocErr` error indicates an allocation failure +/// The `AllocError` error indicates an allocation failure /// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct AllocErr; +pub struct AllocError; // (we need this for downstream impl of trait Error) #[unstable(feature = "allocator_api", issue = "32838")] -impl fmt::Display for AllocErr { +impl fmt::Display for AllocError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("memory allocation failed") } @@ -109,7 +109,7 @@ pub unsafe trait AllocRef { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc(&self, layout: Layout) -> Result, AllocErr>; + fn alloc(&self, layout: Layout) -> Result, AllocError>; /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized. /// @@ -126,7 +126,7 @@ pub unsafe trait AllocRef { /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { let ptr = self.alloc(layout)?; // SAFETY: `alloc` returns a valid memory block unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) } @@ -187,7 +187,7 @@ pub unsafe trait AllocRef { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -248,7 +248,7 @@ pub unsafe trait AllocRef { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -312,7 +312,7 @@ pub unsafe trait AllocRef { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -348,12 +348,12 @@ where A: AllocRef + ?Sized, { #[inline] - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { (**self).alloc(layout) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { (**self).alloc_zeroed(layout) } @@ -369,7 +369,7 @@ where ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: the safety contract must be upheld by the caller unsafe { (**self).grow(ptr, old_layout, new_layout) } } @@ -380,7 +380,7 @@ where ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: the safety contract must be upheld by the caller unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } } @@ -391,7 +391,7 @@ where ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: the safety contract must be upheld by the caller unsafe { (**self).shrink(ptr, old_layout, new_layout) } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 87a59c873b1..5dc7171a7dc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -448,7 +448,7 @@ impl NonNull<[T]> { /// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized. /// # #[allow(unused_variables)] /// let slice: &mut [MaybeUninit] = unsafe { memory.as_uninit_slice_mut() }; - /// # Ok::<_, std::alloc::AllocErr>(()) + /// # Ok::<_, std::alloc::AllocError>(()) /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index ba158511f64..dd760062380 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -133,7 +133,7 @@ pub struct System; impl System { #[inline] - fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocErr> { + fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), // SAFETY: `layout` is non-zero in size, @@ -143,7 +143,7 @@ impl System { } else { GlobalAlloc::alloc(self, layout) }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, size)) }, } @@ -157,7 +157,7 @@ impl System { old_layout: Layout, new_layout: Layout, zeroed: bool, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -175,7 +175,7 @@ impl System { intrinsics::assume(new_size >= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } @@ -202,12 +202,12 @@ impl System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for System { #[inline] - fn alloc(&self, layout: Layout) -> Result, AllocErr> { + fn alloc(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, false) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> { self.alloc_impl(layout, true) } @@ -226,7 +226,7 @@ unsafe impl AllocRef for System { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } @@ -237,7 +237,7 @@ unsafe impl AllocRef for System { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } @@ -248,7 +248,7 @@ unsafe impl AllocRef for System { ptr: NonNull, old_layout: Layout, new_layout: Layout, - ) -> Result, AllocErr> { + ) -> Result, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -267,7 +267,7 @@ unsafe impl AllocRef for System { intrinsics::assume(new_size <= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ee25311d3b7..5771ca758af 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -19,7 +19,7 @@ mod tests; use core::array; use core::convert::Infallible; -use crate::alloc::{AllocErr, LayoutErr}; +use crate::alloc::{AllocError, LayoutErr}; use crate::any::TypeId; use crate::backtrace::Backtrace; use crate::borrow::Cow; @@ -387,7 +387,7 @@ impl Error for ! {} reason = "the precise API and guarantees it provides may be tweaked.", issue = "32838" )] -impl Error for AllocErr {} +impl Error for AllocError {} #[stable(feature = "alloc_layout", since = "1.28.0")] impl Error for LayoutErr {} From 46d8c4bdb7318a50d9df12a7a0b0bcc8efc1846e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 10:45:51 -0400 Subject: [PATCH 08/15] Fix recursive nonterminal expansion during pretty-print/reparse check Makes progress towards #43081 In PR #73084, we started recursively expanded nonterminals during the pretty-print/reparse check, allowing them to be properly compared against the reparsed tokenstream. Unfortunately, the recursive logic in that PR only handles the case where a nonterminal appears inside a `TokenTree::Delimited`. If a nonterminal appears directly in the expanded tokens of another nonterminal, the inner nonterminal will not be expanded. This PR fixes the recursive expansion of nonterminals, ensuring that they are expanded wherever they occur. --- compiler/rustc_parse/src/lib.rs | 57 +++++++++++------- .../proc-macro/nested-nonterminal-tokens.rs | 26 ++++++++ .../nested-nonterminal-tokens.stdout | 60 +++++++++++++++++++ 3 files changed, 120 insertions(+), 23 deletions(-) create mode 100644 src/test/ui/proc-macro/nested-nonterminal-tokens.rs create mode 100644 src/test/ui/proc-macro/nested-nonterminal-tokens.stdout diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index d59dd4016a9..b68d36c9a8e 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -8,7 +8,7 @@ use rustc_ast as ast; use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; @@ -435,31 +435,42 @@ pub fn tokenstream_probably_equal_for_proc_macro( token_trees.into_iter() } - let expand_nt = |tree: TokenTree| { - if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { - // When checking tokenstreams for 'probable equality', we are comparing - // a captured (from parsing) `TokenStream` to a reparsed tokenstream. - // The reparsed Tokenstream will never have `None`-delimited groups, - // since they are only ever inserted as a result of macro expansion. - // Therefore, inserting a `None`-delimtied group here (when we - // convert a nested `Nonterminal` to a tokenstream) would cause - // a mismatch with the reparsed tokenstream. - // - // Note that we currently do not handle the case where the - // reparsed stream has a `Parenthesis`-delimited group - // inserted. This will cause a spurious mismatch: - // issue #75734 tracks resolving this. - nt_to_tokenstream(nt, sess, *span).into_trees() - } else { - TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees() - } - }; + fn expand_token(tree: TokenTree, sess: &ParseSess) -> impl Iterator { + // When checking tokenstreams for 'probable equality', we are comparing + // a captured (from parsing) `TokenStream` to a reparsed tokenstream. + // The reparsed Tokenstream will never have `None`-delimited groups, + // since they are only ever inserted as a result of macro expansion. + // Therefore, inserting a `None`-delimtied group here (when we + // convert a nested `Nonterminal` to a tokenstream) would cause + // a mismatch with the reparsed tokenstream. + // + // Note that we currently do not handle the case where the + // reparsed stream has a `Parenthesis`-delimited group + // inserted. This will cause a spurious mismatch: + // issue #75734 tracks resolving this. + + let expanded: SmallVec<[_; 1]> = + if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { + nt_to_tokenstream(nt, sess, *span) + .into_trees() + .flat_map(|t| expand_token(t, sess)) + .collect() + } else { + // Filter before and after breaking tokens, + // since we may want to ignore both glued and unglued tokens. + std::iter::once(tree) + .filter(semantic_tree) + .flat_map(break_tokens) + .filter(semantic_tree) + .collect() + }; + expanded.into_iter() + } // Break tokens after we expand any nonterminals, so that we break tokens // that are produced as a result of nonterminal expansion. - let tokens = tokens.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); - let reparsed_tokens = - reparsed_tokens.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + let tokens = tokens.trees().flat_map(|t| expand_token(t, sess)); + let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess)); tokens.eq_by(reparsed_tokens, |t, rt| tokentree_probably_equal_for_proc_macro(&t, &rt, sess)) } diff --git a/src/test/ui/proc-macro/nested-nonterminal-tokens.rs b/src/test/ui/proc-macro/nested-nonterminal-tokens.rs new file mode 100644 index 00000000000..2f5af10a40a --- /dev/null +++ b/src/test/ui/proc-macro/nested-nonterminal-tokens.rs @@ -0,0 +1,26 @@ +// check-pass +// edition:2018 +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +// Tests that we properly pass tokens to proc-macro when nested +// nonterminals are involved. + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + + +macro_rules! wrap { + (first, $e:expr) => { wrap!(second, $e + 1) }; + (second, $e:expr) => { wrap!(third, $e + 2) }; + (third, $e:expr) => { + print_bang!($e + 3); + }; +} + +fn main() { + let _ = wrap!(first, 0); +} diff --git a/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout b/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout new file mode 100644 index 00000000000..a3d24dd26fe --- /dev/null +++ b/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout @@ -0,0 +1,60 @@ +PRINT-BANG INPUT (DISPLAY): 0 + 1 + 2 + 3 +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/nested-nonterminal-tokens.rs:25:26: 25:27 (#0), + }, + ], + span: $DIR/nested-nonterminal-tokens.rs:17:41: 17:43 (#4), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/nested-nonterminal-tokens.rs:17:44: 17:45 (#4), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/nested-nonterminal-tokens.rs:17:46: 17:47 (#4), + }, + ], + span: $DIR/nested-nonterminal-tokens.rs:18:41: 18:43 (#5), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/nested-nonterminal-tokens.rs:18:44: 18:45 (#5), + }, + Literal { + kind: Integer, + symbol: "2", + suffix: None, + span: $DIR/nested-nonterminal-tokens.rs:18:46: 18:47 (#5), + }, + ], + span: $DIR/nested-nonterminal-tokens.rs:20:21: 20:23 (#6), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/nested-nonterminal-tokens.rs:20:24: 20:25 (#6), + }, + Literal { + kind: Integer, + symbol: "3", + suffix: None, + span: $DIR/nested-nonterminal-tokens.rs:20:26: 20:27 (#6), + }, +] From c6107c53d72250d8c1d41f161ce54648bef4d9d2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 28 Sep 2020 23:51:57 -0400 Subject: [PATCH 09/15] Don't fire `const_item_mutation` lint on writes through a pointer Fixes #77321 --- .../transform/check_const_item_mutation.rs | 14 ++++-- src/test/ui/lint/lint-const-item-mutation.rs | 13 +++++- .../ui/lint/lint-const-item-mutation.stderr | 46 +++++++++---------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs index 70c1aed0957..0281c478a6c 100644 --- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -60,11 +60,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { // so emitting a lint would be redundant. if !lhs.projection.is_empty() { if let Some(def_id) = self.is_const_item(lhs.local) { - self.lint_const_item_usage(def_id, loc, |lint| { - let mut lint = lint.build("attempting to modify a `const` item"); - lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); - lint - }) + // Don't lint on writes through a pointer + // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`) + if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) { + self.lint_const_item_usage(def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); + lint + }) + } } } // We are looking for MIR of the form: diff --git a/src/test/ui/lint/lint-const-item-mutation.rs b/src/test/ui/lint/lint-const-item-mutation.rs index 92d29a7dae4..43371560e02 100644 --- a/src/test/ui/lint/lint-const-item-mutation.rs +++ b/src/test/ui/lint/lint-const-item-mutation.rs @@ -3,13 +3,15 @@ struct MyStruct { field: bool, inner_array: [char; 1], + raw_ptr: *mut u8 } impl MyStruct { fn use_mut(&mut self) {} } const ARRAY: [u8; 1] = [25]; -const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; +const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; +const RAW_PTR: *mut u8 = 1 as *mut u8; fn main() { ARRAY[0] = 5; //~ WARN attempting to modify @@ -18,4 +20,13 @@ fn main() { MY_STRUCT.use_mut(); //~ WARN taking &mut MY_STRUCT; //~ WARN taking (&mut MY_STRUCT).use_mut(); //~ WARN taking + + // Test that we don't warn when writing through + // a raw pointer + // This is U.B., but this test is check-pass, + // so this never actually executes + unsafe { + *RAW_PTR = 0; + *MY_STRUCT.raw_ptr = 0; + } } diff --git a/src/test/ui/lint/lint-const-item-mutation.stderr b/src/test/ui/lint/lint-const-item-mutation.stderr index 2d8f2c49744..c5a221128ff 100644 --- a/src/test/ui/lint/lint-const-item-mutation.stderr +++ b/src/test/ui/lint/lint-const-item-mutation.stderr @@ -1,5 +1,5 @@ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:15:5 + --> $DIR/lint-const-item-mutation.rs:17:5 | LL | ARRAY[0] = 5; | ^^^^^^^^^^^^ @@ -7,39 +7,39 @@ LL | ARRAY[0] = 5; = note: `#[warn(const_item_mutation)]` on by default = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:11:1 + --> $DIR/lint-const-item-mutation.rs:12:1 | LL | const ARRAY: [u8; 1] = [25]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:16:5 + --> $DIR/lint-const-item-mutation.rs:18:5 | LL | MY_STRUCT.field = false; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:17:5 + --> $DIR/lint-const-item-mutation.rs:19:5 | LL | MY_STRUCT.inner_array[0] = 'b'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:18:5 + --> $DIR/lint-const-item-mutation.rs:20:5 | LL | MY_STRUCT.use_mut(); | ^^^^^^^^^^^^^^^^^^^ @@ -47,18 +47,18 @@ LL | MY_STRUCT.use_mut(); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: mutable reference created due to call to this method - --> $DIR/lint-const-item-mutation.rs:8:5 + --> $DIR/lint-const-item-mutation.rs:9:5 | LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:19:5 + --> $DIR/lint-const-item-mutation.rs:21:5 | LL | &mut MY_STRUCT; | ^^^^^^^^^^^^^^ @@ -66,13 +66,13 @@ LL | &mut MY_STRUCT; = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:20:5 + --> $DIR/lint-const-item-mutation.rs:22:5 | LL | (&mut MY_STRUCT).use_mut(); | ^^^^^^^^^^^^^^^^ @@ -80,10 +80,10 @@ LL | (&mut MY_STRUCT).use_mut(); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 6 warnings emitted From 15c357377864d43f5ee8a5fad61a0388d30b2b86 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 29 Sep 2020 15:55:35 -0700 Subject: [PATCH 10/15] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 05c611ae3c4..75615f8e69f 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 05c611ae3c4255b7a2bcf4fcfa65b20286a07839 +Subproject commit 75615f8e69f748d7ef0df7bc0b064a9b1f5c78b2 From d061fee177c70ae146db2b9720c85dc1f38215af Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Mon, 28 Sep 2020 17:34:27 -0700 Subject: [PATCH 11/15] Stable hashing: add comments and tests concerning platform-independence SipHasher128 implements short_write in an endian-independent way, yet its write_xxx Hasher trait methods undo this endian-independence by byte swapping the integer inputs on big-endian hardware. StableHasher then adds endian-independence back by also byte-swapping on big-endian hardware prior to invoking SipHasher128. This double swap may have the appearance of being a no-op, but is in fact by design. In particular, we really do want SipHasher128 to be platform-dependent, in order to be consistent with the libstd SipHasher. Try to clarify this intent. Also, add and update a couple of unit tests. --- compiler/rustc_data_structures/src/sip128.rs | 25 +++++-- .../rustc_data_structures/src/sip128/tests.rs | 56 +++++++++++--- .../src/stable_hasher.rs | 6 +- .../src/stable_hasher/tests.rs | 73 +++++++++++++++++++ 4 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 compiler/rustc_data_structures/src/stable_hasher/tests.rs diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index beb28dd0720..2c4eff618c6 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -125,15 +125,28 @@ impl SipHasher128 { // A specialized write function for values with size <= 8. // - // The hashing of multi-byte integers depends on endianness. E.g.: + // The input must be zero-extended to 64-bits by the caller. This extension + // isn't hashed, but the implementation requires it for correctness. + // + // This function, given the same integer size and value, has the same effect + // on both little- and big-endian hardware. It operates on values without + // depending on their sequence in memory, so is independent of endianness. + // + // However, we want SipHasher128 to be platform-dependent, in order to be + // consistent with the platform-dependent SipHasher in libstd. In other + // words, we want: + // // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])` // - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])` // - // This function does the right thing for little-endian hardware. On - // big-endian hardware `x` must be byte-swapped first to give the right - // behaviour. After any byte-swapping, the input must be zero-extended to - // 64-bits. The caller is responsible for the byte-swapping and - // zero-extension. + // Therefore, in order to produce endian-dependent results, SipHasher128's + // `write_xxx` Hasher trait methods byte-swap `x` prior to zero-extending. + // + // If clients of SipHasher128 itself want platform-independent results, they + // *also* must byte-swap integer inputs before invoking the `write_xxx` + // methods on big-endian hardware (that is, two byte-swaps must occur--one + // in the client, and one in SipHasher128). Additionally, they must extend + // `usize` and `isize` types to 64 bits on 32-bit systems. #[inline] fn short_write(&mut self, _x: T, x: u64) { let size = mem::size_of::(); diff --git a/compiler/rustc_data_structures/src/sip128/tests.rs b/compiler/rustc_data_structures/src/sip128/tests.rs index 80b7fc74756..2e2274a7b77 100644 --- a/compiler/rustc_data_structures/src/sip128/tests.rs +++ b/compiler/rustc_data_structures/src/sip128/tests.rs @@ -1,7 +1,6 @@ use super::*; use std::hash::{Hash, Hasher}; -use std::{mem, slice}; // Hash just the bytes of the slice, without length prefix struct Bytes<'a>(&'a [u8]); @@ -399,20 +398,55 @@ fn test_hash_no_concat_alias() { } #[test] -fn test_write_short_works() { - let test_usize = 0xd0c0b0a0usize; +fn test_short_write_works() { + let test_u8 = 0xFF_u8; + let test_u16 = 0x1122_u16; + let test_u32 = 0x22334455_u32; + let test_u64 = 0x33445566_778899AA_u64; + let test_u128 = 0x11223344_55667788_99AABBCC_DDEEFF77_u128; + let test_usize = 0xD0C0B0A0_usize; + + let test_i8 = -1_i8; + let test_i16 = -2_i16; + let test_i32 = -3_i32; + let test_i64 = -4_i64; + let test_i128 = -5_i128; + let test_isize = -6_isize; + let mut h1 = SipHasher128::new_with_keys(0, 0); - h1.write_usize(test_usize); h1.write(b"bytes"); h1.write(b"string"); - h1.write_u8(0xFFu8); - h1.write_u8(0x01u8); + h1.write_u8(test_u8); + h1.write_u16(test_u16); + h1.write_u32(test_u32); + h1.write_u64(test_u64); + h1.write_u128(test_u128); + h1.write_usize(test_usize); + h1.write_i8(test_i8); + h1.write_i16(test_i16); + h1.write_i32(test_i32); + h1.write_i64(test_i64); + h1.write_i128(test_i128); + h1.write_isize(test_isize); + let mut h2 = SipHasher128::new_with_keys(0, 0); - h2.write(unsafe { - slice::from_raw_parts(&test_usize as *const _ as *const u8, mem::size_of::()) - }); h2.write(b"bytes"); h2.write(b"string"); - h2.write(&[0xFFu8, 0x01u8]); - assert_eq!(h1.finish128(), h2.finish128()); + h2.write(&test_u8.to_ne_bytes()); + h2.write(&test_u16.to_ne_bytes()); + h2.write(&test_u32.to_ne_bytes()); + h2.write(&test_u64.to_ne_bytes()); + h2.write(&test_u128.to_ne_bytes()); + h2.write(&test_usize.to_ne_bytes()); + h2.write(&test_i8.to_ne_bytes()); + h2.write(&test_i16.to_ne_bytes()); + h2.write(&test_i32.to_ne_bytes()); + h2.write(&test_i64.to_ne_bytes()); + h2.write(&test_i128.to_ne_bytes()); + h2.write(&test_isize.to_ne_bytes()); + + let h1_hash = h1.finish128(); + let h2_hash = h2.finish128(); + + assert_eq!(h1_hash, h2_hash); } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index c1c79b174f4..68875b3fbde 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -5,6 +5,9 @@ use smallvec::SmallVec; use std::hash::{BuildHasher, Hash, Hasher}; use std::mem; +#[cfg(test)] +mod tests; + /// When hashing something that ends up affecting properties like symbol names, /// we want these symbol names to be calculated independently of other factors /// like what architecture you're compiling *from*. @@ -129,7 +132,8 @@ impl Hasher for StableHasher { fn write_isize(&mut self, i: isize) { // Always treat isize as i64 so we get the same results on 32 and 64 bit // platforms. This is important for symbol hashes when cross compiling, - // for example. + // for example. Sign extending here is preferable as it means that the + // same negative number hashes the same on both 32 and 64 bit platforms. self.state.write_i64((i as i64).to_le()); } } diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs new file mode 100644 index 00000000000..cd6ff96a555 --- /dev/null +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -0,0 +1,73 @@ +use super::*; + +// The tests below compare the computed hashes to particular expected values +// in order to test that we produce the same results on different platforms, +// regardless of endianness and `usize` and `isize` size differences (this +// of course assumes we run these tests on platforms that differ in those +// ways). The expected values depend on the hashing algorithm used, so they +// need to be updated whenever StableHasher changes its hashing algorithm. + +#[test] +fn test_hash_integers() { + // Test that integers are handled consistently across platforms. + let test_u8 = 0xAB_u8; + let test_u16 = 0xFFEE_u16; + let test_u32 = 0x445577AA_u32; + let test_u64 = 0x01234567_13243546_u64; + let test_u128 = 0x22114433_66557788_99AACCBB_EEDDFF77_u128; + let test_usize = 0xD0C0B0A0_usize; + + let test_i8 = -100_i8; + let test_i16 = -200_i16; + let test_i32 = -300_i32; + let test_i64 = -400_i64; + let test_i128 = -500_i128; + let test_isize = -600_isize; + + let mut h = StableHasher::new(); + test_u8.hash(&mut h); + test_u16.hash(&mut h); + test_u32.hash(&mut h); + test_u64.hash(&mut h); + test_u128.hash(&mut h); + test_usize.hash(&mut h); + test_i8.hash(&mut h); + test_i16.hash(&mut h); + test_i32.hash(&mut h); + test_i64.hash(&mut h); + test_i128.hash(&mut h); + test_isize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (2736651863462566372, 8121090595289675650); + + assert_eq!(h.finalize(), expected); +} + +#[test] +fn test_hash_usize() { + // Test that usize specifically is handled consistently across platforms. + let test_usize = 0xABCDEF01_usize; + + let mut h = StableHasher::new(); + test_usize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (5798740672699530587, 11186240177685111648); + + assert_eq!(h.finalize(), expected); +} + +#[test] +fn test_hash_isize() { + // Test that isize specifically is handled consistently across platforms. + let test_isize = -7_isize; + + let mut h = StableHasher::new(); + test_isize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (14721296605626097289, 11385941877786388409); + + assert_eq!(h.finalize(), expected); +} From ce6c25da5887dcdb446b56892ace1d2463a743f3 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 30 Sep 2020 10:40:49 +0200 Subject: [PATCH 12/15] References to ZSTs may be at arbitrary aligned addresses --- compiler/rustc_mir/src/const_eval/eval_queries.rs | 10 +++++----- src/test/ui/match/const_non_normal_zst_ref_pattern.rs | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/match/const_non_normal_zst_ref_pattern.rs diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index a0ee7fdc072..57aa216850a 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, subst::Subst, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{Abi, LayoutOf}; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; pub fn note_on_undefined_behavior_error() -> &'static str { "The rules on what exactly is undefined behavior aren't clear, \ @@ -148,10 +148,10 @@ pub(super) fn op_to_const<'tcx>( Scalar::Raw { data, .. } => { assert!(mplace.layout.is_zst()); assert_eq!( - data, - mplace.layout.align.abi.bytes().into(), - "this MPlaceTy must come from `try_as_mplace` being used on a zst, so we know what - value this integer address must have", + u64::try_from(data).unwrap() % mplace.layout.align.abi.bytes(), + 0, + "this MPlaceTy must come from a validated constant, thus we can assume the \ + alignment is correct", ); ConstValue::Scalar(Scalar::zst()) } diff --git a/src/test/ui/match/const_non_normal_zst_ref_pattern.rs b/src/test/ui/match/const_non_normal_zst_ref_pattern.rs new file mode 100644 index 00000000000..a114fafb647 --- /dev/null +++ b/src/test/ui/match/const_non_normal_zst_ref_pattern.rs @@ -0,0 +1,9 @@ +// check-pass + +const FOO: isize = 10; +const ZST: &() = unsafe { std::mem::transmute(FOO) }; +fn main() { + match &() { + ZST => 9, + }; +} From 609786dbd8b07599a2bc6618c777f859d7a01451 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 29 Sep 2020 17:44:32 +0100 Subject: [PATCH 13/15] Validate `rustc_args_required_const` --- .../rustc_mir/src/transform/promote_consts.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 110 ++++++++++++++++-- ...lid-rustc_args_required_const-arguments.rs | 26 +++++ ...rustc_args_required_const-arguments.stderr | 48 ++++++++ 4 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/invalid-rustc_args_required_const-arguments.rs create mode 100644 src/test/ui/invalid-rustc_args_required_const-arguments.stderr diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index cd361e430fa..89f7531b3a7 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -137,7 +137,7 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { LitKind::Int(a, _) => { ret.push(a as usize); } - _ => return None, + _ => bug!("invalid arg index"), } } Some(ret) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index efe947daa28..1ec251326a8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,12 +8,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_ast::{Attribute, NestedMetaItem}; -use rustc_errors::struct_span_err; +use rustc_ast::{Attribute, LitKind, NestedMetaItem}; +use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{self, HirId, Item, ItemKind, TraitItem}; +use rustc_hir::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem}; use rustc_hir::{MethodKind, Target}; use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES}; use rustc_session::parse::feature_err; @@ -43,6 +43,12 @@ pub(crate) fn target_from_impl_item<'tcx>( } } +#[derive(Clone, Copy)] +enum ItemLike<'tcx> { + Item(&'tcx Item<'tcx>), + ForeignItem(&'tcx ForeignItem<'tcx>), +} + struct CheckAttrVisitor<'tcx> { tcx: TyCtxt<'tcx>, } @@ -55,7 +61,7 @@ impl CheckAttrVisitor<'tcx> { attrs: &'hir [Attribute], span: &Span, target: Target, - item: Option<&Item<'_>>, + item: Option>, ) { let mut is_valid = true; for attr in attrs { @@ -75,6 +81,8 @@ impl CheckAttrVisitor<'tcx> { self.check_no_link(&attr, span, target) } else if self.tcx.sess.check_name(attr, sym::export_name) { self.check_export_name(&attr, span, target) + } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) { + self.check_rustc_args_required_const(&attr, span, target, item) } else { // lint-only checks if self.tcx.sess.check_name(attr, sym::cold) { @@ -400,6 +408,71 @@ impl CheckAttrVisitor<'tcx> { } } + /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument. + fn check_rustc_args_required_const( + &self, + attr: &Attribute, + span: &Span, + target: Target, + item: Option>, + ) -> bool { + if let Target::Fn | Target::Method(..) | Target::ForeignFn = target { + let mut invalid_args = vec![]; + for meta in attr.meta_item_list().expect("no meta item list") { + if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { + if let Some(ItemLike::Item(Item { + kind: ItemKind::Fn(FnSig { decl, .. }, ..), + .. + })) + | Some(ItemLike::ForeignItem(ForeignItem { + kind: ForeignItemKind::Fn(decl, ..), + .. + })) = item + { + let arg_count = decl.inputs.len() as u128; + if *val >= arg_count { + let span = meta.span(); + self.tcx + .sess + .struct_span_err(span, "index exceeds number of arguments") + .span_label( + span, + format!( + "there {} only {} argument{}", + if arg_count != 1 { "are" } else { "is" }, + arg_count, + pluralize!(arg_count) + ), + ) + .emit(); + return false; + } + } else { + bug!("should be a function item"); + } + } else { + invalid_args.push(meta.span()); + } + } + if !invalid_args.is_empty() { + self.tcx + .sess + .struct_span_err(invalid_args, "arguments should be non-negative integers") + .emit(); + false + } else { + true + } + } else { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a function") + .span_label(*span, "not a function") + .emit(); + false + } + } + /// Checks if `#[link_section]` is applied to a function or static. fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { @@ -448,7 +521,7 @@ impl CheckAttrVisitor<'tcx> { attrs: &'hir [Attribute], span: &Span, target: Target, - item: Option<&Item<'_>>, + item: Option>, hir_id: HirId, ) { // Extract the names of all repr hints, e.g., [foo, bar, align] for: @@ -564,7 +637,14 @@ impl CheckAttrVisitor<'tcx> { // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) if (int_reprs > 1) || (is_simd && is_c) - || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item))) + || (int_reprs == 1 + && is_c + && item.map_or(false, |item| { + if let ItemLike::Item(item) = item { + return is_c_like_enum(item); + } + return false; + })) { self.tcx.struct_span_lint_hir( CONFLICTING_REPR_HINTS, @@ -649,7 +729,13 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { let target = Target::from_item(item); - self.check_attributes(item.hir_id, item.attrs, &item.span, target, Some(item)); + self.check_attributes( + item.hir_id, + item.attrs, + &item.span, + target, + Some(ItemLike::Item(item)), + ); intravisit::walk_item(self, item) } @@ -659,9 +745,15 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_trait_item(self, trait_item) } - fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem<'tcx>) { + fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) { let target = Target::from_foreign_item(f_item); - self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None); + self.check_attributes( + f_item.hir_id, + &f_item.attrs, + &f_item.span, + target, + Some(ItemLike::ForeignItem(f_item)), + ); intravisit::walk_foreign_item(self, f_item) } diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.rs b/src/test/ui/invalid-rustc_args_required_const-arguments.rs new file mode 100644 index 00000000000..76c01c21301 --- /dev/null +++ b/src/test/ui/invalid-rustc_args_required_const-arguments.rs @@ -0,0 +1,26 @@ +#![feature(rustc_attrs)] + +#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments +fn foo1() {} + +#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments +fn foo2(_: u8) {} + +#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers +fn foo4() {} + +#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers +fn foo5(_: u8, _: u8, _: u8) {} + +#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function +struct S; + +#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes +fn foo6(_: u8) {} + +extern { + #[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments + fn foo7(_: u8); +} + +fn main() {} diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.stderr b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr new file mode 100644 index 00000000000..39d04626168 --- /dev/null +++ b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr @@ -0,0 +1,48 @@ +error: suffixed literals are not allowed in attributes + --> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29 + | +LL | #[rustc_args_required_const(0usize)] + | ^^^^^^ + | + = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29 + | +LL | #[rustc_args_required_const(0)] + | ^ there are only 0 arguments + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29 + | +LL | #[rustc_args_required_const(1)] + | ^ there is only 1 argument + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29 + | +LL | #[rustc_args_required_const(a)] + | ^ + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32 + | +LL | #[rustc_args_required_const(1, a, 2, b)] + | ^ ^ + +error: attribute should be applied to a function + --> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1 + | +LL | #[rustc_args_required_const(0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct S; + | --------- not a function + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33 + | +LL | #[rustc_args_required_const(1)] + | ^ there is only 1 argument + +error: aborting due to 7 previous errors + From 05d0b969d9e1c3da6a2651a2ded45f662842a3f9 Mon Sep 17 00:00:00 2001 From: Camelid Date: Wed, 30 Sep 2020 11:24:50 -0700 Subject: [PATCH 14/15] Add test for error message --- src/test/ui/slice-to-vec-comparison.rs | 6 ++++++ src/test/ui/slice-to-vec-comparison.stderr | 12 ++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/test/ui/slice-to-vec-comparison.rs create mode 100644 src/test/ui/slice-to-vec-comparison.stderr diff --git a/src/test/ui/slice-to-vec-comparison.rs b/src/test/ui/slice-to-vec-comparison.rs new file mode 100644 index 00000000000..7026a49000c --- /dev/null +++ b/src/test/ui/slice-to-vec-comparison.rs @@ -0,0 +1,6 @@ +fn main() { + let a = &[]; + let b: &Vec = &vec![]; + a > b; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/slice-to-vec-comparison.stderr b/src/test/ui/slice-to-vec-comparison.stderr new file mode 100644 index 00000000000..c80f86c5592 --- /dev/null +++ b/src/test/ui/slice-to-vec-comparison.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/slice-to-vec-comparison.rs:4:9 + | +LL | a > b; + | ^ expected array of 0 elements , found struct `Vec` + | + = note: expected reference `&[_; 0]` + found reference `&Vec` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From b2ce3e53446173b56607b5a8ad8d9c4b3565d5da Mon Sep 17 00:00:00 2001 From: Camelid Date: Wed, 30 Sep 2020 11:29:19 -0700 Subject: [PATCH 15/15] Remove trailing space in error message --- compiler/rustc_middle/src/ty/error.rs | 2 +- src/test/ui/slice-to-vec-comparison.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 67c69d69bda..82d698b37ab 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -232,7 +232,7 @@ impl<'tcx> ty::TyS<'tcx> { let n = tcx.lift(&n).unwrap(); match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { _ if t.is_simple_ty() => format!("array `{}`", self).into(), - Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(), + Some(n) => format!("array of {} element{}", n, pluralize!(n)).into(), None => "array".into(), } } diff --git a/src/test/ui/slice-to-vec-comparison.stderr b/src/test/ui/slice-to-vec-comparison.stderr index c80f86c5592..e3b3b040f66 100644 --- a/src/test/ui/slice-to-vec-comparison.stderr +++ b/src/test/ui/slice-to-vec-comparison.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/slice-to-vec-comparison.rs:4:9 | LL | a > b; - | ^ expected array of 0 elements , found struct `Vec` + | ^ expected array of 0 elements, found struct `Vec` | = note: expected reference `&[_; 0]` found reference `&Vec`