From 4ba20f6a080724faca9e38647d4c66056871a8f2 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 17 Oct 2024 05:05:38 +0000 Subject: [PATCH 01/27] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 8b9e7efdff908..d0f9fa7ac4297 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -17a19e684cdf3ca088af8b4da6a6209d128913f4 +dd5127615ad626741a1116d022cf784637ac05df From 8640c43c684a1d11f6aad2ccc86accc6844981ac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 17 Oct 2024 08:32:40 +0200 Subject: [PATCH 02/27] data-race test: disable address reuse that can incur accidental sync --- src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs index f4c009456d296..55491da9f60d7 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs @@ -1,7 +1,7 @@ //! This is a regression test for : we had some //! faulty logic around `release_clock` that led to this code not reporting a data race. //@ignore-target: windows # no libc socketpair on Windows -//@compile-flags: -Zmiri-preemption-rate=0 +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-rate=0 use std::thread; fn main() { From d7e91ba962e05ff38c9a89ebdde106cc8991a099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Thu, 17 Oct 2024 12:37:09 +0200 Subject: [PATCH 03/27] miri: improve support for `f16` and `f128` Rounding intrinsics are now implemented for `f16` and `f128` and tests for `is_infinite`, NaN, `abs`, `copysign`, `min`, `max`, rounding, `*_fast` and `*_algebraic` have been added. --- src/tools/miri/src/intrinsics/mod.rs | 30 +++++ src/tools/miri/tests/pass/float.rs | 160 +++++++++++++++++++++++++-- 2 files changed, 181 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 09ec2cb46b05e..776d2561b430f 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -145,6 +145,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_bool(branch), dest)?; } + "floorf16" | "ceilf16" | "truncf16" | "roundf16" | "rintf16" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f16()?; + let mode = match intrinsic_name { + "floorf16" => Round::TowardNegative, + "ceilf16" => Round::TowardPositive, + "truncf16" => Round::TowardZero, + "roundf16" => Round::NearestTiesToAway, + "rintf16" => Round::NearestTiesToEven, + _ => bug!(), + }; + let res = f.round_to_integral(mode).value; + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; @@ -175,6 +190,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + "floorf128" | "ceilf128" | "truncf128" | "roundf128" | "rintf128" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f128()?; + let mode = match intrinsic_name { + "floorf128" => Round::TowardNegative, + "ceilf128" => Round::TowardPositive, + "truncf128" => Round::TowardZero, + "roundf128" => Round::NearestTiesToAway, + "rintf128" => Round::NearestTiesToEven, + _ => bug!(), + }; + let res = f.round_to_integral(mode).value; + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } #[rustfmt::skip] | "sinf32" diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 853d3e80517da..66843ca584b8d 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -157,13 +157,18 @@ fn basic() { assert_eq(-{ 5.0_f128 }, -5.0_f128); // infinities, NaN - // FIXME(f16_f128): add when constants and `is_infinite` are available + assert!((5.0_f16 / 0.0).is_infinite()); + assert_ne!({ 5.0_f16 / 0.0 }, { -5.0_f16 / 0.0 }); assert!((5.0_f32 / 0.0).is_infinite()); assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 }); assert!((5.0_f64 / 0.0).is_infinite()); assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 }); + assert!((5.0_f128 / 0.0).is_infinite()); + assert_ne!({ 5.0_f128 / 0.0 }, { 5.0_f128 / -0.0 }); + assert_ne!(f16::NAN, f16::NAN); assert_ne!(f32::NAN, f32::NAN); assert_ne!(f64::NAN, f64::NAN); + assert_ne!(f128::NAN, f128::NAN); // negative zero let posz = 0.0f16; @@ -215,9 +220,14 @@ fn basic() { assert!((black_box(-1.0f128) % 1.0).is_sign_negative()); assert!((black_box(-1.0f128) % -1.0).is_sign_negative()); - // FIXME(f16_f128): add when `abs` is available + assert_eq!((-1.0f16).abs(), 1.0f16); + assert_eq!(34.2f16.abs(), 34.2f16); assert_eq!((-1.0f32).abs(), 1.0f32); + assert_eq!(34.2f32.abs(), 34.2f32); + assert_eq!((-1.0f64).abs(), 1.0f64); assert_eq!(34.2f64.abs(), 34.2f64); + assert_eq!((-1.0f128).abs(), 1.0f128); + assert_eq!(34.2f128.abs(), 34.2f128); } /// Test casts from floats to ints and back @@ -654,6 +664,14 @@ fn casts() { } fn ops() { + // f16 min/max + assert_eq((1.0_f16).max(-1.0), 1.0); + assert_eq((1.0_f16).min(-1.0), -1.0); + assert_eq(f16::NAN.min(9.0), 9.0); + assert_eq(f16::NAN.max(-9.0), -9.0); + assert_eq((9.0_f16).min(f16::NAN), 9.0); + assert_eq((-9.0_f16).max(f16::NAN), -9.0); + // f32 min/max assert_eq((1.0 as f32).max(-1.0), 1.0); assert_eq((1.0 as f32).min(-1.0), -1.0); @@ -670,6 +688,21 @@ fn ops() { assert_eq((9.0 as f64).min(f64::NAN), 9.0); assert_eq((-9.0 as f64).max(f64::NAN), -9.0); + // f128 min/max + assert_eq((1.0_f128).max(-1.0), 1.0); + assert_eq((1.0_f128).min(-1.0), -1.0); + assert_eq(f128::NAN.min(9.0), 9.0); + assert_eq(f128::NAN.max(-9.0), -9.0); + assert_eq((9.0_f128).min(f128::NAN), 9.0); + assert_eq((-9.0_f128).max(f128::NAN), -9.0); + + // f16 copysign + assert_eq(3.5_f16.copysign(0.42), 3.5_f16); + assert_eq(3.5_f16.copysign(-0.42), -3.5_f16); + assert_eq((-3.5_f16).copysign(0.42), 3.5_f16); + assert_eq((-3.5_f16).copysign(-0.42), -3.5_f16); + assert!(f16::NAN.copysign(1.0).is_nan()); + // f32 copysign assert_eq(3.5_f32.copysign(0.42), 3.5_f32); assert_eq(3.5_f32.copysign(-0.42), -3.5_f32); @@ -683,6 +716,13 @@ fn ops() { assert_eq((-3.5_f64).copysign(0.42), 3.5_f64); assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64); assert!(f64::NAN.copysign(1.0).is_nan()); + + // f128 copysign + assert_eq(3.5_f128.copysign(0.42), 3.5_f128); + assert_eq(3.5_f128.copysign(-0.42), -3.5_f128); + assert_eq((-3.5_f128).copysign(0.42), 3.5_f128); + assert_eq((-3.5_f128).copysign(-0.42), -3.5_f128); + assert!(f128::NAN.copysign(1.0).is_nan()); } /// Tests taken from rustc test suite. @@ -807,6 +847,18 @@ fn nan_casts() { fn rounding() { // Test cases taken from the library's tests for this feature + // f16 + assert_eq(2.5f16.round_ties_even(), 2.0f16); + assert_eq(1.0f16.round_ties_even(), 1.0f16); + assert_eq(1.3f16.round_ties_even(), 1.0f16); + assert_eq(1.5f16.round_ties_even(), 2.0f16); + assert_eq(1.7f16.round_ties_even(), 2.0f16); + assert_eq(0.0f16.round_ties_even(), 0.0f16); + assert_eq((-0.0f16).round_ties_even(), -0.0f16); + assert_eq((-1.0f16).round_ties_even(), -1.0f16); + assert_eq((-1.3f16).round_ties_even(), -1.0f16); + assert_eq((-1.5f16).round_ties_even(), -2.0f16); + assert_eq((-1.7f16).round_ties_even(), -2.0f16); // f32 assert_eq(2.5f32.round_ties_even(), 2.0f32); assert_eq(1.0f32.round_ties_even(), 1.0f32); @@ -831,23 +883,59 @@ fn rounding() { assert_eq((-1.3f64).round_ties_even(), -1.0f64); assert_eq((-1.5f64).round_ties_even(), -2.0f64); assert_eq((-1.7f64).round_ties_even(), -2.0f64); - + // f128 + assert_eq(2.5f128.round_ties_even(), 2.0f128); + assert_eq(1.0f128.round_ties_even(), 1.0f128); + assert_eq(1.3f128.round_ties_even(), 1.0f128); + assert_eq(1.5f128.round_ties_even(), 2.0f128); + assert_eq(1.7f128.round_ties_even(), 2.0f128); + assert_eq(0.0f128.round_ties_even(), 0.0f128); + assert_eq((-0.0f128).round_ties_even(), -0.0f128); + assert_eq((-1.0f128).round_ties_even(), -1.0f128); + assert_eq((-1.3f128).round_ties_even(), -1.0f128); + assert_eq((-1.5f128).round_ties_even(), -2.0f128); + assert_eq((-1.7f128).round_ties_even(), -2.0f128); + + assert_eq!(3.8f16.floor(), 3.0f16); + assert_eq!((-1.1f16).floor(), -2.0f16); assert_eq!(3.8f32.floor(), 3.0f32); + assert_eq!((-1.1f32).floor(), -2.0f32); + assert_eq!(3.8f64.floor(), 3.0f64); assert_eq!((-1.1f64).floor(), -2.0f64); + assert_eq!(3.8f128.floor(), 3.0f128); + assert_eq!((-1.1f128).floor(), -2.0f128); + assert_eq!(3.8f16.ceil(), 4.0f16); + assert_eq!((-2.3f16).ceil(), -2.0f16); + assert_eq!(3.8f32.ceil(), 4.0f32); assert_eq!((-2.3f32).ceil(), -2.0f32); assert_eq!(3.8f64.ceil(), 4.0f64); + assert_eq!((-2.3f64).ceil(), -2.0f64); + assert_eq!(3.8f128.ceil(), 4.0f128); + assert_eq!((-2.3f128).ceil(), -2.0f128); + assert_eq!(0.1f16.trunc(), 0.0f16); + assert_eq!((-0.1f16).trunc(), 0.0f16); assert_eq!(0.1f32.trunc(), 0.0f32); + assert_eq!((-0.1f32).trunc(), 0.0f32); + assert_eq!(0.1f64.trunc(), 0.0f64); assert_eq!((-0.1f64).trunc(), 0.0f64); + assert_eq!(0.1f128.trunc(), 0.0f128); + assert_eq!((-0.1f128).trunc(), 0.0f128); + assert_eq!(3.3_f16.round(), 3.0); + assert_eq!(2.5_f16.round(), 3.0); assert_eq!(3.3_f32.round(), 3.0); assert_eq!(2.5_f32.round(), 3.0); assert_eq!(3.9_f64.round(), 4.0); assert_eq!(2.5_f64.round(), 3.0); + assert_eq!(3.9_f128.round(), 4.0); + assert_eq!(2.5_f128.round(), 3.0); } fn mul_add() { + // FIXME(f16_f128): add when supported + assert_eq!(3.0f32.mul_add(2.0f32, 5.0f32), 11.0); assert_eq!(0.0f32.mul_add(-2.0, f32::consts::E), f32::consts::E); assert_eq!(3.0f64.mul_add(2.0, 5.0), 11.0); @@ -983,7 +1071,7 @@ fn test_fast() { use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; #[inline(never)] - pub fn test_operations_f64(a: f64, b: f64) { + pub fn test_operations_f16(a: f16, b: f16) { // make sure they all map to the correct operation unsafe { assert_eq!(fadd_fast(a, b), a + b); @@ -1006,10 +1094,38 @@ fn test_fast() { } } - test_operations_f64(1., 2.); - test_operations_f64(10., 5.); + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } + } + + #[inline(never)] + pub fn test_operations_f128(a: f128, b: f128) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } + } + + test_operations_f16(11., 2.); + test_operations_f16(10., 15.); test_operations_f32(11., 2.); test_operations_f32(10., 15.); + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f128(1., 2.); + test_operations_f128(10., 5.); } fn test_algebraic() { @@ -1018,7 +1134,7 @@ fn test_algebraic() { }; #[inline(never)] - pub fn test_operations_f64(a: f64, b: f64) { + pub fn test_operations_f16(a: f16, b: f16) { // make sure they all map to the correct operation assert_eq!(fadd_algebraic(a, b), a + b); assert_eq!(fsub_algebraic(a, b), a - b); @@ -1037,15 +1153,41 @@ fn test_algebraic() { assert_eq!(frem_algebraic(a, b), a % b); } - test_operations_f64(1., 2.); - test_operations_f64(10., 5.); + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); + } + + #[inline(never)] + pub fn test_operations_f128(a: f128, b: f128) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); + } + + test_operations_f16(11., 2.); + test_operations_f16(10., 15.); test_operations_f32(11., 2.); test_operations_f32(10., 15.); + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f128(1., 2.); + test_operations_f128(10., 5.); } fn test_fmuladd() { use std::intrinsics::{fmuladdf32, fmuladdf64}; + // FIXME(f16_f128): add when supported + #[inline(never)] pub fn test_operations_f32(a: f32, b: f32, c: f32) { assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c); From 5db0e4037b851adc25578f578763705156787469 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 18 Oct 2024 14:06:54 +0200 Subject: [PATCH 04/27] pthread-threadname: ensure we can see the name set via the Rust API --- .../tests/pass-dep/libc/pthread-threadname.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index 404ef7cc42d61..757d9e209344a 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -65,6 +65,22 @@ fn main() { } } + // Set name via Rust API, get it via pthreads. + let long_name2 = long_name.clone(); + thread::Builder::new() + .name(long_name.clone()) + .spawn(move || { + let mut buf = vec![0u8; long_name2.len() + 1]; + assert_eq!(get_thread_name(&mut buf), 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)]; + assert_eq!(cstr.to_bytes(), truncated_name.as_bytes()); + }) + .unwrap() + .join() + .unwrap(); + + // Set name via pthread and get it again (short name). thread::Builder::new() .spawn(move || { // Set short thread name. @@ -130,6 +146,7 @@ fn main() { .join() .unwrap(); + // Set name via pthread and get it again (long name). thread::Builder::new() .spawn(move || { // Set full thread name. From 2775f3962abe4060b4f11bfb5d2b822fbaf18035 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 18 Oct 2024 16:08:52 +0200 Subject: [PATCH 05/27] tail_calls: add test ensuring local vars are indeed gone --- .../fail/tail_calls/dangling-local-var.rs | 16 ++++++++++ .../fail/tail_calls/dangling-local-var.stderr | 30 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs create mode 100644 src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs new file mode 100644 index 0000000000000..b69f7ee9dff65 --- /dev/null +++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs @@ -0,0 +1,16 @@ +#![feature(explicit_tail_calls)] +#![allow(incomplete_features)] + +fn g(x: *const i32) { + let _val = unsafe { *x }; //~ERROR: has been freed, so this pointer is dangling +} + +fn f(_x: *const i32) { + let local = 0; + let ptr = &local as *const i32; + become g(ptr) +} + +fn main() { + f(std::ptr::null()); +} diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr new file mode 100644 index 0000000000000..6acd69ab3f8cf --- /dev/null +++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr @@ -0,0 +1,30 @@ +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC + | +LL | let _val = unsafe { *x }; + | ^^ memory access failed: ALLOC has been freed, so this pointer is dangling + | + = 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 +help: ALLOC was allocated here: + --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC + | +LL | let local = 0; + | ^^^^^ +help: ALLOC was deallocated here: + --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC + | +LL | } + | ^ + = note: BACKTRACE (of the first span): + = note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC +note: inside `main` + --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC + | +LL | f(std::ptr::null()); + | ^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From ea2ac892bd89b12cedde4071f2f5f9f6702be5c9 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 20 Oct 2024 04:55:19 +0000 Subject: [PATCH 06/27] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d0f9fa7ac4297..778061e13105e 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -dd5127615ad626741a1116d022cf784637ac05df +54791efd8235805dcfbdad3b8788e08f2142c50b From ccd680ee01564d32cad783a94c4dcccf228922c1 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 20 Oct 2024 05:04:32 +0000 Subject: [PATCH 07/27] fmt --- .../miri/src/borrow_tracker/stacked_borrows/diagnostics.rs | 6 +++++- src/tools/miri/tests/pass/dyn-upcast.rs | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) 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 1684abeec6bfb..5624c4c479ef6 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -424,7 +424,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { } #[inline(never)] // This is only called on fatal code paths - pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpErrorKind<'tcx> { + pub(super) fn protector_error( + &self, + item: &Item, + kind: ProtectorKind, + ) -> InterpErrorKind<'tcx> { let protected = match kind { ProtectorKind::WeakProtector => "weakly protected", ProtectorKind::StrongProtector => "strongly protected", diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs index 306e9ab9c6715..61410f7c4e0b5 100644 --- a/src/tools/miri/tests/pass/dyn-upcast.rs +++ b/src/tools/miri/tests/pass/dyn-upcast.rs @@ -433,7 +433,8 @@ fn replace_vptr() { } fn drop_principal() { - use std::{alloc::Layout, any::Any}; + use std::alloc::Layout; + use std::any::Any; const fn yeet_principal(x: Box) -> Box { x From 2b11c706900c51612ec6bc6cb5a6c01b30b5a0e6 Mon Sep 17 00:00:00 2001 From: Noah Bright Date: Fri, 4 Oct 2024 14:05:57 -0400 Subject: [PATCH 08/27] Replace `set_last_error` with `set_last_error_and_return_i*` Add `set_last_error_and_return_i64`, change calls from `this.libc_eval` to `LibcError`, and replace pairs of `set_last_error` and returning values of the right type with the new helper functions --- src/tools/miri/src/shims/io_error.rs | 10 +++ src/tools/miri/src/shims/unix/fd.rs | 3 +- src/tools/miri/src/shims/unix/fs.rs | 82 ++++++-------------- src/tools/miri/src/shims/unix/linux/epoll.rs | 15 +--- src/tools/miri/src/shims/unix/mem.rs | 6 +- 5 files changed, 42 insertions(+), 74 deletions(-) diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index 38aa181cb4f05..04491f0542bd8 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -141,6 +141,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(-1)) } + /// Sets the last OS error and return `-1` as a `i64`-typed Scalar + fn set_last_error_and_return_i64( + &mut self, + err: impl Into, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.set_last_error(err)?; + interp_ok(Scalar::from_i64(-1)) + } + /// Gets the last error variable. fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index e3914640037e3..c21982ee37932 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -561,8 +561,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// types (like `read`, that returns an `i64`). fn fd_not_found>(&mut self) -> InterpResult<'tcx, T> { let this = self.eval_context_mut(); - let ebadf = this.eval_libc("EBADF"); - this.set_last_error(ebadf)?; + this.set_last_error(LibcError("EBADF"))?; interp_ok((-1).into()) } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 4b3ae8e0520b3..fcb824ab35b38 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -528,8 +528,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let o_tmpfile = this.eval_libc_i32("O_TMPFILE"); if flag & o_tmpfile == o_tmpfile { // if the flag contains `O_TMPFILE` then we return a graceful error - this.set_last_error(LibcError("EOPNOTSUPP"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EOPNOTSUPP")); } } @@ -548,9 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // O_NOFOLLOW only fails when the trailing component is a symlink; // the entire rest of the path can still contain symlinks. if path.is_symlink() { - let eloop = this.eval_libc("ELOOP"); - this.set_last_error(eloop)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("ELOOP")); } } mirror |= o_nofollow; @@ -565,8 +562,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let fd = options @@ -584,8 +580,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let seek_from = if whence == this.eval_libc_i32("SEEK_SET") { if offset < 0 { // Negative offsets return `EINVAL`. - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(Scalar::from_i64(-1)); + return this.set_last_error_and_return_i64(LibcError("EINVAL")); } else { SeekFrom::Start(u64::try_from(offset).unwrap()) } @@ -594,8 +589,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if whence == this.eval_libc_i32("SEEK_END") { SeekFrom::End(i64::try_from(offset).unwrap()) } else { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(Scalar::from_i64(-1)); + return this.set_last_error_and_return_i64(LibcError("EINVAL")); }; let communicate = this.machine.communicate(); @@ -618,8 +612,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let result = remove_file(path).map(|_| 0); @@ -649,8 +642,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let result = create_link(&target, &linkpath).map(|_| 0); @@ -674,9 +666,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`stat`", reject_with)?; - let eacc = this.eval_libc("EACCES"); - this.set_last_error(eacc)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EACCES")); } // `stat` always follows symlinks. @@ -706,9 +696,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`lstat`", reject_with)?; - let eacc = this.eval_libc("EACCES"); - this.set_last_error(eacc)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EACCES")); } let metadata = match FileMetadata::from_path(this, &path, false)? { @@ -766,9 +754,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If the statxbuf or pathname pointers are null, the function fails with `EFAULT`. if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? { - let efault = this.eval_libc("EFAULT"); - this.set_last_error(efault)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EFAULT")); } let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?; @@ -809,8 +795,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert!(empty_path_flag); this.eval_libc("EBADF") }; - this.set_last_error(ecode)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ecode); } // the `_mask_op` parameter specifies the file information that the caller requested. @@ -939,9 +924,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let newpath_ptr = this.read_pointer(newpath_op)?; if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? { - let efault = this.eval_libc("EFAULT"); - this.set_last_error(efault)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EFAULT")); } let oldpath = this.read_path_from_c_str(oldpath_ptr)?; @@ -950,8 +933,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let result = rename(oldpath, newpath).map(|_| 0); @@ -974,8 +956,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } #[cfg_attr(not(unix), allow(unused_mut))] @@ -1002,8 +983,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let result = remove_dir(path).map(|_| 0i32); @@ -1019,8 +999,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`opendir`", reject_with)?; - let eacc = this.eval_libc("EACCES"); - this.set_last_error(eacc)?; + this.set_last_error(LibcError("EACCES"))?; return interp_ok(Scalar::null_ptr(this)); } @@ -1307,14 +1286,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(result)) } else { drop(fd); - this.set_last_error(LibcError("EINVAL"))?; - interp_ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EINVAL")) } } else { drop(fd); // The file is not writable - this.set_last_error(LibcError("EINVAL"))?; - interp_ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EINVAL")) } } @@ -1391,15 +1368,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let flags = this.read_scalar(flags_op)?.to_i32()?; if offset < 0 || nbytes < 0 { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE") | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE") | this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER"); if flags & allowed_flags != flags { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } // Reject if isolation is enabled. @@ -1436,8 +1411,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readlink`", reject_with)?; - let eacc = this.eval_libc("EACCES"); - this.set_last_error(eacc)?; + this.set_last_error(LibcError("EACCES"))?; return interp_ok(-1); } @@ -1574,9 +1548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkstemp`", reject_with)?; - let eacc = this.eval_libc("EACCES"); - this.set_last_error(eacc)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EACCES")); } // Get the bytes of the suffix we expect in _target_ encoding. @@ -1592,8 +1564,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If we don't find the suffix, it is an error. if last_six_char_bytes != suffix_bytes { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } // At this point we know we have 6 ASCII 'X' characters as a suffix. @@ -1658,17 +1629,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => { // "On error, -1 is returned, and errno is set to // indicate the error" - this.set_last_error(e)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(e); } }, } } // We ran out of attempts to create the file, return an error. - let eexist = this.eval_libc("EEXIST"); - this.set_last_error(eexist)?; - interp_ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EEXIST")) } } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index cafc7161d2624..539231743ae32 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -266,8 +266,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Throw EINVAL if epfd and fd have the same value. if epfd_value == fd { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } // Check if epfd is a valid epoll file descriptor. @@ -332,15 +331,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Check the existence of fd in the interest list. if op == epoll_ctl_add { if interest_list.contains_key(&epoll_key) { - let eexist = this.eval_libc("EEXIST"); - this.set_last_error(eexist)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EEXIST")); } } else { if !interest_list.contains_key(&epoll_key) { - let enoent = this.eval_libc("ENOENT"); - this.set_last_error(enoent)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("ENOENT")); } } @@ -374,9 +369,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Remove epoll_event_interest from interest_list. let Some(epoll_interest) = interest_list.remove(&epoll_key) else { - let enoent = this.eval_libc("ENOENT"); - this.set_last_error(enoent)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("ENOENT")); }; // All related Weak will fail to upgrade after the drop. drop(epoll_interest); diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 9273748ef3bfd..bf95cf27b83fb 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -134,13 +134,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // as a dealloc. #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if addr.addr().bytes() % this.machine.page_size != 0 { - this.set_last_error(this.eval_libc("EINVAL"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { - this.set_last_error(this.eval_libc("EINVAL"))?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; if length > this.target_usize_max() { this.set_last_error(this.eval_libc("EINVAL"))?; From c2f43ba09cdc8b79d540373c3c5847480affd5ee Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Fri, 20 Sep 2024 18:20:27 +0300 Subject: [PATCH 09/27] Added support for prctl handling thread names --- src/tools/miri/ci/ci.sh | 2 +- .../src/shims/unix/android/foreign_items.rs | 4 + src/tools/miri/src/shims/unix/android/mod.rs | 1 + .../miri/src/shims/unix/android/thread.rs | 57 ++++++++++++++ .../src/shims/unix/freebsd/foreign_items.rs | 1 + .../src/shims/unix/linux/foreign_items.rs | 1 + .../src/shims/unix/macos/foreign_items.rs | 1 + .../src/shims/unix/solarish/foreign_items.rs | 1 + src/tools/miri/src/shims/unix/thread.rs | 12 ++- src/tools/miri/test_dependencies/Cargo.lock | 6 +- .../libc/prctl-get-name-buffer-too-small.rs | 10 +++ .../prctl-get-name-buffer-too-small.stderr | 21 ++++++ .../tests/pass-dep/libc/prctl-threadname.rs | 74 +++++++++++++++++++ .../tests/pass-dep/libc/pthread-threadname.rs | 1 + 14 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 src/tools/miri/src/shims/unix/android/thread.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr create mode 100644 src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index ad1b2f4d0c3d1..4e7cbc50ca03b 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -154,7 +154,7 @@ case $HOST_TARGET in TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe - TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap pthread --skip threadname + TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap threadname pthread TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 583a1f6500963..b6f04951fc7e7 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -1,6 +1,7 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; +use crate::shims::unix::android::thread::prctl; use crate::*; pub fn is_dyn_sym(_name: &str) -> bool { @@ -25,6 +26,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } + // Threading + "prctl" => prctl(this, link_name, abi, args, dest)?, + _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) diff --git a/src/tools/miri/src/shims/unix/android/mod.rs b/src/tools/miri/src/shims/unix/android/mod.rs index 09c6507b24f84..1f2a74bac5935 100644 --- a/src/tools/miri/src/shims/unix/android/mod.rs +++ b/src/tools/miri/src/shims/unix/android/mod.rs @@ -1 +1,2 @@ pub mod foreign_items; +pub mod thread; diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs new file mode 100644 index 0000000000000..6f5f0f74a2295 --- /dev/null +++ b/src/tools/miri/src/shims/unix/android/thread.rs @@ -0,0 +1,57 @@ +use rustc_span::Symbol; +use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; + +use crate::helpers::check_min_arg_count; +use crate::shims::unix::thread::EvalContextExt as _; +use crate::*; + +const TASK_COMM_LEN: usize = 16; + +pub fn prctl<'tcx>( + this: &mut MiriInterpCx<'tcx>, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx> { + // We do not use `check_shim` here because `prctl` is variadic. The argument + // count is checked bellow. + this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?; + + // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch. + let pr_set_name = 15; + let pr_get_name = 16; + + let [op] = check_min_arg_count("prctl", args)?; + let res = match this.read_scalar(op)?.to_i32()? { + op if op == pr_set_name => { + let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?; + let name = this.read_scalar(name)?; + let thread = this.pthread_self()?; + // The Linux kernel silently truncates long names. + // https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html + let res = + this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?; + assert!(res); + Scalar::from_u32(0) + } + op if op == pr_get_name => { + let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?; + let name = this.read_scalar(name)?; + let thread = this.pthread_self()?; + let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this); + this.check_ptr_access( + name.to_pointer(this)?, + Size::from_bytes(TASK_COMM_LEN), + CheckInAllocMsg::MemoryAccessTest, + )?; + let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?; + assert!(res); + Scalar::from_u32(0) + } + op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op), + }; + this.write_scalar(res, dest)?; + interp_ok(()) +} diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 5204e57705a73..71953aca98905 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -29,6 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_scalar(thread)?, this.read_scalar(name)?, max_len, + /* truncate */ false, )?; } "pthread_get_name_np" => { diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index e73bde1ddb660..6616a9845c09b 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -84,6 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_scalar(thread)?, this.read_scalar(name)?, TASK_COMM_LEN, + /* truncate */ false, )?; let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }; this.write_scalar(res, dest)?; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index b199992245cd4..cd07bc9e013fd 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -181,6 +181,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { thread, this.read_scalar(name)?, this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(), + /* truncate */ false, )? { Scalar::from_u32(0) } else { diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index 7f3d0f07bdce0..c9c1b01b8b1c7 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -30,6 +30,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_scalar(thread)?, this.read_scalar(name)?, max_len, + /* truncate */ false, )?; let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }; this.write_scalar(res, dest)?; diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 7f97afc8e4b12..51256d800a432 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -64,23 +64,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Set the name of the specified thread. If the name including the null terminator - /// is longer than `name_max_len`, then `false` is returned. + /// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name + /// is used as the thread name, otherwise `false` is returned. fn pthread_setname_np( &mut self, thread: Scalar, name: Scalar, name_max_len: usize, + truncate: bool, ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; let thread = ThreadId::try_from(thread).unwrap(); let name = name.to_pointer(this)?; - let name = this.read_c_str(name)?.to_owned(); + let mut name = this.read_c_str(name)?.to_owned(); // Comparing with `>=` to account for null terminator. if name.len() >= name_max_len { - return interp_ok(false); + if truncate { + name.truncate(name_max_len.saturating_sub(1)); + } else { + return interp_ok(false); + } } this.set_thread_name(thread, name); diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index 64bfc84ef2006..0a5e9f62dd9fb 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "linux-raw-sys" diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs new file mode 100644 index 0000000000000..4b731866aca1d --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs @@ -0,0 +1,10 @@ +//! Ensure we report UB when the buffer is smaller than 16 bytes (even if the thread +//! name would fit in the smaller buffer). +//@only-target: android # Miri supports prctl for Android only + +fn main() { + let mut buf = vec![0u8; 15]; + unsafe { + libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::()); //~ ERROR: memory access failed: expected a pointer to 16 bytes of memory, but got alloc952 which is only 15 bytes from the end of the allocation + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr new file mode 100644 index 0000000000000..275a38e593c8f --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr @@ -0,0 +1,21 @@ +error: Undefined Behavior: memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation + --> tests/fail-dep/libc/prctl-threadname.rs:LL:CC + | +LL | libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation + | + = 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 +help: ALLOC was allocated here: + --> tests/fail-dep/libc/prctl-threadname.rs:LL:CC + | +LL | let mut buf = vec![0u8; 15]; + | ^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail-dep/libc/prctl-threadname.rs:LL:CC + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs b/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs new file mode 100644 index 0000000000000..87ae3753fea70 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs @@ -0,0 +1,74 @@ +//@only-target: android # Miri supports prctl for Android only +use std::ffi::{CStr, CString}; +use std::thread; + +// The Linux kernel all names 16 bytes long including the null terminator. +const MAX_THREAD_NAME_LEN: usize = 16; + +fn main() { + // The short name should be shorter than 16 bytes which POSIX promises + // for thread names. The length includes a null terminator. + let short_name = "test_named".to_owned(); + let long_name = std::iter::once("test_named_thread_truncation") + .chain(std::iter::repeat(" yada").take(100)) + .collect::(); + + fn set_thread_name(name: &CStr) -> i32 { + unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr().cast::()) } + } + + fn get_thread_name(name: &mut [u8]) -> i32 { + assert!(name.len() >= MAX_THREAD_NAME_LEN); + unsafe { libc::prctl(libc::PR_GET_NAME, name.as_mut_ptr().cast::()) } + } + + // Set name via Rust API, get it via prctl. + let long_name2 = long_name.clone(); + thread::Builder::new() + .name(long_name.clone()) + .spawn(move || { + let mut buf = vec![0u8; MAX_THREAD_NAME_LEN]; + assert_eq!(get_thread_name(&mut buf), 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)]; + assert_eq!(cstr.to_bytes(), truncated_name.as_bytes()); + }) + .unwrap() + .join() + .unwrap(); + + // Set name via prctl and get it again (short name). + thread::Builder::new() + .spawn(move || { + // Set short thread name. + let cstr = CString::new(short_name.clone()).unwrap(); + assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit + assert_eq!(set_thread_name(&cstr), 0); + + let mut buf = vec![0u8; MAX_THREAD_NAME_LEN]; + assert_eq!(get_thread_name(&mut buf), 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(cstr.to_bytes(), short_name.as_bytes()); + }) + .unwrap() + .join() + .unwrap(); + + // Set name via prctl and get it again (long name). + thread::Builder::new() + .spawn(move || { + // Set full thread name. + let cstr = CString::new(long_name.clone()).unwrap(); + assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); + // Names are truncated by the Linux kernel. + assert_eq!(set_thread_name(&cstr), 0); + + let mut buf = vec![0u8; MAX_THREAD_NAME_LEN]; + assert_eq!(get_thread_name(&mut buf), 0); + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert_eq!(cstr.to_bytes(), &long_name.as_bytes()[..(MAX_THREAD_NAME_LEN - 1)]); + }) + .unwrap() + .join() + .unwrap(); +} diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index 757d9e209344a..0e5b501bbccdc 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -1,4 +1,5 @@ //@ignore-target: windows # No pthreads on Windows +//@ignore-target: android # No pthread_{get,set}_name on Android use std::ffi::{CStr, CString}; use std::thread; From e51eded01a866b06edac644bba1ff10a73e069c6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 21 Oct 2024 15:18:01 +0100 Subject: [PATCH 10/27] epoll_ctl: throw unsupported error on unsupported opcode --- src/tools/miri/src/shims/unix/linux/epoll.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index cafc7161d2624..574eed549da6d 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -256,14 +256,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let epollhup = this.eval_libc_u32("EPOLLHUP"); let epollerr = this.eval_libc_u32("EPOLLERR"); - // Fail on unsupported operations. - if op & epoll_ctl_add != epoll_ctl_add - && op & epoll_ctl_mod != epoll_ctl_mod - && op & epoll_ctl_del != epoll_ctl_del - { - throw_unsup_format!("epoll_ctl: encountered unknown unsupported operation {:#x}", op); - } - // Throw EINVAL if epfd and fd have the same value. if epfd_value == fd { this.set_last_error(LibcError("EINVAL"))?; @@ -368,7 +360,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Notification will be returned for current epfd if there is event in the file // descriptor we registered. check_and_update_one_event_interest(&fd_ref, interest, id, this)?; - return interp_ok(Scalar::from_i32(0)); + interp_ok(Scalar::from_i32(0)) } else if op == epoll_ctl_del { let epoll_key = (id, fd); @@ -394,9 +386,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .unwrap() .retain(|event| event.upgrade().is_some()); - return interp_ok(Scalar::from_i32(0)); + interp_ok(Scalar::from_i32(0)) + } else { + throw_unsup_format!("unsupported epoll_ctl operation: {op}"); } - interp_ok(Scalar::from_i32(-1)) } /// The `epoll_wait()` system call waits for events on the `Epoll` From 499820a494fcd739a7c3d3f2ee2f8e4c0aff7a03 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 22 Oct 2024 04:59:47 +0000 Subject: [PATCH 11/27] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 778061e13105e..133edd3191d5b 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -54791efd8235805dcfbdad3b8788e08f2142c50b +814df6e50eaf89b90793e7d9618bb60f1f18377a From cd8c1234ce50de8add477b47fa04aa44a36fc2e6 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 22 Oct 2024 05:08:45 +0000 Subject: [PATCH 12/27] fmt --- src/tools/miri/src/intrinsics/simd.rs | 11 ++++------- src/tools/miri/tests/fail/provenance/ptr_invalid.rs | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 0799b93dbb0e0..f15b83a054fdb 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -1,7 +1,8 @@ use either::Either; use rustc_apfloat::{Float, Round}; +use rustc_middle::ty::FloatTy; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::{mir, ty, ty::FloatTy}; +use rustc_middle::{mir, ty}; use rustc_span::{Symbol, sym}; use rustc_target::abi::{Endian, HasDataLayout}; @@ -630,12 +631,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (right, right_len) = this.project_to_simd(right)?; let (dest, dest_len) = this.project_to_simd(dest)?; - let index = generic_args[2] - .expect_const() - .try_to_valtree() - .unwrap() - .0 - .unwrap_branch(); + let index = + generic_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch(); let index_len = index.len(); assert_eq!(left_len, right_len); diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs index d4479f32e56fb..c91f4ec158fee 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs +++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs @@ -1,4 +1,3 @@ - // Ensure that a `ptr::without_provenance` ptr is truly invalid. fn main() { let x = 42; From 615822d0da576d2c7b263fa17a2af8291a26c5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 21 Oct 2024 13:24:53 +0200 Subject: [PATCH 13/27] Switch CI to merge queues --- src/tools/miri/.github/workflows/ci.yml | 39 ++++++++++--------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 7d8c7203b320b..dd24f3f54d97d 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -1,11 +1,7 @@ name: CI on: - push: - # Run in PRs and for bors, but not on master. - branches: - - 'auto' - - 'try' + merge_group: pull_request: branches: - 'master' @@ -68,27 +64,24 @@ jobs: - name: rustdoc run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items - # These jobs doesn't actually test anything, but they're only used to tell - # bors the build completed, as there is no practical way to detect when a - # workflow is successful listening to webhooks only. - # - # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - end-success: - name: bors build finished - runs-on: ubuntu-latest + conclusion: needs: [build, style] - if: github.event.pusher.name == 'bors' && success() - steps: - - name: mark the job as a success - run: exit 0 - end-failure: - name: bors build finished + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} runs-on: ubuntu-latest - needs: [build, style] - if: github.event.pusher.name == 'bors' && (failure() || cancelled()) steps: - - name: mark the job as a failure - run: exit 1 + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' cron-fail-notify: name: cronjob failure notification From 0b52ec3d7e9135562569c5ee51ad1ad475ca1797 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 22 Oct 2024 19:57:58 +0000 Subject: [PATCH 14/27] Simplify confusing ResolvedArg constructors --- .../src/collect/resolve_bound_vars.rs | 103 ++++++++---------- 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index cb7f0901c7e4a..c5d64ddb99b74 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -33,18 +33,12 @@ use crate::errors; #[extension(trait RegionExt)] impl ResolvedArg { - fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) { - debug!("ResolvedArg::early: def_id={:?}", param.def_id); - (param.def_id, ResolvedArg::EarlyBound(param.def_id)) + fn early(param: &GenericParam<'_>) -> ResolvedArg { + ResolvedArg::EarlyBound(param.def_id) } - fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) { - let depth = ty::INNERMOST; - debug!( - "ResolvedArg::late: idx={:?}, param={:?} depth={:?} def_id={:?}", - idx, param, depth, param.def_id, - ); - (param.def_id, ResolvedArg::LateBound(depth, idx, param.def_id)) + fn late(idx: u32, param: &GenericParam<'_>) -> ResolvedArg { + ResolvedArg::LateBound(ty::INNERMOST, idx, param.def_id) } fn id(&self) -> Option { @@ -282,24 +276,18 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou fn late_arg_as_bound_arg<'tcx>( tcx: TyCtxt<'tcx>, - arg: &ResolvedArg, param: &GenericParam<'tcx>, ) -> ty::BoundVariableKind { - match arg { - ResolvedArg::LateBound(_, _, def_id) => { - let def_id = def_id.to_def_id(); - let name = tcx.item_name(def_id); - match param.kind { - GenericParamKind::Lifetime { .. } => { - ty::BoundVariableKind::Region(ty::BrNamed(def_id, name)) - } - GenericParamKind::Type { .. } => { - ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name)) - } - GenericParamKind::Const { .. } => ty::BoundVariableKind::Const, - } + let def_id = param.def_id.to_def_id(); + let name = tcx.item_name(def_id); + match param.kind { + GenericParamKind::Lifetime { .. } => { + ty::BoundVariableKind::Region(ty::BrNamed(def_id, name)) } - _ => bug!("{:?} is not a late argument", arg), + GenericParamKind::Type { .. } => { + ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name)) + } + GenericParamKind::Const { .. } => ty::BoundVariableKind::Const, } } @@ -360,10 +348,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let mut bound_vars: FxIndexMap = FxIndexMap::default(); let binders_iter = trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| { - let pair = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param); - let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); - bound_vars.insert(pair.0, pair.1); - r + let arg = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param); + bound_vars.insert(param.def_id, arg); + late_arg_as_bound_arg(self.tcx, param) }); binders.extend(binders_iter); @@ -458,9 +445,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { .iter() .enumerate() .map(|(late_bound_idx, param)| { - let pair = ResolvedArg::late(late_bound_idx as u32, param); - let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); - (pair, r) + ( + (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)), + late_arg_as_bound_arg(self.tcx, param), + ) }) .unzip(); @@ -492,8 +480,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let mut bound_vars = FxIndexMap::default(); debug!(?opaque.generics.params); for param in opaque.generics.params { - let (def_id, reg) = ResolvedArg::early(param); - bound_vars.insert(def_id, reg); + let arg = ResolvedArg::early(param); + bound_vars.insert(param.def_id, arg); } let hir_id = self.tcx.local_def_id_to_hir_id(opaque.def_id); @@ -618,9 +606,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { .iter() .enumerate() .map(|(late_bound_idx, param)| { - let pair = ResolvedArg::late(late_bound_idx as u32, param); - let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); - (pair, r) + ( + (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)), + late_arg_as_bound_arg(self.tcx, param), + ) }) .unzip(); @@ -870,9 +859,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { .iter() .enumerate() .map(|(late_bound_idx, param)| { - let pair = ResolvedArg::late(late_bound_idx as u32, param); - let r = late_arg_as_bound_arg(self.tcx, &pair.1, param); - (pair, r) + ( + (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)), + late_arg_as_bound_arg(self.tcx, param), + ) }) .unzip(); @@ -1052,19 +1042,21 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let bound_vars: FxIndexMap = generics .params .iter() - .map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - if self.tcx.is_late_bound(param.hir_id) { - let late_bound_idx = named_late_bound_vars; - named_late_bound_vars += 1; - ResolvedArg::late(late_bound_idx, param) - } else { + .map(|param| { + (param.def_id, match param.kind { + GenericParamKind::Lifetime { .. } => { + if self.tcx.is_late_bound(param.hir_id) { + let late_bound_idx = named_late_bound_vars; + named_late_bound_vars += 1; + ResolvedArg::late(late_bound_idx, param) + } else { + ResolvedArg::early(param) + } + } + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { ResolvedArg::early(param) } - } - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - ResolvedArg::early(param) - } + }) }) .collect(); @@ -1075,11 +1067,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { matches!(param.kind, GenericParamKind::Lifetime { .. }) && self.tcx.is_late_bound(param.hir_id) }) - .enumerate() - .map(|(late_bound_idx, param)| { - let pair = ResolvedArg::late(late_bound_idx as u32, param); - late_arg_as_bound_arg(self.tcx, &pair.1, param) - }) + .map(|param| late_arg_as_bound_arg(self.tcx, param)) .collect(); self.record_late_bound_vars(hir_id, binders); let scope = Scope::Binder { @@ -1096,7 +1084,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { where F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>), { - let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); + let bound_vars = + generics.params.iter().map(|param| (param.def_id, ResolvedArg::early(param))).collect(); self.record_late_bound_vars(hir_id, vec![]); let scope = Scope::Binder { hir_id, From 1b7a91ed719ebc1f0615365a551f55ded1e9beed Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 22 Oct 2024 19:58:15 +0000 Subject: [PATCH 15/27] Deduplicate handling of early-bound params in RTN --- .../src/collect/resolve_bound_vars.rs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index c5d64ddb99b74..0e9623ecb73be 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -291,6 +291,21 @@ fn late_arg_as_bound_arg<'tcx>( } } +/// Turn a [`ty::GenericParamDef`] into a bound arg. Generally, this should only +/// be used when turning early-bound vars into late-bound vars when lowering +/// return type notation. +fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind { + match param.kind { + ty::GenericParamDefKind::Lifetime => { + ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(param.def_id, param.name)) + } + ty::GenericParamDefKind::Type { .. } => { + ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name)) + } + ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const, + } +} + impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. fn poly_trait_ref_binder_info(&mut self) -> (Vec, BinderScopeType) { @@ -1628,17 +1643,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { constraint.ident, ty::AssocKind::Fn, ) { - bound_vars.extend(self.tcx.generics_of(assoc_fn.def_id).own_params.iter().map( - |param| match param.kind { - ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region( - ty::BoundRegionKind::BrNamed(param.def_id, param.name), - ), - ty::GenericParamDefKind::Type { .. } => ty::BoundVariableKind::Ty( - ty::BoundTyKind::Param(param.def_id, param.name), - ), - ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const, - }, - )); + bound_vars.extend( + self.tcx + .generics_of(assoc_fn.def_id) + .own_params + .iter() + .map(|param| generic_param_def_as_bound_arg(param)), + ); bound_vars.extend( self.tcx.fn_sig(assoc_fn.def_id).instantiate_identity().bound_vars(), ); @@ -1957,17 +1968,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // Append the early-bound vars on the function, and then the late-bound ones. // We actually turn type parameters into higher-ranked types here, but we // deny them later in HIR lowering. - bound_vars.extend(self.tcx.generics_of(item_def_id).own_params.iter().map(|param| { - match param.kind { - ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region( - ty::BoundRegionKind::BrNamed(param.def_id, param.name), - ), - ty::GenericParamDefKind::Type { .. } => { - ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name)) - } - ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const, - } - })); + bound_vars.extend( + self.tcx + .generics_of(item_def_id) + .own_params + .iter() + .map(|param| generic_param_def_as_bound_arg(param)), + ); bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars()); // SUBTLE: Stash the old bound vars onto the *item segment* before appending From c1004ff8618dd3382bcade201e28529df4385c3c Mon Sep 17 00:00:00 2001 From: Noah Bright Date: Mon, 21 Oct 2024 14:06:29 -0400 Subject: [PATCH 16/27] Clear more `eval_libc` errors from unix shims --- src/tools/miri/src/shims/foreign_items.rs | 9 +++---- src/tools/miri/src/shims/time.rs | 12 +++------ .../miri/src/shims/unix/foreign_items.rs | 25 ++++++------------- src/tools/miri/src/shims/unix/fs.rs | 17 ++++++------- src/tools/miri/src/shims/unix/linux/mem.rs | 4 +-- src/tools/miri/src/shims/unix/linux/sync.rs | 4 +-- src/tools/miri/src/shims/unix/mem.rs | 10 ++++---- 7 files changed, 28 insertions(+), 53 deletions(-) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index f6f91e589697a..12f8facfd023e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -447,8 +447,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { // If this does not fit in an isize, return null and, on Unix, set errno. if this.target_os_is_unix() { - let einval = this.eval_libc("ENOMEM"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("ENOMEM"))?; } this.write_null(dest)?; } @@ -464,8 +463,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { // On size overflow, return null and, on Unix, set errno. if this.target_os_is_unix() { - let einval = this.eval_libc("ENOMEM"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("ENOMEM"))?; } this.write_null(dest)?; } @@ -486,8 +484,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { // If this does not fit in an isize, return null and, on Unix, set errno. if this.target_os_is_unix() { - let einval = this.eval_libc("ENOMEM"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("ENOMEM"))?; } this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 12c7679608ded..6436823b0fdfe 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -81,9 +81,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if relative_clocks.contains(&clk_id) { this.machine.clock.now().duration_since(this.machine.clock.epoch()) } else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; let tv_sec = duration.as_secs(); @@ -109,9 +107,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Using tz is obsolete and should always be null let tz = this.read_pointer(tz_op)?; if !this.ptr_is_null(tz)? { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } let duration = system_time_to_duration(&SystemTime::now())?; @@ -323,9 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let duration = match this.read_timespec(&req)? { Some(duration) => duration, None => { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - return interp_ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(LibcError("EINVAL")); } }; diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 7ba9898192018..355c93c444ccf 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -362,8 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) { None => { - let enmem = this.eval_libc("ENOMEM"); - this.set_last_error(enmem)?; + this.set_last_error(LibcError("ENOMEM"))?; this.write_null(dest)?; } Some(len) => { @@ -653,13 +652,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let chunk_size = CpuAffinityMask::chunk_size(this); if this.ptr_is_null(mask)? { - let efault = this.eval_libc("EFAULT"); - this.set_last_error(efault)?; - this.write_int(-1, dest)?; + this.set_last_error_and_return(LibcError("EFAULT"), dest)?; } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 { // we only copy whole chunks of size_of::() - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) { let cpuset = cpuset.clone(); // we only copy whole chunks of size_of::() @@ -668,9 +664,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } else { // The thread whose ID is pid could not be found - let esrch = this.eval_libc("ESRCH"); - this.set_last_error(esrch)?; - this.write_int(-1, dest)?; + this.set_last_error_and_return(LibcError("ESRCH"), dest)?; } } "sched_setaffinity" => { @@ -695,9 +689,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; if this.ptr_is_null(mask)? { - let efault = this.eval_libc("EFAULT"); - this.set_last_error(efault)?; - this.write_int(-1, dest)?; + this.set_last_error_and_return(LibcError("EFAULT"), dest)?; } else { // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`. // Any unspecified bytes are treated as zero here (none of the CPUs are configured). @@ -713,8 +705,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } None => { // The intersection between the mask and the available CPUs was empty. - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; } } } @@ -770,9 +761,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html // Solaris/Illumos: https://illumos.org/man/3C/getentropy if bufsize > 256 { - let err = this.eval_libc("EIO"); - this.set_last_error(err)?; - this.write_int(-1, dest)?; + this.set_last_error_and_return(LibcError("EIO"), dest)?; } else { this.gen_random(buf, bufsize)?; this.write_null(dest)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index fcb824ab35b38..8c502c578f6ba 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -787,13 +787,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD") { // since `path` is provided, either absolute or // relative to CWD, `EACCES` is the most relevant. - this.eval_libc("EACCES") + LibcError("EACCES") } else { // `dirfd` is set to target file, and `path` is empty // (or we would have hit the `throw_unsup_format` // above). `EACCES` would violate the spec. assert!(empty_path_flag); - this.eval_libc("EBADF") + LibcError("EBADF") }; return this.set_last_error_and_return_i32(ecode); } @@ -1031,8 +1031,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir`", reject_with)?; - let eacc = this.eval_libc("EBADF"); - this.set_last_error(eacc)?; + this.set_last_error(LibcError("EBADF"))?; return interp_ok(Scalar::null_ptr(this)); } @@ -1449,11 +1448,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if fd.is_tty(this.machine.communicate()) { return interp_ok(Scalar::from_i32(1)); } else { - this.eval_libc("ENOTTY") + LibcError("ENOTTY") } } else { // FD does not exist - this.eval_libc("EBADF") + LibcError("EBADF") }; this.set_last_error(error)?; interp_ok(Scalar::from_i32(0)) @@ -1473,8 +1472,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`realpath`", reject_with)?; - let eacc = this.eval_libc("EACCES"); - this.set_last_error(eacc)?; + this.set_last_error(LibcError("EACCES"))?; return interp_ok(Scalar::from_target_usize(0, this)); } @@ -1504,8 +1502,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Note that we do not explicitly handle `FILENAME_MAX` // (different from `PATH_MAX` above) as it is Linux-specific and // seems like a bit of a mess anyway: . - let enametoolong = this.eval_libc("ENAMETOOLONG"); - this.set_last_error(enametoolong)?; + this.set_last_error(LibcError("ENAMETOOLONG"))?; return interp_ok(Scalar::from_target_usize(0, this)); } processed_ptr diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 4f2e17d50c8b4..d5f9669b36095 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -24,7 +24,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // old_address must be a multiple of the page size #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } @@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index 941011bfac640..8640ce2e2cd6b 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -158,9 +158,7 @@ pub fn futex<'tcx>( } else { // The futex value doesn't match the expected value, so we return failure // right away without sleeping: -1 and errno set to EAGAIN. - let eagain = this.eval_libc("EAGAIN"); - this.set_last_error(eagain)?; - this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; + this.set_last_error_and_return(LibcError("EAGAIN"), dest)?; } } // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val) diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index bf95cf27b83fb..9371edfc83db2 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -57,11 +57,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } if length == 0 { - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } @@ -103,11 +103,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let align = this.machine.page_align(); let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); }; if map_length > this.target_usize_max() { - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } @@ -141,7 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; if length > this.target_usize_max() { - this.set_last_error(this.eval_libc("EINVAL"))?; + this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); } From e5d32b67578083f59db8e8ea397db9ac7b9597ff Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 25 Oct 2024 15:23:23 +0200 Subject: [PATCH 17/27] CI workflow: tweak conclusion job --- src/tools/miri/.github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 400a012deed66..f3975ec73892a 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: # The `style` job only runs on Linux; this makes sure the Windows-host-specific # code is also covered by clippy. - name: Check clippy - if: matrix.os == 'windows-latest' + if: ${{ matrix.os == 'windows-latest' }} run: ./miri clippy -- -D warnings - name: Test Miri @@ -58,14 +58,15 @@ jobs: - name: rustdoc run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + # And they should be added below in `cron-fail-notify` as well. conclusion: needs: [build, style] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run # when the workflow is canceled manually. - # - # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! if: ${{ !cancelled() }} runs-on: ubuntu-latest steps: @@ -86,7 +87,7 @@ jobs: # ... and create a PR. pull-requests: write needs: [build, style] - if: github.event_name == 'schedule' && failure() + if: ${{ github.event_name == 'schedule' && failure() }} steps: # Send a Zulip notification - name: Install zulip-send From 82b041e356e5ca26ff3161e3ea068dc19b817f54 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 25 Oct 2024 16:05:58 +0200 Subject: [PATCH 18/27] get rid of fd_not_found; improve error handling in FileMetadata --- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/shims/unix/fd.rs | 36 +++------ src/tools/miri/src/shims/unix/fs.rs | 79 ++++++++++---------- src/tools/miri/src/shims/unix/linux/epoll.rs | 8 +- 4 files changed, 54 insertions(+), 71 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 660f2e493bc0f..938d1ca319e0f 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -147,7 +147,7 @@ pub use crate::range_map::RangeMap; pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; -pub use crate::shims::io_error::{EvalContextExt as _, LibcError}; +pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError}; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index c21982ee37932..f3db56695fc2c 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -423,7 +423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(old_fd_num) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; interp_ok(Scalar::from_i32(this.machine.fds.insert(fd))) } @@ -432,7 +432,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(old_fd_num) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; if new_fd_num != old_fd_num { // Close new_fd if it is previously opened. @@ -448,7 +448,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(fd_num) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // We need to check that there aren't unsupported options in `op`. @@ -498,11 +498,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` // always sets this flag when opening a file. However we still need to check that the // file itself is open. - interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) { - this.eval_libc_i32("FD_CLOEXEC") + if !this.machine.fds.is_fd_num(fd_num) { + this.set_last_error_and_return_i32(LibcError("EBADF")) } else { - this.fd_not_found()? - })) + interp_ok(this.eval_libc("FD_CLOEXEC")) + } } cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => { // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part @@ -521,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(fd) = this.machine.fds.get(fd_num) { interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))) } else { - interp_ok(Scalar::from_i32(this.fd_not_found()?)) + this.set_last_error_and_return_i32(LibcError("EBADF")) } } cmd if this.tcx.sess.target.os == "macos" @@ -547,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd_num = this.read_scalar(fd_op)?.to_i32()?; let Some(fd) = this.machine.fds.remove(fd_num) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; let result = fd.close(this.machine.communicate(), this)?; // return `0` if close is successful @@ -555,16 +555,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } - /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets - /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses - /// `T: From` instead of `i32` directly because some fs functions return different integer - /// types (like `read`, that returns an `i64`). - fn fd_not_found>(&mut self) -> InterpResult<'tcx, T> { - let this = self.eval_context_mut(); - this.set_last_error(LibcError("EBADF"))?; - interp_ok((-1).into()) - } - /// Read data from `fd` into buffer specified by `buf` and `count`. /// /// If `offset` is `None`, reads data from current cursor position associated with `fd` @@ -598,9 +588,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We temporarily dup the FD to be able to retain mutable access to `this`. let Some(fd) = this.machine.fds.get(fd_num) else { trace!("read: FD not found"); - let res: i32 = this.fd_not_found()?; - this.write_int(res, dest)?; - return interp_ok(()); + return this.set_last_error_and_return(LibcError("EBADF"), dest); }; trace!("read: FD mapped to {fd:?}"); @@ -645,9 +633,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We temporarily dup the FD to be able to retain mutable access to `this`. let Some(fd) = this.machine.fds.get(fd_num) else { - let res: i32 = this.fd_not_found()?; - this.write_int(res, dest)?; - return interp_ok(()); + return this.set_last_error_and_return(LibcError("EBADF"), dest); }; match offset { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 8c502c578f6ba..4a94d3807a637 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -595,7 +595,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let communicate = this.machine.communicate(); let Some(fd) = this.machine.fds.get(fd_num) else { - return interp_ok(Scalar::from_i64(this.fd_not_found()?)); + return this.set_last_error_and_return_i64(LibcError("EBADF")); }; let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap()); drop(fd); @@ -671,8 +671,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `stat` always follows symlinks. let metadata = match FileMetadata::from_path(this, &path, true)? { - Some(metadata) => metadata, - None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno + Ok(metadata) => metadata, + Err(err) => return this.set_last_error_and_return_i32(err), }; interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) @@ -700,8 +700,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let metadata = match FileMetadata::from_path(this, &path, false)? { - Some(metadata) => metadata, - None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno + Ok(metadata) => metadata, + Err(err) => return this.set_last_error_and_return_i32(err), }; interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) @@ -724,12 +724,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fstat`", reject_with)?; // Set error code as "EBADF" (bad fd) - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); } let metadata = match FileMetadata::from_fd_num(this, fd)? { - Some(metadata) => metadata, - None => return interp_ok(Scalar::from_i32(-1)), + Ok(metadata) => metadata, + Err(err) => return this.set_last_error_and_return_i32(err), }; interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?)) } @@ -816,8 +816,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { FileMetadata::from_path(this, &path, follow_symlink)? }; let metadata = match metadata { - Some(metadata) => metadata, - None => return interp_ok(Scalar::from_i32(-1)), + Ok(metadata) => metadata, + Err(err) => return this.set_last_error_and_return_i32(err), }; // The `mode` field specifies the type of the file and the permissions over the file for @@ -1131,7 +1131,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir_r`", reject_with)?; // Set error code as "EBADF" (bad fd) - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); } let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { @@ -1242,20 +1242,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dirp = this.read_target_usize(dirp_op)?; // Reject if isolation is enabled. - interp_ok(Scalar::from_i32( - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`closedir`", reject_with)?; - this.fd_not_found()? - } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { - if let Some(entry) = open_dir.entry { - this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; - } - drop(open_dir); - 0 - } else { - this.fd_not_found()? - }, - )) + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`closedir`", reject_with)?; + return this.set_last_error_and_return_i32(LibcError("EBADF")); + } + + let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) else { + return this.set_last_error_and_return_i32(LibcError("EBADF")); + }; + if let Some(entry) = open_dir.entry { + this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; + } + drop(open_dir); + + interp_ok(Scalar::from_i32(0)) } fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> { @@ -1265,11 +1265,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`ftruncate64`", reject_with)?; // Set error code as "EBADF" (bad fd) - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); } let Some(fd) = this.machine.fds.get(fd_num) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // FIXME: Support ftruncate64 for all FDs @@ -1308,7 +1308,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fsync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); } self.ffullsync_fd(fd) @@ -1317,7 +1317,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let Some(fd) = this.machine.fds.get(fd_num) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1337,11 +1337,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fdatasync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); } let Some(fd) = this.machine.fds.get(fd) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1380,11 +1380,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`sync_file_range`", reject_with)?; // Set error code as "EBADF" (bad fd) - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); } let Some(fd) = this.machine.fds.get(fd) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; // Only regular files support synchronization. let FileHandle { file, writable } = fd.downcast::().ok_or_else(|| { @@ -1667,7 +1667,7 @@ impl FileMetadata { ecx: &mut MiriInterpCx<'tcx>, path: &Path, follow_symlink: bool, - ) -> InterpResult<'tcx, Option> { + ) -> InterpResult<'tcx, Result> { let metadata = if follow_symlink { std::fs::metadata(path) } else { std::fs::symlink_metadata(path) }; @@ -1677,9 +1677,9 @@ impl FileMetadata { fn from_fd_num<'tcx>( ecx: &mut MiriInterpCx<'tcx>, fd_num: i32, - ) -> InterpResult<'tcx, Option> { + ) -> InterpResult<'tcx, Result> { let Some(fd) = ecx.machine.fds.get(fd_num) else { - return ecx.fd_not_found().map(|_: i32| None); + return interp_ok(Err(LibcError("EBADF"))); }; let file = &fd @@ -1699,12 +1699,11 @@ impl FileMetadata { fn from_meta<'tcx>( ecx: &mut MiriInterpCx<'tcx>, metadata: Result, - ) -> InterpResult<'tcx, Option> { + ) -> InterpResult<'tcx, Result> { let metadata = match metadata { Ok(metadata) => metadata, Err(e) => { - ecx.set_last_error(e)?; - return interp_ok(None); + return interp_ok(Err(e.into())); } }; @@ -1727,6 +1726,6 @@ impl FileMetadata { let modified = extract_sec_and_nsec(metadata.modified())?; // FIXME: Provide more fields using platform specific methods. - interp_ok(Some(FileMetadata { mode, size, created, accessed, modified })) + interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified })) } } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index d6788efbba4c7..f743a298dad03 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -263,7 +263,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Check if epfd is a valid epoll file descriptor. let Some(epfd) = this.machine.fds.get(epfd_value) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; let epoll_file_description = epfd .downcast::() @@ -273,7 +273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ready_list = &epoll_file_description.ready_list; let Some(fd_ref) = this.machine.fds.get(fd) else { - return interp_ok(Scalar::from_i32(this.fd_not_found()?)); + return this.set_last_error_and_return_i32(LibcError("EBADF")); }; let id = fd_ref.get_id(); @@ -446,9 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; let Some(epfd) = this.machine.fds.get(epfd_value) else { - let result_value: i32 = this.fd_not_found()?; - this.write_int(result_value, dest)?; - return interp_ok(()); + return this.set_last_error_and_return(LibcError("EBADF"), dest); }; // Create a weak ref of epfd and pass it to callback so we will make sure that epfd // is not close after the thread unblocks. From 2350ba72764cd0744c6484069621869ec33f94f3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 25 Oct 2024 16:10:48 +0200 Subject: [PATCH 19/27] some more opportunities for set_last_error_and_return --- src/tools/miri/src/shims/unix/linux/epoll.rs | 4 +--- src/tools/miri/src/shims/unix/linux/sync.rs | 15 +++++---------- src/tools/miri/src/shims/windows/sync.rs | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index f743a298dad03..de108665e9f8c 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -433,9 +433,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timeout = this.read_scalar(timeout)?.to_i32()?; if epfd_value <= 0 || maxevents <= 0 { - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; - return interp_ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); } // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap() diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index 8640ce2e2cd6b..c258be78f7647 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -63,8 +63,7 @@ pub fn futex<'tcx>( }; if bitset == 0 { - this.set_last_error(LibcError("EINVAL"))?; - this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; return interp_ok(()); } @@ -75,9 +74,7 @@ pub fn futex<'tcx>( let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { - this.set_last_error(LibcError("EINVAL"))?; - this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return interp_ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); } }; let timeout_clock = if op & futex_realtime == futex_realtime { @@ -153,12 +150,12 @@ pub fn futex<'tcx>( Scalar::from_target_isize(0, this), // retval_succ Scalar::from_target_isize(-1, this), // retval_timeout dest.clone(), - this.eval_libc("ETIMEDOUT"), + this.eval_libc("ETIMEDOUT"), // errno_timeout ); } else { // The futex value doesn't match the expected value, so we return failure // right away without sleeping: -1 and errno set to EAGAIN. - this.set_last_error_and_return(LibcError("EAGAIN"), dest)?; + return this.set_last_error_and_return(LibcError("EAGAIN"), dest); } } // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val) @@ -178,9 +175,7 @@ pub fn futex<'tcx>( u32::MAX }; if bitset == 0 { - this.set_last_error(LibcError("EINVAL"))?; - this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; - return interp_ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); } // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait // will see the latest value on addr which could be changed by our caller diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index f8861085fe5de..f7566a8112da7 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -202,7 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Scalar::from_i32(1), // retval_succ Scalar::from_i32(0), // retval_timeout dest.clone(), - this.eval_windows("c", "ERROR_TIMEOUT"), + this.eval_windows("c", "ERROR_TIMEOUT"), // errno_timeout ); } From df9829c64dc0ada05473bead6a3ce9f078d8bb0a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 25 Oct 2024 16:48:50 +0200 Subject: [PATCH 20/27] indicate more explicitly where we close host file/dir handles --- src/tools/miri/src/shims/unix/fs.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 4a94d3807a637..12020d64cbe8f 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -149,6 +149,7 @@ impl FileDescription for FileHandle { // to handle possible errors correctly. let result = self.file.sync_all(); // Now we actually close the file and return the result. + drop(*self); interp_ok(result) } else { // We drop the file, this closes it but ignores any errors @@ -157,6 +158,7 @@ impl FileDescription for FileHandle { // `/dev/urandom` which are read-only. Check // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 // for a deeper discussion. + drop(*self); interp_ok(Ok(())) } } @@ -1247,12 +1249,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); } - let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) else { + let Some(mut open_dir) = this.machine.dirs.streams.remove(&dirp) else { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - if let Some(entry) = open_dir.entry { + if let Some(entry) = open_dir.entry.take() { this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; } + // We drop the `open_dir`, which will close the host dir handle. drop(open_dir); interp_ok(Scalar::from_i32(0)) From 3ff01dd908a804e58f571035b1822d681a855446 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Oct 2024 07:23:51 +0200 Subject: [PATCH 21/27] automatic PR: explain how to trigger CI --- src/tools/miri/.github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index f3975ec73892a..2e49131982297 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -139,7 +139,7 @@ jobs: git push -u origin $BRANCH - name: Create Pull Request run: | - PR=$(gh pr create -B master --title 'Automatic Rustup' --body '') + PR=$(gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.') ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ --stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \ --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience." From 72664f7760f72181741809a177e73349c53e066e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Oct 2024 08:58:48 +0200 Subject: [PATCH 22/27] fix error returned from readdir_r when isolation is enabled --- src/tools/miri/src/shims/unix/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 4a94d3807a637..c1d113e00cc6e 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1130,8 +1130,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`readdir_r`", reject_with)?; - // Set error code as "EBADF" (bad fd) - return this.set_last_error_and_return_i32(LibcError("EBADF")); + // Return error code, do *not* set `errno`. + return interp_ok(this.eval_libc("EBADF")); } let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { From 3f763c776eaeb0f2c088ec1fec6284d16e66564f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Oct 2024 09:02:26 +0200 Subject: [PATCH 23/27] do not use host 'raw_os_error' to compute target error code --- src/tools/miri/src/shims/unix/fs.rs | 35 +++++++++-------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index c1d113e00cc6e..e88f62ffbb276 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -229,6 +229,8 @@ impl FileDescription for FileHandle { TRUE => Ok(()), FALSE => { let mut err = io::Error::last_os_error(); + // This only runs on Windows hosts so we can use `raw_os_error`. + // We have to be careful not to forward that error code to target code. let code: u32 = err.raw_os_error().unwrap().try_into().unwrap(); if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) { if lock_nb { @@ -337,15 +339,7 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), } } - Err(e) => - match e.raw_os_error() { - Some(error) => interp_ok(error), - None => - throw_unsup_format!( - "the error {} couldn't be converted to a return value", - e - ), - }, + Err(e) => this.io_error_to_errnum(e)?.to_i32(), } } } @@ -1137,7 +1131,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| { err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir") })?; - interp_ok(Scalar::from_i32(match open_dir.read_dir.next() { + interp_ok(match open_dir.read_dir.next() { Some(Ok(dir_entry)) => { // Write into entry, write pointer to result, return 0 on success. // The name is written with write_os_str_to_c_str, while the rest of the @@ -1215,25 +1209,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result_place = this.deref_pointer(result_op)?; this.write_scalar(this.read_scalar(entry_op)?, &result_place)?; - 0 + Scalar::from_i32(0) } None => { // end of stream: return 0, assign *result=NULL this.write_null(&this.deref_pointer(result_op)?)?; - 0 + Scalar::from_i32(0) } - Some(Err(e)) => - match e.raw_os_error() { - // return positive error number on error - Some(error) => error, - None => { - throw_unsup_format!( - "the error {} couldn't be converted to a return value", - e - ) - } - }, - })) + Some(Err(e)) => { + // return positive error number on error (do *not* set last error) + this.io_error_to_errnum(e)? + } + }) } fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { From 4a34e2784b4eb0bb2c0c3740141937c88c4e4cfd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Oct 2024 09:09:14 +0200 Subject: [PATCH 24/27] do not set the file type to an error code --- src/tools/miri/src/shims/unix/fs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index e88f62ffbb276..eed6a7b04ff24 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -339,7 +339,10 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), } } - Err(e) => this.io_error_to_errnum(e)?.to_i32(), + Err(_) => { + // Fallback on error + interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()) + } } } } From 442f39582d2aec5026c8d75079a75f58579059e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 27 Oct 2024 06:21:24 +0100 Subject: [PATCH 25/27] Move an impl-Trait check from AST validation to AST lowering --- compiler/rustc_ast_lowering/src/path.rs | 19 ++++- compiler/rustc_ast_passes/messages.ftl | 2 - .../rustc_ast_passes/src/ast_validation.rs | 46 ------------ compiler/rustc_ast_passes/src/errors.rs | 7 -- .../src/error_codes/E0667.md | 6 +- .../src/hir_ty_lowering/mod.rs | 9 --- tests/crashes/126725.rs | 20 ------ tests/ui/impl-trait/impl_trait_projections.rs | 11 ++- .../impl-trait/impl_trait_projections.stderr | 70 +++++-------------- .../in-trait/bad-projection-from-opaque.rs | 22 ++++++ .../bad-projection-from-opaque.stderr | 11 +++ .../issues/issue-57979-impl-trait-in-path.rs | 2 +- .../issue-57979-impl-trait-in-path.stderr | 6 +- 13 files changed, 78 insertions(+), 153 deletions(-) delete mode 100644 tests/crashes/126725.rs create mode 100644 tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs create mode 100644 tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 258655de486f9..6a0f03c8b3196 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -34,7 +34,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { modifiers: Option, ) -> hir::QPath<'hir> { let qself_position = qself.as_ref().map(|q| q.position); - let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx)); + let qself = qself + .as_ref() + // Reject cases like `::Assoc` and `::Assoc`. + .map(|q| self.lower_ty(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path))); let partial_res = self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err)); @@ -75,6 +78,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { None }; + // Only permit `impl Trait` in the final segment. E.g., we permit `Option`, + // `option::Option::Xyz` and reject `option::Option::Xyz`. + let itctx = |i| { + if i + 1 == p.segments.len() { + itctx + } else { + ImplTraitContext::Disallowed(ImplTraitPosition::Path) + } + }; + let path_span_lo = p.span.shrink_to_lo(); let proj_start = p.segments.len() - unresolved_segments; let path = self.arena.alloc(hir::Path { @@ -121,7 +134,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { segment, param_mode, generic_args_mode, - itctx, + itctx(i), bound_modifier_allowed_features.clone(), ) }, @@ -185,7 +198,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { segment, param_mode, generic_args_mode, - itctx, + itctx(i), None, )); let qpath = hir::QPath::TypeRelative(ty, hir_segment); diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 92acaaa5f36b0..d81fd53938b89 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -146,8 +146,6 @@ ast_passes_generic_before_constraints = generic arguments must come before the f ast_passes_generic_default_trailing = generic parameters with a default must be trailing -ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters - ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed .help = remove one of these features diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bf6ebfb160bc4..485d84b0b6dd3 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -80,10 +80,6 @@ struct AstValidator<'a> { disallow_tilde_const: Option, - /// Used to ban `impl Trait` in path projections like `::Item` - /// or `Foo::Bar` - is_impl_trait_banned: bool, - /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe. extern_mod_safety: Option, @@ -123,12 +119,6 @@ impl<'a> AstValidator<'a> { self.extern_mod_safety = old; } - fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { - let old = mem::replace(&mut self.is_impl_trait_banned, true); - f(self); - self.is_impl_trait_banned = old; - } - fn with_tilde_const( &mut self, disallowed: Option, @@ -213,37 +203,6 @@ impl<'a> AstValidator<'a> { .with_tilde_const(Some(TildeConstReason::TraitObject), |this| { visit::walk_ty(this, t) }), - TyKind::Path(qself, path) => { - // We allow these: - // - `Option` - // - `option::Option` - // - `option::Option::Foo` - // - // But not these: - // - `::Foo` - // - `option::Option::Foo`. - // - // To implement this, we disallow `impl Trait` from `qself` - // (for cases like `::Foo>`) - // but we allow `impl Trait` in `GenericArgs` - // iff there are no more PathSegments. - if let Some(qself) = qself { - // `impl Trait` in `qself` is always illegal - self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty)); - } - - // Note that there should be a call to visit_path here, - // so if any logic is added to process `Path`s a call to it should be - // added both in visit_path and here. This code mirrors visit::walk_path. - for (i, segment) in path.segments.iter().enumerate() { - // Allow `impl Trait` iff we're on the final path segment - if i == path.segments.len() - 1 { - self.visit_path_segment(segment); - } else { - self.with_banned_impl_trait(|this| this.visit_path_segment(segment)); - } - } - } _ => visit::walk_ty(self, t), } } @@ -737,10 +696,6 @@ impl<'a> AstValidator<'a> { } } TyKind::ImplTrait(_, bounds) => { - if self.is_impl_trait_banned { - self.dcx().emit_err(errors::ImplTraitPath { span: ty.span }); - } - if let Some(outer_impl_trait_sp) = self.outer_impl_trait { self.dcx().emit_err(errors::NestedImplTrait { span: ty.span, @@ -1729,7 +1684,6 @@ pub fn check_crate( has_proc_macro_decls: false, outer_impl_trait: None, disallow_tilde_const: Some(TildeConstReason::Item), - is_impl_trait_banned: false, extern_mod_safety: None, lint_buffer: lints, }; diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 4ca1acde1e289..8c3ac9864ed8a 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -418,13 +418,6 @@ pub(crate) struct TraitObjectBound { pub span: Span, } -#[derive(Diagnostic)] -#[diag(ast_passes_impl_trait_path, code = E0667)] -pub(crate) struct ImplTraitPath { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_passes_nested_impl_trait, code = E0666)] pub(crate) struct NestedImplTrait { diff --git a/compiler/rustc_error_codes/src/error_codes/E0667.md b/compiler/rustc_error_codes/src/error_codes/E0667.md index 0709a24c43312..339bc0686103a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0667.md +++ b/compiler/rustc_error_codes/src/error_codes/E0667.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + `impl Trait` is not allowed in path parameters. Erroneous code example: -```compile_fail,E0667 +```ignore (removed error code) fn some_fn(mut x: impl Iterator) -> ::Item { // error! x.next().unwrap() } @@ -11,7 +13,7 @@ fn some_fn(mut x: impl Iterator) -> ::Item { // error! You cannot use `impl Trait` in path parameters. If you want something equivalent, you can do this instead: -``` +```ignore (removed error code) fn some_fn(mut x: T) -> T::Item { // ok! x.next().unwrap() } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 863c077a9e03c..cfd1241ad69c8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1203,15 +1203,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.emit() } else if let Err(reported) = qself_ty.error_reported() { reported - } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() { - // `::Assoc` makes no sense. - struct_span_code_err!( - self.dcx(), - tcx.def_span(alias_ty.def_id), - E0667, - "`impl Trait` is not allowed in path parameters" - ) - .emit() // Already reported in an earlier stage. } else { self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?; diff --git a/tests/crashes/126725.rs b/tests/crashes/126725.rs deleted file mode 100644 index d7a7d21ae4244..0000000000000 --- a/tests/crashes/126725.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ known-bug: rust-lang/rust#126725 -trait Foo { - fn foo<'a>(&'a self) -> <&'a impl Sized as Bar>::Output; -} - -trait Bar { - type Output; -} - -struct X(i32); - -impl<'a> Bar for &'a X { - type Output = &'a i32; -} - -impl Foo for X { - fn foo<'a>(&'a self) -> <&'a Self as Bar>::Output { - &self.0 - } -} diff --git a/tests/ui/impl-trait/impl_trait_projections.rs b/tests/ui/impl-trait/impl_trait_projections.rs index 365ac85e2f665..2c277aee06dad 100644 --- a/tests/ui/impl-trait/impl_trait_projections.rs +++ b/tests/ui/impl-trait/impl_trait_projections.rs @@ -10,30 +10,27 @@ fn path_parametrized_type_is_allowed() -> option::Option { } fn projection_is_disallowed(x: impl Iterator) -> ::Item { -//~^ ERROR `impl Trait` is not allowed in path parameters -//~| ERROR `impl Trait` is not allowed in path parameters +//~^ ERROR `impl Trait` is not allowed in paths x.next().unwrap() } fn projection_with_named_trait_is_disallowed(mut x: impl Iterator) -> ::Item -//~^ ERROR `impl Trait` is not allowed in path parameters +//~^ ERROR `impl Trait` is not allowed in paths { x.next().unwrap() } fn projection_with_named_trait_inside_path_is_disallowed() -> <::std::ops::Range as Iterator>::Item -//~^ ERROR `impl Trait` is not allowed in path parameters -//~| ERROR `impl Debug: Step` is not satisfied +//~^ ERROR `impl Trait` is not allowed in paths { - //~^ ERROR `impl Debug: Step` is not satisfied (1i32..100).next().unwrap() } fn projection_from_impl_trait_inside_dyn_trait_is_disallowed() -> as Iterator>::Item -//~^ ERROR `impl Trait` is not allowed in path parameters +//~^ ERROR `impl Trait` is not allowed in paths { panic!() } diff --git a/tests/ui/impl-trait/impl_trait_projections.stderr b/tests/ui/impl-trait/impl_trait_projections.stderr index d62e3ac4183f5..5e0b80fcd5922 100644 --- a/tests/ui/impl-trait/impl_trait_projections.stderr +++ b/tests/ui/impl-trait/impl_trait_projections.stderr @@ -1,73 +1,35 @@ -error[E0667]: `impl Trait` is not allowed in path parameters +error[E0562]: `impl Trait` is not allowed in paths --> $DIR/impl_trait_projections.rs:12:51 | LL | fn projection_is_disallowed(x: impl Iterator) -> ::Item { | ^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0667]: `impl Trait` is not allowed in path parameters - --> $DIR/impl_trait_projections.rs:19:9 +error[E0562]: `impl Trait` is not allowed in paths + --> $DIR/impl_trait_projections.rs:18:9 | LL | -> ::Item | ^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0667]: `impl Trait` is not allowed in path parameters - --> $DIR/impl_trait_projections.rs:26:27 +error[E0562]: `impl Trait` is not allowed in paths + --> $DIR/impl_trait_projections.rs:25:27 | LL | -> <::std::ops::Range as Iterator>::Item | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0667]: `impl Trait` is not allowed in path parameters - --> $DIR/impl_trait_projections.rs:35:29 +error[E0562]: `impl Trait` is not allowed in paths + --> $DIR/impl_trait_projections.rs:32:29 | LL | -> as Iterator>::Item | ^^^^^^^^^^ - -error[E0667]: `impl Trait` is not allowed in path parameters - --> $DIR/impl_trait_projections.rs:12:51 - | -LL | fn projection_is_disallowed(x: impl Iterator) -> ::Item { - | ^^^^^^^^^^^^^ - -error[E0277]: the trait bound `impl Debug: Step` is not satisfied - --> $DIR/impl_trait_projections.rs:26:8 - | -LL | -> <::std::ops::Range as Iterator>::Item - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Step` is not implemented for `impl Debug`, which is required by `std::ops::Range: Iterator` - | - = help: the following other types implement trait `Step`: - Char - Ipv4Addr - Ipv6Addr - char - i128 - i16 - i32 - i64 - and 8 others - = note: required for `std::ops::Range` to implement `Iterator` - -error[E0277]: the trait bound `impl Debug: Step` is not satisfied - --> $DIR/impl_trait_projections.rs:29:1 - | -LL | / { -LL | | -LL | | (1i32..100).next().unwrap() -LL | | } - | |_^ the trait `Step` is not implemented for `impl Debug`, which is required by `std::ops::Range: Iterator` | - = help: the following other types implement trait `Step`: - Char - Ipv4Addr - Ipv6Addr - char - i128 - i16 - i32 - i64 - and 8 others - = note: required for `std::ops::Range` to implement `Iterator` + = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0277, E0667. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0562`. diff --git a/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs new file mode 100644 index 0000000000000..c2c22cd1abfcc --- /dev/null +++ b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs @@ -0,0 +1,22 @@ +// issue: rust-lang/rust#126725 + +trait Foo { + fn foo<'a>() -> <&'a impl Sized as Bar>::Output; + //~^ ERROR `impl Trait` is not allowed in paths +} + +trait Bar { + type Output; +} + +impl<'a> Bar for &'a () { + type Output = &'a i32; +} + +impl Foo for () { + fn foo<'a>() -> <&'a Self as Bar>::Output { + &0 + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr new file mode 100644 index 0000000000000..bea7ccd1a18d0 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr @@ -0,0 +1,11 @@ +error[E0562]: `impl Trait` is not allowed in paths + --> $DIR/bad-projection-from-opaque.rs:4:26 + | +LL | fn foo<'a>() -> <&'a impl Sized as Bar>::Output; + | ^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0562`. diff --git a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs index c5ecd1caae1f9..9466668b1dcd4 100644 --- a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs +++ b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs @@ -6,7 +6,7 @@ pub trait Bar { } pub trait Quux { type Assoc; } pub fn demo(_: impl Quux<(), Assoc=<() as Quux>::Assoc>) { } -//~^ ERROR `impl Trait` is not allowed in path parameters +//~^ ERROR `impl Trait` is not allowed in paths impl Quux for () { type Assoc = u32; } fn main() { } diff --git a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr index 55f47785f0eeb..25547dfa66f5d 100644 --- a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr +++ b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr @@ -1,9 +1,11 @@ -error[E0667]: `impl Trait` is not allowed in path parameters +error[E0562]: `impl Trait` is not allowed in paths --> $DIR/issue-57979-impl-trait-in-path.rs:8:48 | LL | pub fn demo(_: impl Quux<(), Assoc=<() as Quux>::Assoc>) { } | ^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0667`. +For more information about this error, try `rustc --explain E0562`. From 924ded6b72a83d550a7d60f434a1472ef1ddb30e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 27 Oct 2024 04:30:51 -0700 Subject: [PATCH 26/27] Clean up some comments on lint implementation --- compiler/rustc_lint/src/builtin.rs | 17 +++++------------ compiler/rustc_lint/src/context.rs | 20 +++----------------- compiler/rustc_lint/src/early.rs | 18 ++++-------------- compiler/rustc_lint/src/late.rs | 17 +++-------------- compiler/rustc_lint/src/lib.rs | 12 +++--------- 5 files changed, 18 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 3ee4980a94821..dbb8c66753236 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -4,21 +4,14 @@ //! AST visitor. Also see `rustc_session::lint::builtin`, which contains the //! definitions of lints that are emitted directly inside the main compiler. //! -//! To add a new lint to rustc, declare it here using `declare_lint!()`. +//! To add a new lint to rustc, declare it here using [`declare_lint!`]. //! Then add code to emit the new lint in the appropriate circumstances. -//! You can do that in an existing `LintPass` if it makes sense, or in a -//! new `LintPass`, or using `Session::add_lint` elsewhere in the -//! compiler. Only do the latter if the check can't be written cleanly as a -//! `LintPass` (also, note that such lints will need to be defined in -//! `rustc_session::lint::builtin`, not here). //! -//! If you define a new `EarlyLintPass`, you will also need to add it to the -//! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in -//! `lib.rs`. Use the former for unit-like structs and the latter for structs -//! with a `pub fn new()`. +//! If you define a new [`EarlyLintPass`], you will also need to add it to the +//! [`crate::early_lint_methods!`] invocation in `lib.rs`. //! -//! If you define a new `LateLintPass`, you will also need to add it to the -//! `late_lint_methods!` invocation in `lib.rs`. +//! If you define a new [`LateLintPass`], you will also need to add it to the +//! [`crate::late_lint_methods!`] invocation in `lib.rs`. use std::fmt::Write; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 39f90a8e9ed14..4af1fd1baf9cd 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -1,18 +1,7 @@ -//! Implementation of lint checking. +//! Basic types for managing and implementing lints. //! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. +//! See for an +//! overview of how lints are implemented. use std::cell::Cell; use std::{iter, slice}; @@ -52,9 +41,6 @@ type LateLintPassFactory = dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync; /// Information about the registered lints. -/// -/// This is basically the subset of `Context` that we can -/// build early in the compile pipeline. pub struct LintStore { /// Registered lints. lints: Vec<&'static Lint>, diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index e2247d3a1f6c3..acccff77a10a7 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -1,18 +1,8 @@ -//! Implementation of lint checking. +//! Implementation of the early lint pass. //! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. +//! The early lint pass works on AST nodes after macro expansion and name +//! resolution, just before AST lowering. These lints are for purely +//! syntactical lints. use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 9b1877599bae1..9d35ce19b5702 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -1,18 +1,7 @@ -//! Implementation of lint checking. +//! Implementation of the late lint pass. //! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. +//! The late lint pass Works on HIR nodes, towards the end of analysis (after +//! borrow checking, etc.). These lints have full type information available. use std::any::Any; use std::cell::Cell; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5389860e23bb2..d950b1ff5ec88 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -6,20 +6,14 @@ //! other phases of the compiler, which are generally required to hold in order //! to compile the program at all. //! -//! Most lints can be written as [LintPass] instances. These run after +//! Most lints can be written as [`LintPass`] instances. These run after //! all other analyses. The `LintPass`es built into rustc are defined //! within [rustc_session::lint::builtin], //! which has further comments on how to add such a lint. //! rustc can also load external lint plugins, as is done for Clippy. //! -//! Some of rustc's lints are defined elsewhere in the compiler and work by -//! calling `add_lint()` on the overall `Session` object. This works when -//! it happens before the main lint pass, which emits the lints stored by -//! `add_lint()`. To emit lints after the main lint pass (from codegen, for -//! example) requires more effort. See `emit_lint` and `GatherNodeLevels` -//! in `context.rs`. -//! -//! Some code also exists in [rustc_session::lint], [rustc_middle::lint]. +//! See for an +//! overview of how lints are implemented. //! //! ## Note //! From 4ccaef12a65ccdd0d1071c08c15e2c41fa425000 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 27 Oct 2024 16:20:14 +0000 Subject: [PATCH 27/27] Revert "ci update freebsd version proposal, freebsd 12 being eol." This reverts commit 1239c81c145d2bfb96f32856f377cd741d5c7256. Fix GH-132185 revert for now until early next year/FreeBSD 13.3 becomes EOL. --- .../host-x86_64/dist-various-2/Dockerfile | 6 +++--- .../host-x86_64/dist-x86_64-freebsd/Dockerfile | 6 +++--- src/ci/docker/scripts/freebsd-toolchain.sh | 8 ++++---- src/doc/rustc/src/platform-support.md | 18 +++++++++--------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 8aabfaafbabbc..ece5f174d06de 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -56,9 +56,9 @@ ENV \ CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ CXX_x86_64_fortanix_unknown_sgx=clang++-11 \ CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ - AR_i686_unknown_freebsd=i686-unknown-freebsd13-ar \ - CC_i686_unknown_freebsd=i686-unknown-freebsd13-clang \ - CXX_i686_unknown_freebsd=i686-unknown-freebsd13-clang++ \ + AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \ + CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \ + CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \ CC_aarch64_unknown_uefi=clang-11 \ CXX_aarch64_unknown_uefi=clang++-11 \ CC_i686_unknown_uefi=clang-11 \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile index fd0f5da8c495a..f42e6f770ebe8 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile @@ -29,9 +29,9 @@ COPY scripts/cmake.sh /scripts/ RUN /scripts/cmake.sh ENV \ - AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-ar \ - CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang \ - CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang++ + AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar \ + CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang \ + CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang++ ENV HOSTS=x86_64-unknown-freebsd diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh index 4826b81d56ce3..0d02636db9196 100755 --- a/src/ci/docker/scripts/freebsd-toolchain.sh +++ b/src/ci/docker/scripts/freebsd-toolchain.sh @@ -5,8 +5,8 @@ set -eux arch=$1 binutils_version=2.40 -freebsd_version=13.2 -triple=$arch-unknown-freebsd13 +freebsd_version=12.3 +triple=$arch-unknown-freebsd12 sysroot=/usr/local/$triple hide_output() { @@ -59,7 +59,7 @@ done # Originally downloaded from: # URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz -URL=https://ci-mirrors.rust-lang.org/rustc/2024-02-18-freebsd-${freebsd_version}-${freebsd_arch}-base.txz +URL=https://ci-mirrors.rust-lang.org/rustc/2022-05-06-freebsd-${freebsd_version}-${freebsd_arch}-base.txz curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}" # Clang can do cross-builds out of the box, if we give it the right @@ -68,7 +68,7 @@ curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}" # there might be other problems.) # # The --target option is last because the cross-build of LLVM uses -# --target without an OS version ("-freebsd" vs. "-freebsd13"). This +# --target without an OS version ("-freebsd" vs. "-freebsd12"). This # makes Clang default to libstdc++ (which no longer exists), and also # controls other features, like GNU-style symbol table hashing and # anything predicated on the version number in the __FreeBSD__ diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index f7b8a8fc6e649..04bb40d750c95 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -100,7 +100,7 @@ target | notes [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3) `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17) -`x86_64-unknown-freebsd` | 64-bit FreeBSD (version 13.2) +`x86_64-unknown-freebsd` | 64-bit FreeBSD `x86_64-unknown-illumos` | illumos `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 @@ -166,7 +166,7 @@ target | std | notes `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] [`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI] -`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD (version 13.2) [^x86_32-floats-return-ABI] +`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) @@ -258,7 +258,7 @@ target | std | host | notes [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS | [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | -`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD (version 13.2) +`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit `aarch64-unknown-illumos` | ✓ | ✓ | ARM64 illumos `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) @@ -277,14 +277,14 @@ target | std | host | notes `armv4t-unknown-linux-gnueabi` | ? | | Armv4T Linux [`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Bare Armv5TE `armv5te-unknown-linux-uclibceabi` | ? | | Armv5TE Linux with uClibc -`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD (version 13.2) +`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD [`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float [`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain) [`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? | | RTEMS OS for ARM BSPs [`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain) [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat -`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD (version 13.2) +`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float [`armv7-unknown-trusty`](platform-support/trusty.md) | ? | | [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks @@ -343,9 +343,9 @@ target | std | host | notes [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | -`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2, version 13.2) -`powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD (version 13.2) -`powerpc-unknown-freebsd` | | | PowerPC FreeBSD (version 13.2) +`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) +`powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD +`powerpc-unknown-freebsd` | | | PowerPC FreeBSD `powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `powerpc64le-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3, Little Endian @@ -361,7 +361,7 @@ target | std | host | notes [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit -`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD (version 13.2) +`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64