diff --git a/Cargo.lock b/Cargo.lock index b5da9de5d6248..96eda77abb279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5406,9 +5406,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "ui_test" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1" +checksum = "c21899b59f53717dfad29e4f46e5b21a200a1b6888ab86532a07cfc8b48dd78c" dependencies = [ "bstr", "cargo-platform", diff --git a/config.example.toml b/config.example.toml index 0c65b25fe1389..367f95b156fed 100644 --- a/config.example.toml +++ b/config.example.toml @@ -400,10 +400,20 @@ changelog-seen = 2 # ============================================================================= [rust] -# Whether or not to optimize the compiler and standard library. +# Whether or not to optimize when compiling the compiler and standard library, +# and what level of optimization to use. # WARNING: Building with optimize = false is NOT SUPPORTED. Due to bootstrapping, # building without optimizations takes much longer than optimizing. Further, some platforms # fail to build without this optimization (c.f. #65352). +# The valid options are: +# true - Enable optimizations. +# false - Disable optimizations. +# 0 - Disable optimizations. +# 1 - Basic optimizations. +# 2 - Some optimizations. +# 3 - All optimizations. +# "s" - Optimize for binary size. +# "z" - Optimize for binary size, but also turn off loop vectorization. #optimize = true # Indicates that the build should be configured for debugging Rust. A @@ -757,7 +767,7 @@ changelog-seen = 2 # This option will override the same option under [build] section. #profiler = build.profiler (bool) -# This option supports enable `rpath` in each target independently, +# This option supports enable `rpath` in each target independently, # and will override the same option under [rust] section. It only works on Unix platforms #rpath = rust.rpath (bool) diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 1f7be85d38ac7..5242e97eb9aed 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -133,51 +133,6 @@ pub trait Default: Sized { fn default() -> Self; } -/// Return the default value of a type according to the `Default` trait. -/// -/// The type to return is inferred from context; this is equivalent to -/// `Default::default()` but shorter to type. -/// -/// For example: -/// ``` -/// #![feature(default_free_fn)] -/// -/// use std::default::default; -/// -/// #[derive(Default)] -/// struct AppConfig { -/// foo: FooConfig, -/// bar: BarConfig, -/// } -/// -/// #[derive(Default)] -/// struct FooConfig { -/// foo: i32, -/// } -/// -/// #[derive(Default)] -/// struct BarConfig { -/// bar: f32, -/// baz: u8, -/// } -/// -/// fn main() { -/// let options = AppConfig { -/// foo: default(), -/// bar: BarConfig { -/// bar: 10.1, -/// ..default() -/// }, -/// }; -/// } -/// ``` -#[unstable(feature = "default_free_fn", issue = "73014")] -#[must_use] -#[inline] -pub fn default() -> T { - Default::default() -} - /// Derive macro generating an impl of the trait `Default`. #[rustc_builtin_macro(Default, attributes(default))] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index ef1f4031ef202..3322940d2452f 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,4 +1,8 @@ use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; +use crate::alloc::Allocator; +use crate::cmp; +use crate::collections::VecDeque; +use crate::io::IoSlice; use crate::mem::MaybeUninit; #[cfg(test)] @@ -86,7 +90,7 @@ where /// Specialization of the read-write loop that reuses the internal /// buffer of a BufReader. If there's no buffer then the writer side -/// should be used intead. +/// should be used instead. trait BufferedReaderSpec { fn buffer_size(&self) -> usize; @@ -104,7 +108,39 @@ where } default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result { - unimplemented!("only called from specializations"); + unreachable!("only called from specializations") + } +} + +impl BufferedReaderSpec for &[u8] { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let len = self.len(); + to.write_all(self)?; + *self = &self[len..]; + Ok(len as u64) + } +} + +impl BufferedReaderSpec for VecDeque { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let len = self.len(); + let (front, back) = self.as_slices(); + let bufs = &mut [IoSlice::new(front), IoSlice::new(back)]; + to.write_all_vectored(bufs)?; + self.clear(); + Ok(len as u64) } } @@ -218,6 +254,47 @@ impl BufferedWriterSpec for BufWriter { } } +impl BufferedWriterSpec for Vec { + fn buffer_size(&self) -> usize { + cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) + } + + fn copy_from(&mut self, reader: &mut R) -> Result { + let mut bytes = 0; + + // avoid allocating before we have determined that there's anything to read + if self.capacity() == 0 { + bytes = stack_buffer_copy(&mut reader.take(DEFAULT_BUF_SIZE as u64), self)?; + if bytes == 0 { + return Ok(0); + } + } + + loop { + self.reserve(DEFAULT_BUF_SIZE); + let mut buf: BorrowedBuf<'_> = self.spare_capacity_mut().into(); + match reader.read_buf(buf.unfilled()) { + Ok(()) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + + let read = buf.filled().len(); + if read == 0 { + break; + } + + // SAFETY: BorrowedBuf guarantees all of its filled bytes are init + // and the number of read bytes can't exceed the spare capacity since + // that's what the buffer is borrowing from. + unsafe { self.set_len(self.len() + read) }; + bytes += read as u64; + } + + Ok(bytes) + } +} + fn stack_buffer_copy( reader: &mut R, writer: &mut W, diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 8c816af15b808..af137eaf856f5 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -1,4 +1,6 @@ use crate::cmp::{max, min}; +use crate::collections::VecDeque; +use crate::io; use crate::io::*; #[test] @@ -19,7 +21,7 @@ struct ShortReader { impl Read for ShortReader { fn read(&mut self, buf: &mut [u8]) -> Result { - let bytes = min(self.cap, self.read_size); + let bytes = min(self.cap, self.read_size).min(buf.len()); self.cap -= bytes; self.observed_buffer = max(self.observed_buffer, buf.len()); Ok(bytes) @@ -78,6 +80,40 @@ fn copy_specializes_bufreader() { ); } +#[test] +fn copy_specializes_to_vec() { + let cap = 123456; + let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; + let mut sink = Vec::new(); + assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap()); + assert!( + source.observed_buffer > DEFAULT_BUF_SIZE, + "expected a large buffer to be provided to the reader" + ); +} + +#[test] +fn copy_specializes_from_vecdeque() { + let mut source = VecDeque::with_capacity(100 * 1024); + for _ in 0..20 * 1024 { + source.push_front(0); + } + for _ in 0..20 * 1024 { + source.push_back(0); + } + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); + assert_eq!(20 * 1024, sink.observed_buffer); +} + +#[test] +fn copy_specializes_from_slice() { + let mut source = [1; 60 * 1024].as_slice(); + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); + assert_eq!(60 * 1024, sink.observed_buffer); +} + #[cfg(unix)] mod io_benches { use crate::fs::File; diff --git a/library/std/src/os/android/raw.rs b/library/std/src/os/android/raw.rs index a255d03208623..0182810ff271e 100644 --- a/library/std/src/os/android/raw.rs +++ b/library/std/src/os/android/raw.rs @@ -21,26 +21,26 @@ pub use self::arch::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, #[cfg(any(target_arch = "arm", target_arch = "x86"))] mod arch { - use crate::os::raw::{c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong}; + use crate::os::raw::{c_long, c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong}; use crate::os::unix::raw::{gid_t, uid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type dev_t = u64; + pub type dev_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type mode_t = u32; + pub type mode_t = c_uint; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; + pub type ino_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; + pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -70,18 +70,20 @@ mod arch { pub st_blksize: u32, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: c_ulonglong, + #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_ulong, + pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_ulong, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_ulong, + pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_ulong, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_ulong, + pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_ulong, + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ino: c_ulonglong, } @@ -89,26 +91,26 @@ mod arch { #[cfg(target_arch = "aarch64")] mod arch { - use crate::os::raw::{c_uchar, c_ulong}; + use crate::os::raw::{c_int, c_long, c_uchar, c_uint, c_ulong}; use crate::os::unix::raw::{gid_t, uid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type mode_t = u32; + pub type mode_t = c_uint; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; + pub type ino_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; + pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -117,9 +119,7 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad0: [c_uchar; 4], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __st_ino: ino_t, + pub st_ino: ino_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mode: mode_t, #[stable(feature = "raw_ext", since = "1.1.0")] @@ -131,27 +131,33 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad3: [c_uchar; 4], + pub __pad1: c_ulong, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_size: off_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, + pub st_blksize: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_int, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, + pub st_blocks: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_ulong, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_ulong, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_ulong, + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, + pub __unused4: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused5: c_uint, } } @@ -163,20 +169,20 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type mode_t = u32; + pub type mode_t = c_uint; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; + pub type ino_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -195,25 +201,30 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_gid: gid_t, #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad0: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, + pub st_size: off_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blksize: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: c_long, + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_ulong, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_ulong, + pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_ulong, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_ulong, + pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_ulong, + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_ulong, - __unused: [c_long; 3], + pub __pad3: [c_long; 3], } } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 5f5f7ea25fb95..28ae46efefe7a 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -875,11 +875,10 @@ impl Default for StringOrBool { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] -#[serde(untagged)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum RustOptimize { - #[serde(deserialize_with = "deserialize_and_validate_opt_level")] String(String), + Int(u8), Bool(bool), } @@ -889,26 +888,74 @@ impl Default for RustOptimize { } } -fn deserialize_and_validate_opt_level<'de, D>(d: D) -> Result -where - D: serde::de::Deserializer<'de>, -{ - let v = String::deserialize(d)?; - if ["0", "1", "2", "3", "s", "z"].iter().find(|x| **x == v).is_some() { - Ok(v) - } else { - Err(format!(r#"unrecognized option for rust optimize: "{}", expected one of "0", "1", "2", "3", "s", "z""#, v)).map_err(serde::de::Error::custom) +impl<'de> Deserialize<'de> for RustOptimize { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(OptimizeVisitor) + } +} + +struct OptimizeVisitor; + +impl<'de> serde::de::Visitor<'de> for OptimizeVisitor { + type Value = RustOptimize; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if ["s", "z"].iter().find(|x| **x == value).is_some() { + Ok(RustOptimize::String(value.to_string())) + } else { + Err(format_optimize_error_msg(value)).map_err(serde::de::Error::custom) + } + } + + fn visit_i64(self, value: i64) -> Result + where + E: serde::de::Error, + { + if matches!(value, 0..=3) { + Ok(RustOptimize::Int(value as u8)) + } else { + Err(format_optimize_error_msg(value)).map_err(serde::de::Error::custom) + } + } + + fn visit_bool(self, value: bool) -> Result + where + E: serde::de::Error, + { + Ok(RustOptimize::Bool(value)) } } +fn format_optimize_error_msg(v: impl std::fmt::Display) -> String { + format!( + r#"unrecognized option for rust optimize: "{}", expected one of 0, 1, 2, 3, "s", "z", true, false"#, + v + ) +} + impl RustOptimize { pub(crate) fn is_release(&self) -> bool { - if let RustOptimize::Bool(true) | RustOptimize::String(_) = &self { true } else { false } + match &self { + RustOptimize::Bool(true) | RustOptimize::String(_) => true, + RustOptimize::Int(i) => *i > 0, + RustOptimize::Bool(false) => false, + } } pub(crate) fn get_opt_level(&self) -> Option { match &self { RustOptimize::String(s) => Some(s.clone()), + RustOptimize::Int(i) => Some(i.to_string()), RustOptimize::Bool(_) => None, } } diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index 732df54cdacbd..c340bb2982a2b 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -184,7 +184,10 @@ fn rust_optimize() { assert_eq!(parse("").rust_optimize.is_release(), true); assert_eq!(parse("rust.optimize = false").rust_optimize.is_release(), false); assert_eq!(parse("rust.optimize = true").rust_optimize.is_release(), true); - assert_eq!(parse("rust.optimize = \"1\"").rust_optimize.get_opt_level(), Some("1".to_string())); + assert_eq!(parse("rust.optimize = 0").rust_optimize.is_release(), false); + assert_eq!(parse("rust.optimize = 1").rust_optimize.is_release(), true); + assert_eq!(parse("rust.optimize = 1").rust_optimize.get_opt_level(), Some("1".to_string())); + assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.is_release(), true); assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.get_opt_level(), Some("s".to_string())); } diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 1dc7b79872438..a1d06ab18443c 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -24,7 +24,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins qemu-system-x86 \ && rm -rf /var/lib/apt/lists/* -RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \ +RUN curl -sL https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz | \ tar -xJ # Install 32-bit OVMF files for the i686-unknown-uefi test. This package @@ -42,7 +42,7 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set build.nodejs=/node-v15.14.0-linux-x64/bin/node \ + --set build.nodejs=/node-v18.12.0-linux-x64/bin/node \ --set rust.lld # Some run-make tests have assertions about code size, and enabling debug @@ -58,6 +58,8 @@ ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_T tests/ui \ tests/mir-opt \ tests/codegen-units \ + tests/codegen \ + tests/assembly \ library/core ENV NVPTX_TARGETS=nvptx64-nvidia-cuda diff --git a/src/doc/unstable-book/src/library-features/default-free-fn.md b/src/doc/unstable-book/src/library-features/default-free-fn.md deleted file mode 100644 index bafc9ac4d0d96..0000000000000 --- a/src/doc/unstable-book/src/library-features/default-free-fn.md +++ /dev/null @@ -1,47 +0,0 @@ -# `default_free_fn` - -The tracking issue for this feature is: [#73014] - -[#73014]: https://github.com/rust-lang/rust/issues/73014 - ------------------------- - -Adds a free `default()` function to the `std::default` module. This function -just forwards to [`Default::default()`], but may remove repetition of the word -"default" from the call site. - -[`Default::default()`]: ../../std/default/trait.Default.html#tymethod.default - -Here is an example: - -```rust -#![feature(default_free_fn)] -use std::default::default; - -#[derive(Default)] -struct AppConfig { - foo: FooConfig, - bar: BarConfig, -} - -#[derive(Default)] -struct FooConfig { - foo: i32, -} - -#[derive(Default)] -struct BarConfig { - bar: f32, - baz: u8, -} - -fn main() { - let options = AppConfig { - foo: default(), - bar: BarConfig { - bar: 10.1, - ..default() - }, - }; -} -``` diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 8068db1cf4f0d..c87b3e4232361 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -15,6 +15,10 @@ on: env: CARGO_UNSTABLE_SPARSE_REGISTRY: 'true' +defaults: + run: + shell: bash + jobs: build: runs-on: ${{ matrix.os }} @@ -59,12 +63,9 @@ jobs: - name: Install rustup-toolchain-install-master if: ${{ steps.cache.outputs.cache-hit != 'true' }} - shell: bash - run: | - cargo install -f rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master - name: Install "master" toolchain - shell: bash run: | if [[ ${{ github.event_name }} == 'schedule' ]]; then echo "Building against latest rustc git version" @@ -79,7 +80,7 @@ jobs: cargo -V - name: Test - run: bash ./ci.sh + run: ./ci.sh style: name: style checks @@ -111,14 +112,10 @@ jobs: - name: Install rustup-toolchain-install-master if: ${{ steps.cache.outputs.cache-hit != 'true' }} - shell: bash - run: | - cargo install -f rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master - name: Install "master" toolchain - shell: bash - run: | - ./miri toolchain + run: ./miri toolchain - name: Show Rust version run: | @@ -138,7 +135,6 @@ jobs: # workflow is successful listening to webhooks only. # # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - # (`fmt` is deliberately not listed, we want bors to ignore it.) end-success: name: bors build finished runs-on: ubuntu-latest @@ -166,12 +162,12 @@ jobs: - name: Install zulip-send run: pip3 install zulip - name: Send Zulip notification - shell: bash env: ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} run: | - ~/.local/bin/zulip-send --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ + --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ --message 'Dear @*T-miri*, It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed. @@ -183,9 +179,12 @@ jobs: Thanks in advance! Sincerely, - The Miri Cronjobs Bot' \ - --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com + The Miri Cronjobs Bot' + # Attempt to auto-sync with rustc + - uses: actions/checkout@v3 + with: + fetch-depth: 256 # get a bit more of the history - name: install josh-proxy run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06 - name: start josh-proxy @@ -196,16 +195,24 @@ jobs: git config --global user.email 'miri@cron.bot' - name: get changes from rustc run: ./miri rustc-pull + - name: Install rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master - name: format changes (if any) run: | ./miri toolchain ./miri fmt --check || (./miri fmt && git commit -am "fmt") - name: Push changes to a branch run: | - git switch -c "rustup$(date -u +%Y-%m)" - git push + BRANCH="rustup$(date -u +%Y-%m-%d)" + git switch -c $BRANCH + git push -u origin $BRANCH - name: Create Pull Request - run: gh pr create -B master --title 'Automatic sync from rustc' --body '' + run: | + PR=$(gh pr create -B master --title 'Automatic sync from rustc' --body '') + ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ + --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience." env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} + ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index edb3ee48a4eaf..4232d7fda78f1 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -842,9 +842,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1" +checksum = "c21899b59f53717dfad29e4f46e5b21a200a1b6888ab86532a07cfc8b48dd78c" dependencies = [ "bstr", "cargo-platform", diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 50864334fc51e..a625e1696e143 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -36,7 +36,7 @@ libloading = "0.7" [dev-dependencies] colored = "2" -ui_test = "0.11.6" +ui_test = "0.11.7" rustc_version = "0.4" # Features chosen to match those required by env_logger, to avoid rebuilds regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] } diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 85c9cdad7dfd2..6178670b4f034 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -80,7 +80,11 @@ fn main() { match first.as_str() { "miri" => phase_cargo_miri(args), "runner" => phase_runner(args, RunnerPhase::Cargo), - arg if arg == env::var("RUSTC").unwrap() => { + arg if arg == env::var("RUSTC").unwrap_or_else(|_| { + show_error!( + "`cargo-miri` called without RUSTC set; please only invoke this binary through `cargo miri`" + ) + }) => { // If the first arg is equal to the RUSTC env variable (which should be set at this // point), then we need to behave as rustc. This is the somewhat counter-intuitive // behavior of having both RUSTC and RUSTC_WRAPPER set diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs index 60f39cb36abaa..4c19ed97fb810 100644 --- a/src/tools/miri/cargo-miri/src/util.rs +++ b/src/tools/miri/cargo-miri/src/util.rs @@ -82,7 +82,7 @@ pub enum MiriCommand { pub fn escape_for_toml(s: &str) -> String { // We want to surround this string in quotes `"`. So we first escape all quotes, // and also all backslashes (that are used to escape quotes). - let s = s.replace('\\', r#"\\"#).replace('"', r#"\""#); + let s = s.replace('\\', r"\\").replace('"', r#"\""#); format!("\"{s}\"") } @@ -130,7 +130,7 @@ pub fn exec(mut cmd: Command) -> ! { { use std::os::unix::process::CommandExt; let error = cmd.exec(); - Err(error).expect("failed to run command") + panic!("failed to run command: {error}") } } diff --git a/src/tools/miri/miri b/src/tools/miri/miri index 7cda995879cfb..ace3d17ae2afb 100755 --- a/src/tools/miri/miri +++ b/src/tools/miri/miri @@ -124,7 +124,7 @@ rustc-pull) git commit rust-version -m "Preparing for merge from rustc" || (echo "FAILED to commit rust-version file, something went wrong"; exit 1) # Fetch given rustc commit and note down which one that was git fetch http://localhost:8000/rust-lang/rust.git@$FETCH_COMMIT$JOSH_FILTER.git || (echo "FAILED to fetch new commits, something went wrong"; exit 1) - git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits, something went wrong"; exit 1) + git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits ($(git rev-parse FETCH_HEAD)), something went wrong"; exit 1) exit 0 ;; rustc-push) @@ -325,6 +325,7 @@ run|run-dep) MIRIFLAGS="$MIRIFLAGS --target $MIRI_TEST_TARGET" fi + CARGO="$CARGO --quiet" # First build and get a sysroot. $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml find_sysroot diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 6790454bdb72b..02b0dd16f91c1 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -75726cae37317c7262b69d3e9fd11a3496a88d04 +d4096e0412ac5de785d739a0aa2b1c1c7b9d3b7d diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 2dc33334417e3..1ec4cbc4de71d 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -67,7 +67,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { if tcx.sess.compile_status().is_err() { tcx.sess.fatal("miri cannot be run on programs that fail compilation"); } -; + init_late_loggers(handler, tcx); if !tcx.sess.crate_types().contains(&CrateType::Executable) { tcx.sess.fatal("miri only makes sense on bin crates"); diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index c9674e0a2fe2c..de307a3c5f5be 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -221,7 +221,10 @@ impl AllocHistory { impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { pub fn start_grant(&mut self, perm: Permission) { let Operation::Retag(op) = &mut self.operation else { - unreachable!("start_grant must only be called during a retag, this is: {:?}", self.operation) + unreachable!( + "start_grant must only be called during a retag, this is: {:?}", + self.operation + ) }; op.permission = Some(perm); @@ -286,7 +289,8 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { tag: BorTag, protector_tag: Option, ) -> Option { - let Some(created) = self.history + let Some(created) = self + .history .creations .iter() .rev() @@ -315,22 +319,27 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { None } }) - }).or_else(|| { + }) + .or_else(|| { // If we didn't find a retag that created this tag, it might be the base tag of // this allocation. if self.history.base.0.tag() == tag { Some(( - format!("{tag:?} was created here, as the base tag for {:?}", self.history.id), - self.history.base.1.data() + format!( + "{tag:?} was created here, as the base tag for {:?}", + self.history.id + ), + self.history.base.1.data(), )) } else { None } - }) else { - // But if we don't have a creation event, this is related to a wildcard, and there - // is really nothing we can do to help. - return None; - }; + }) + else { + // But if we don't have a creation event, this is related to a wildcard, and there + // is really nothing we can do to help. + return None; + }; let invalidated = self.history.invalidations.iter().rev().find_map(|event| { if event.tag == tag { Some(event.generate_diagnostic()) } else { None } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 581327e54f1bc..ca0f69450c931 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -430,12 +430,15 @@ impl<'tcx> Stack { .find_granting(AccessKind::Write, derived_from, exposed_tags) .map_err(|()| dcx.grant_error(self))?; - let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else { + let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) + else { // The parent is a wildcard pointer or matched the unknown bottom. // This is approximate. Nobody knows what happened, so forget everything. // The new thing is SRW anyway, so we cannot push it "on top of the unknown part" // (for all we know, it might join an SRW group inside the unknown). - trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown"); + trace!( + "reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown" + ); self.set_unknown_bottom(global.next_ptr_tag); return Ok(()); }; @@ -1008,7 +1011,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to turn the place into a pointer to use the existing code. // (The pointer type does not matter, so we use a raw pointer.) - let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?; + let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?; let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout); // Reborrow it. With protection! That is part of the point. let new_perm = NewPermission::Uniform { diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index a1e949183ad40..291807c25eeb7 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -196,19 +196,19 @@ impl<'tcx> Stack { let ProvenanceExtra::Concrete(tag) = tag else { // Handle the wildcard case. // Go search the stack for an exposed tag. - if let Some(idx) = - self.borrows - .iter() - .enumerate() // we also need to know *where* in the stack - .rev() // search top-to-bottom - .find_map(|(idx, item)| { - // If the item fits and *might* be this wildcard, use it. - if item.perm().grants(access) && exposed_tags.contains(&item.tag()) { - Some(idx) - } else { - None - } - }) + if let Some(idx) = self + .borrows + .iter() + .enumerate() // we also need to know *where* in the stack + .rev() // search top-to-bottom + .find_map(|(idx, item)| { + // If the item fits and *might* be this wildcard, use it. + if item.perm().grants(access) && exposed_tags.contains(&item.tag()) { + Some(idx) + } else { + None + } + }) { return Ok(Some(idx)); } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index a87a4bbddadea..3b2d6f9608ee4 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -570,9 +570,13 @@ impl DisplayRepr { extraction_aux(tree, tree.root, show_unnamed, &mut v); let Some(root) = v.pop() else { if show_unnamed { - unreachable!("This allocation contains no tags, not even a root. This should not happen."); + unreachable!( + "This allocation contains no tags, not even a root. This should not happen." + ); } - eprintln!("This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."); + eprintln!( + "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags." + ); return None; }; assert!(v.is_empty()); diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 4c0690c585cd5..e134b73988800 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -256,7 +256,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' ptr_size.bytes() ); - let Some(new_perm) = new_perm else { return Ok(Some((alloc_id, orig_tag))); }; + let Some(new_perm) = new_perm else { + return Ok(Some((alloc_id, orig_tag))); + }; if let Some(protect) = new_perm.protector { // We register the protection in two different places. @@ -509,7 +511,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to turn the place into a pointer to use the existing code. // (The pointer type does not matter, so we use a raw pointer.) - let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?; + let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?; let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout); // Reborrow it. With protection! That is part of the point. // FIXME: do we truly want a 2phase borrow here? diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 56de9c007d0e9..aba7dd5a9fee0 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -305,7 +305,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( for arg in config.args.iter() { // Make space for `0` terminator. let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap(); - let arg_type = Ty::new_array(tcx,tcx.types.u8, size); + let arg_type = Ty::new_array(tcx, tcx.types.u8, size); let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into())?; ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?; @@ -313,9 +313,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( argvs.push(arg_place.to_ref(&ecx)); } // Make an array with all these pointers, in the Miri memory. - let argvs_layout = ecx.layout_of( - Ty::new_array(tcx,Ty::new_imm_ptr(tcx,tcx.types.u8), u64::try_from(argvs.len()).unwrap()), - )?; + let argvs_layout = ecx.layout_of(Ty::new_array( + tcx, + Ty::new_imm_ptr(tcx, tcx.types.u8), + u64::try_from(argvs.len()).unwrap(), + ))?; let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { let place = ecx.mplace_field(&argvs_place, idx)?; @@ -333,7 +335,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.machine.argc = Some(*argc_place); let argv_place = ecx.allocate( - ecx.layout_of(Ty::new_imm_ptr(tcx,tcx.types.unit))?, + ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, MiriMemoryKind::Machine.into(), )?; ecx.write_immediate(argv, &argv_place.into())?; @@ -345,7 +347,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( // Construct a command string with all the arguments. let cmd_utf16: Vec = args_to_utf16_command_string(config.args.iter()); - let cmd_type = Ty::new_array(tcx,tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap()); + let cmd_type = + Ty::new_array(tcx, tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap()); let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?; ecx.machine.cmd_line = Some(*cmd_place); @@ -366,7 +369,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( match entry_type { EntryFnType::Main { .. } => { - let start_id = tcx.lang_items().start_fn().unwrap(); + let start_id = tcx.lang_items().start_fn().unwrap_or_else(|| { + tcx.sess.fatal( + "could not find start function. Make sure the entry point is marked with `#[start]`." + ); + }); let main_ret_ty = tcx.fn_sig(entry_id).no_bound_vars().unwrap().output(); let main_ret_ty = main_ret_ty.no_bound_vars().unwrap(); let start_instance = ty::Instance::resolve( diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index e79bb47c78b18..7e92dc7a0c743 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -9,6 +9,7 @@ #![feature(local_key_cell_methods)] #![feature(round_ties_even)] #![feature(os_str_bytes)] +#![feature(lint_reasons)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 5fcf3905199d3..ac2bad221199c 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -313,10 +313,12 @@ pub struct PrimitiveLayouts<'tcx> { impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result> { let tcx = layout_cx.tcx; - let mut_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut }); - let const_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); + let mut_raw_ptr = + Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut }); + let const_raw_ptr = + Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); Ok(Self { - unit: layout_cx.layout_of(Ty::new_unit(tcx,))?, + unit: layout_cx.layout_of(Ty::new_unit(tcx))?, i8: layout_cx.layout_of(tcx.types.i8)?, i16: layout_cx.layout_of(tcx.types.i16)?, i32: layout_cx.layout_of(tcx.types.i32)?, diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index eaa66f1f87469..adf9a35d5c3a3 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let len: u64 = ptrs.len().try_into().unwrap(); let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; - let array_layout = this.layout_of(Ty::new_array(tcx.tcx,ptr_ty, len)).unwrap(); + let array_layout = this.layout_of(Ty::new_array(tcx.tcx, ptr_ty, len)).unwrap(); match flags { // storage for pointers is allocated by miri diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 3d4967f1f834e..1dcb877a83f0d 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -5,8 +5,8 @@ use std::mem; use rustc_const_eval::interpret::Pointer; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::Ty; use rustc_target::abi::Size; use crate::helpers::target_os_is_unix; @@ -449,9 +449,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { vars.push(Pointer::null()); // Make an array with all these pointers inside Miri. let tcx = this.tcx; - let vars_layout = this.layout_of( - Ty::new_array(tcx.tcx,this.machine.layouts.mut_raw_ptr.ty, u64::try_from(vars.len()).unwrap()), - )?; + let vars_layout = this.layout_of(Ty::new_array( + tcx.tcx, + this.machine.layouts.mut_raw_ptr.ty, + u64::try_from(vars.len()).unwrap(), + ))?; let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; for (idx, var) in vars.into_iter().enumerate() { let place = this.mplace_field(&vars_place, idx)?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index f4e91c30d9f1a..6915c396d616c 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -763,6 +763,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; let n = this.read_target_usize(n)?; + + // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that. + // (This is more than Rust requires, so `mem_copy` is not sufficient.) + this.ptr_get_alloc_id(ptr_dest)?; + this.ptr_get_alloc_id(ptr_src)?; + this.mem_copy( ptr_src, Align::ONE, diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 1995db715e8da..94f8cfbfb1c3d 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -483,7 +483,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `index` is an array, not a SIMD type let ty::Array(_, index_len) = index.layout.ty.kind() else { - span_bug!(this.cur_span(), "simd_shuffle index argument has non-array type {}", index.layout.ty) + span_bug!( + this.cur_span(), + "simd_shuffle index argument has non-array type {}", + index.layout.ty + ) }; let index_len = index_len.eval_target_usize(*this.tcx, this.param_env()); @@ -622,9 +626,7 @@ fn fmax_op<'tcx>( right: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmax operand is not a float") - }; + let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmax operand is not a float") }; let left = left.to_scalar(); let right = right.to_scalar(); Ok(match float_ty { @@ -638,9 +640,7 @@ fn fmin_op<'tcx>( right: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmin operand is not a float") - }; + let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmin operand is not a float") }; let left = left.to_scalar(); let right = right.to_scalar(); Ok(match float_ty { diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 4cdd54fa21b77..f08f0aad5e764 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -7,8 +7,8 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(windows)] use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::Ty; use crate::*; @@ -141,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. let this = self.eval_context_mut(); - let arg_type = Ty::new_array(this.tcx.tcx,this.tcx.types.u8, size); + let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u8, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap(); assert!(written); @@ -157,7 +157,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); - let arg_type = Ty::new_array(this.tcx.tcx,this.tcx.types.u16, size); + let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr, size, /*truncate*/ false).unwrap(); diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs index dc395d39ce11a..87e887000c523 100644 --- a/src/tools/miri/src/shims/unix/linux/fd.rs +++ b/src/tools/miri/src/shims/unix/linux/fd.rs @@ -181,6 +181,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics. /// /// + #[expect(clippy::needless_if)] fn eventfd( &mut self, val: &OpTy<'tcx, Provenance>, diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 73671e716efe5..59143550253d6 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -145,7 +145,8 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R // The files we're actually interested in (all `.rs` files). |path| { path.extension().is_some_and(|ext| ext == "rs") - && (filters.is_empty() || filters.iter().any(|f| path.starts_with(f))) + && (filters.is_empty() + || filters.iter().any(|f| path.display().to_string().contains(f))) }, // This could be used to overwrite the `Config` on a per-test basis. |_, _| None, @@ -274,13 +275,13 @@ fn main() -> Result<()> { fn run_dep_mode(target: String, mut args: impl Iterator) -> Result<()> { let path = args.next().expect("./miri run-dep must be followed by a file name"); let mut config = test_config(&target, "", Mode::Yolo, /* with dependencies */ true); - config.program.args.remove(0); // remove the `--error-format=json` argument - config.program.args.push("--color".into()); - config.program.args.push("always".into()); - let mut cmd = ui_test::test_command(config, Path::new(&path))?; - // Separate the arguments to the `cargo miri` invocation from - // the arguments to the interpreted prog - cmd.arg("--"); + config.program.args.clear(); // We want to give the user full control over flags + config.build_dependencies_and_link_them()?; + + let mut cmd = config.program.build(&config.out_dir); + + cmd.arg(path); + cmd.args(args); if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } } diff --git a/src/tools/miri/tests/fail/shims/memchr_null.rs b/src/tools/miri/tests/fail/shims/memchr_null.rs new file mode 100644 index 0000000000000..6bc7af7e6bf77 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memchr_null.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No libc on Windows + +use std::ptr; + +// null is explicitly called out as UB in the C docs. +fn main() { + unsafe { + libc::memchr(ptr::null(), 0, 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memchr_null.stderr b/src/tools/miri/tests/fail/shims/memchr_null.stderr new file mode 100644 index 0000000000000..d48606f34ad9e --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memchr_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) + --> $DIR/memchr_null.rs:LL:CC + | +LL | libc::memchr(ptr::null(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memchr_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.rs b/src/tools/miri/tests/fail/shims/memcmp_null.rs new file mode 100644 index 0000000000000..a4e0034c40bdc --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcmp_null.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No libc on Windows + +use std::ptr; + +// null is explicitly called out as UB in the C docs. +fn main() { + unsafe { + libc::memcmp(ptr::null(), ptr::null(), 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.stderr b/src/tools/miri/tests/fail/shims/memcmp_null.stderr new file mode 100644 index 0000000000000..7a09c77989455 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcmp_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) + --> $DIR/memcmp_null.rs:LL:CC + | +LL | libc::memcmp(ptr::null(), ptr::null(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memcmp_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memcpy_zero.rs b/src/tools/miri/tests/fail/shims/memcpy_zero.rs new file mode 100644 index 0000000000000..5283fea4cb989 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcpy_zero.rs @@ -0,0 +1,12 @@ +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-permissive-provenance +// C's memcpy is 0 bytes is UB for some pointers that are allowed in Rust's `copy_nonoverlapping`. + +fn main() { + let from = 42 as *const u8; + let to = 23 as *mut u8; + unsafe { + to.copy_from(from, 0); // this is fine + libc::memcpy(to.cast(), from.cast(), 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memcpy_zero.stderr b/src/tools/miri/tests/fail/shims/memcpy_zero.stderr new file mode 100644 index 0000000000000..7c1c3fe20c4e5 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcpy_zero.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance) + --> $DIR/memcpy_zero.rs:LL:CC + | +LL | libc::memcpy(to.cast(), from.cast(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memcpy_zero.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.rs b/src/tools/miri/tests/fail/shims/memrchr_null.rs new file mode 100644 index 0000000000000..b6707d558d8f6 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memrchr_null.rs @@ -0,0 +1,11 @@ +//@ignore-target-windows: No libc on Windows +//@ignore-target-apple: No `memrchr` on some apple targets + +use std::ptr; + +// null is explicitly called out as UB in the C docs. +fn main() { + unsafe { + libc::memrchr(ptr::null(), 0, 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.stderr b/src/tools/miri/tests/fail/shims/memrchr_null.stderr new file mode 100644 index 0000000000000..b5b7630e7fd0d --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memrchr_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) + --> $DIR/memrchr_null.rs:LL:CC + | +LL | libc::memrchr(ptr::null(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memrchr_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/pass/vec.rs b/src/tools/miri/tests/pass/vec.rs index 048f7d1c3512a..4ab2bcb7f2281 100644 --- a/src/tools/miri/tests/pass/vec.rs +++ b/src/tools/miri/tests/pass/vec.rs @@ -100,7 +100,7 @@ fn vec_push_ptr_stable() { v.push(0); let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. v.push(1); - let _val = *v0; + *v0 = *v0; } fn vec_extend_ptr_stable() { @@ -109,23 +109,23 @@ fn vec_extend_ptr_stable() { let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. // `slice::Iter` (with `T: Copy`) specialization v.extend(&[1]); - let _val = *v0; + *v0 = *v0; // `vec::IntoIter` specialization v.extend(vec![2]); - let _val = *v0; + *v0 = *v0; // `TrustedLen` specialization v.extend(std::iter::once(3)); - let _val = *v0; + *v0 = *v0; // base case v.extend(std::iter::once(3).filter(|_| true)); - let _val = *v0; + *v0 = *v0; } fn vec_truncate_ptr_stable() { let mut v = vec![0; 10]; let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. v.truncate(5); - let _val = *v0; + *v0 = *v0; } fn push_str_ptr_stable() { diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs index 011a253c6ff61..a7c9e4845c70a 100644 --- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs +++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs @@ -3,6 +3,7 @@ // ignore-macos slightly different policy on stack protection of arrays // ignore-windows stack check code uses different function names // ignore-nvptx64 stack protector is not supported +// ignore-wasm32-bare // [all] compile-flags: -Z stack-protector=all // [strong] compile-flags: -Z stack-protector=strong // [basic] compile-flags: -Z stack-protector=basic diff --git a/tests/assembly/wasm_exceptions.rs b/tests/assembly/wasm_exceptions.rs new file mode 100644 index 0000000000000..b7d20881b624f --- /dev/null +++ b/tests/assembly/wasm_exceptions.rs @@ -0,0 +1,60 @@ +// only-wasm32-bare +// assembly-output: emit-asm +// compile-flags: -C target-feature=+exception-handling +// compile-flags: -C panic=unwind +// compile-flags: -C llvm-args=-wasm-enable-eh + +#![crate_type = "lib"] +#![feature(core_intrinsics)] +#![feature(rustc_attrs)] + +extern { + fn may_panic(); + + #[rustc_nounwind] + fn log_number(number: usize); +} + +struct LogOnDrop; + +impl Drop for LogOnDrop { + fn drop(&mut self) { + unsafe { log_number(0); } + } +} + +// CHECK-LABEL: test_cleanup: +#[no_mangle] +pub fn test_cleanup() { + let _log_on_drop = LogOnDrop; + unsafe { may_panic(); } + + // CHECK-NOT: call + // CHECK: try + // CHECK: call may_panic + // CHECK: catch_all + // CHECK: rethrow + // CHECK: end_try +} + +// CHECK-LABEL: test_rtry: +#[no_mangle] +pub fn test_rtry() { + unsafe { + core::intrinsics::r#try(|_| { + may_panic(); + }, core::ptr::null_mut(), |data, exception| { + log_number(data as usize); + log_number(exception as usize); + }); + } + + // CHECK-NOT: call + // CHECK: try + // CHECK: call may_panic + // CHECK: catch + // CHECK: call log_number + // CHECK: call log_number + // CHECK-NOT: rethrow + // CHECK: end_try +} diff --git a/tests/codegen/repr-transparent-aggregates-1.rs b/tests/codegen/repr-transparent-aggregates-1.rs index 9c4b0e58e7187..ba3ba272efbb8 100644 --- a/tests/codegen/repr-transparent-aggregates-1.rs +++ b/tests/codegen/repr-transparent-aggregates-1.rs @@ -11,6 +11,7 @@ // ignore-s390x // ignore-windows // ignore-loongarch64 +// ignore-wasm32-bare // See repr-transparent.rs #![feature(transparent_unions)] diff --git a/tests/codegen/wasm_exceptions.rs b/tests/codegen/wasm_exceptions.rs new file mode 100644 index 0000000000000..2b2359f5b6caa --- /dev/null +++ b/tests/codegen/wasm_exceptions.rs @@ -0,0 +1,51 @@ +// only-wasm32-bare +// compile-flags: -C panic=unwind + +#![crate_type = "lib"] +#![feature(core_intrinsics)] +#![feature(rustc_attrs)] + +extern { + fn may_panic(); + + #[rustc_nounwind] + fn log_number(number: usize); +} + +struct LogOnDrop; + +impl Drop for LogOnDrop { + fn drop(&mut self) { + unsafe { log_number(0); } + } +} + +// CHECK-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0 +#[no_mangle] +pub fn test_cleanup() { + let _log_on_drop = LogOnDrop; + unsafe { may_panic(); } + + // CHECK-NOT: call + // CHECK: invoke void @may_panic() + // CHECK: %cleanuppad = cleanuppad within none [] +} + +// CHECK-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0 +#[no_mangle] +pub fn test_rtry() { + unsafe { + core::intrinsics::r#try(|_| { + may_panic(); + }, core::ptr::null_mut(), |data, exception| { + log_number(data as usize); + log_number(exception as usize); + }); + } + + // CHECK-NOT: call + // CHECK: invoke void @may_panic() + // CHECK: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller + // CHECK: {{.*}} = catchpad within {{.*}} [ptr null] + // CHECK: catchret +} diff --git a/tests/run-make/wasm-exceptions-nostd/Makefile b/tests/run-make/wasm-exceptions-nostd/Makefile new file mode 100644 index 0000000000000..34755ec14b745 --- /dev/null +++ b/tests/run-make/wasm-exceptions-nostd/Makefile @@ -0,0 +1,12 @@ +include ../tools.mk + +# only-wasm32-bare + +# Add a few command line args to make exceptions work +RUSTC := $(RUSTC) -C llvm-args=-wasm-enable-eh +RUSTC := $(RUSTC) -C target-feature=+exception-handling +RUSTC := $(RUSTC) -C panic=unwind + +all: + $(RUSTC) src/lib.rs --target wasm32-unknown-unknown + $(NODE) verify.mjs $(TMPDIR)/lib.wasm diff --git a/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs b/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs new file mode 100644 index 0000000000000..572d253309cee --- /dev/null +++ b/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs @@ -0,0 +1,67 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::UnsafeCell; + +#[global_allocator] +static ALLOCATOR: ArenaAllocator = ArenaAllocator::new(); + +/// Very simple allocator which never deallocates memory +/// +/// Based on the example from +/// https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html +pub struct ArenaAllocator { + arena: UnsafeCell, +} + +impl ArenaAllocator { + pub const fn new() -> Self { + Self { + arena: UnsafeCell::new(Arena::new()), + } + } +} + +/// Safe because we are singlethreaded +unsafe impl Sync for ArenaAllocator {} + +unsafe impl GlobalAlloc for ArenaAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let arena = &mut *self.arena.get(); + arena.alloc(layout) + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +const ARENA_SIZE: usize = 64 * 1024; // more than enough + +#[repr(C, align(4096))] +struct Arena { + buf: [u8; ARENA_SIZE], // aligned at 4096 + allocated: usize, +} + +impl Arena { + pub const fn new() -> Self { + Self { + buf: [0x55; ARENA_SIZE], + allocated: 0, + } + } + + pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 { + if layout.align() > 4096 || layout.size() > ARENA_SIZE { + return core::ptr::null_mut(); + } + + let align_minus_one = layout.align() - 1; + let start = (self.allocated + align_minus_one) & !align_minus_one; // round up + let new_cursor = start + layout.size(); + + if new_cursor >= ARENA_SIZE { + return core::ptr::null_mut(); + } + + self.allocated = new_cursor; + self.buf.as_mut_ptr().add(start) + } +} diff --git a/tests/run-make/wasm-exceptions-nostd/src/lib.rs b/tests/run-make/wasm-exceptions-nostd/src/lib.rs new file mode 100644 index 0000000000000..7049d2fd9e0d1 --- /dev/null +++ b/tests/run-make/wasm-exceptions-nostd/src/lib.rs @@ -0,0 +1,60 @@ +#![no_std] +#![crate_type = "cdylib"] + +// Allow a few unstable features because we create a panic +// runtime for native wasm exceptions from scratch + +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(link_llvm_intrinsics)] +#![feature(panic_info_message)] + +extern crate alloc; + +/// This module allows us to use `Box`, `String`, ... even in no-std +mod arena_alloc; + +/// This module allows logging text, even in no-std +mod logging; + +/// This module allows exceptions, even in no-std +#[cfg(target_arch = "wasm32")] +mod panicking; + +use alloc::boxed::Box; +use alloc::string::String; + +struct LogOnDrop; + +impl Drop for LogOnDrop { + fn drop(&mut self) { + logging::log_str("Dropped"); + } +} + +#[allow(unreachable_code)] +#[allow(unconditional_panic)] +#[no_mangle] +pub extern "C" fn start() -> usize { + let data = 0x1234usize as *mut u8; // Something to recognize + + unsafe { + core::intrinsics::r#try(|data: *mut u8| { + let _log_on_drop = LogOnDrop; + + logging::log_str(&alloc::format!("`r#try` called with ptr {:?}", data)); + let x = [12]; + let _ = x[4]; // should panic + + logging::log_str("This line should not be visible! :("); + }, data, |data, exception| { + let exception = *Box::from_raw(exception as *mut String); + logging::log_str("Caught something!"); + logging::log_str(&alloc::format!(" data : {:?}", data)); + logging::log_str(&alloc::format!(" exception: {:?}", exception)); + }); + } + + logging::log_str("This program terminates correctly."); + 0 +} diff --git a/tests/run-make/wasm-exceptions-nostd/src/logging.rs b/tests/run-make/wasm-exceptions-nostd/src/logging.rs new file mode 100644 index 0000000000000..569d03ec82f57 --- /dev/null +++ b/tests/run-make/wasm-exceptions-nostd/src/logging.rs @@ -0,0 +1,9 @@ +extern "C" { + fn __log_utf8(ptr: *const u8, size: usize); +} + +pub fn log_str(text: &str) { + unsafe { + __log_utf8(text.as_ptr(), text.len()); + } +} diff --git a/tests/run-make/wasm-exceptions-nostd/src/panicking.rs b/tests/run-make/wasm-exceptions-nostd/src/panicking.rs new file mode 100644 index 0000000000000..4a8923fd43db6 --- /dev/null +++ b/tests/run-make/wasm-exceptions-nostd/src/panicking.rs @@ -0,0 +1,29 @@ +#[lang = "eh_personality"] +fn eh_personality() {} + +mod internal { + extern "C" { + #[link_name = "llvm.wasm.throw"] + pub fn wasm_throw(tag: i32, ptr: *mut u8) -> !; + } +} + +unsafe fn wasm_throw(ptr: *mut u8) -> ! { + internal::wasm_throw(0, ptr); +} + +#[panic_handler] +fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { + use alloc::boxed::Box; + use alloc::string::ToString; + + let msg = info + .message() + .map(|msg| msg.to_string()) + .unwrap_or("(no message)".to_string()); + let exception = Box::new(msg.to_string()); + unsafe { + let exception_raw = Box::into_raw(exception); + wasm_throw(exception_raw as *mut u8); + } +} diff --git a/tests/run-make/wasm-exceptions-nostd/verify.mjs b/tests/run-make/wasm-exceptions-nostd/verify.mjs new file mode 100644 index 0000000000000..e6c44d89d331d --- /dev/null +++ b/tests/run-make/wasm-exceptions-nostd/verify.mjs @@ -0,0 +1,75 @@ +import fs from 'fs'; + +const dec = new TextDecoder("utf-8"); + +if (process.argv.length != 3) { + console.log("Usage: node verify.mjs "); + process.exit(0); +} + +const wasmfile = process.argv[2]; +if (!fs.existsSync(wasmfile)) { + console.log("Error: File not found:", wasmfile); + process.exit(1); +} + +const wasmBuffer = fs.readFileSync(wasmfile); + +async function main() { + + let memory = new ArrayBuffer(0) // will be changed after instantiate + + const captured_output = []; + + const imports = { + env: { + __log_utf8: (ptr, size) => { + const str = dec.decode(new DataView(memory, ptr, size)); + captured_output.push(str); + console.log(str); + } + } + }; + + const wasmModule = await WebAssembly.instantiate(wasmBuffer, imports); + memory = wasmModule.instance.exports.memory.buffer; + + const start = wasmModule.instance.exports.start; + const return_code = start(); + + console.log("Return-Code:", return_code); + + if (return_code !== 0) { + console.error("Expected return code 0"); + process.exit(return_code); + } + + const expected_output = [ + '`r#try` called with ptr 0x1234', + 'Dropped', + 'Caught something!', + ' data : 0x1234', + ' exception: "index out of bounds: the len is 1 but the index is 4"', + 'This program terminates correctly.', + ]; + + assert_equal(captured_output, expected_output); +} + +function assert_equal(captured_output, expected_output) { + if (captured_output.length != expected_output.length) { + console.error("Unexpected number of output lines. Got", captured_output.length, "but expected", expected_output.length); + process.exit(1); // exit with error + } + + for (let idx = 0; idx < expected_output.length; ++idx) { + if (captured_output[idx] !== expected_output[idx]) { + console.error("Unexpected output"); + console.error("[got] ", captured_output[idx]); + console.error("[expected]", expected_output[idx]); + process.exit(2); // exit with error + } + } +} + +await main(); \ No newline at end of file diff --git a/tests/ui/resolve/issue-2356.stderr b/tests/ui/resolve/issue-2356.stderr index 313b3e30dd956..30f5f05952664 100644 --- a/tests/ui/resolve/issue-2356.stderr +++ b/tests/ui/resolve/issue-2356.stderr @@ -1,18 +1,3 @@ -error[E0425]: cannot find function `default` in this scope - --> $DIR/issue-2356.rs:31:5 - | -LL | default(); - | ^^^^^^^ - | -help: you might have meant to call the associated function - | -LL | Self::default(); - | ~~~~~~~~~~~~~ -help: consider importing this function - | -LL + use std::default::default; - | - error[E0425]: cannot find value `whiskers` in this scope --> $DIR/issue-2356.rs:39:5 | @@ -64,6 +49,12 @@ error[E0425]: cannot find function `clone` in this scope LL | clone(); | ^^^^^ help: you might have meant to call the method: `self.clone` +error[E0425]: cannot find function `default` in this scope + --> $DIR/issue-2356.rs:31:5 + | +LL | default(); + | ^^^^^^^ help: you might have meant to call the associated function: `Self::default` + error[E0425]: cannot find function `shave` in this scope --> $DIR/issue-2356.rs:41:5 |