From 11c15229557cb489a5604f13e26a51099cc05314 Mon Sep 17 00:00:00 2001 From: beetrees Date: Wed, 21 May 2025 18:11:11 +0100 Subject: [PATCH 01/59] Enable `__powitf2` on MSVC --- library/compiler-builtins/builtins-test/tests/float_pow.rs | 2 -- library/compiler-builtins/compiler-builtins/src/float/pow.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/library/compiler-builtins/builtins-test/tests/float_pow.rs b/library/compiler-builtins/builtins-test/tests/float_pow.rs index 8209543e666a..0e8ae88e83ef 100644 --- a/library/compiler-builtins/builtins-test/tests/float_pow.rs +++ b/library/compiler-builtins/builtins-test/tests/float_pow.rs @@ -58,8 +58,6 @@ pow! { } #[cfg(f128_enabled)] -// FIXME(f16_f128): MSVC cannot build these until `__divtf3` is available in nightly. -#[cfg(not(target_env = "msvc"))] #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] pow! { f128, 1e-36, __powitf2, not(feature = "no-sys-f128"); diff --git a/library/compiler-builtins/compiler-builtins/src/float/pow.rs b/library/compiler-builtins/compiler-builtins/src/float/pow.rs index 45a4ad9049de..6997a9c213c5 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/pow.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/pow.rs @@ -32,8 +32,6 @@ intrinsics! { #[ppc_alias = __powikf2] #[cfg(f128_enabled)] - // FIXME(f16_f128): MSVC cannot build these until `__divtf3` is available in nightly. - #[cfg(not(target_env = "msvc"))] pub extern "C" fn __powitf2(a: f128, b: i32) -> f128 { pow(a, b) } From 7966f1b556f39882b3e451533a74a3b24a34cb05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1lyi=20L=C5=91rinc?= Date: Sat, 10 May 2025 08:36:28 +0000 Subject: [PATCH 02/59] fixed typo in readme --- library/compiler-builtins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/compiler-builtins/README.md b/library/compiler-builtins/README.md index 3130ff7b77d9..177bce624e0a 100644 --- a/library/compiler-builtins/README.md +++ b/library/compiler-builtins/README.md @@ -5,7 +5,7 @@ This repository contains two main crates: * `compiler-builtins`: symbols that the compiler expects to be available at link time * `libm`: a Rust implementation of C math libraries, used to provide - implementations in `ocre`. + implementations in `core`. More details are at [compiler-builtins/README.md](compiler-builtins/README.md) and [libm/README.md](libm/README.md). From db21837095f18a76d1fecb7d55547688a6b19647 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 22 Apr 2025 19:35:21 -0400 Subject: [PATCH 03/59] libm: Clean up unused files These were deleted during refactoring in 0a2dc5d9 ("Combine the source files for more generic implementations") but got added back by accident in 54bac411 ("refactor: Move the libm crate to a subdirectory"). Remove them again here. --- .../libm/src/math/copysignf.rs | 8 ---- .../libm/src/math/copysignf128.rs | 8 ---- .../libm/src/math/copysignf16.rs | 8 ---- .../compiler-builtins/libm/src/math/fabsf.rs | 39 ------------------- .../libm/src/math/fabsf128.rs | 31 --------------- .../libm/src/math/fabsf16.rs | 31 --------------- .../compiler-builtins/libm/src/math/fdimf.rs | 12 ------ .../libm/src/math/fdimf128.rs | 12 ------ .../libm/src/math/fdimf16.rs | 12 ------ .../compiler-builtins/libm/src/math/floorf.rs | 13 ------- .../libm/src/math/floorf128.rs | 7 ---- .../libm/src/math/floorf16.rs | 7 ---- .../compiler-builtins/libm/src/math/fmodf.rs | 5 --- .../libm/src/math/fmodf128.rs | 5 --- .../libm/src/math/fmodf16.rs | 5 --- .../compiler-builtins/libm/src/math/ldexpf.rs | 4 -- .../libm/src/math/ldexpf128.rs | 4 -- .../libm/src/math/ldexpf16.rs | 4 -- .../compiler-builtins/libm/src/math/roundf.rs | 5 --- .../libm/src/math/roundf128.rs | 5 --- .../libm/src/math/roundf16.rs | 5 --- .../libm/src/math/scalbnf.rs | 4 -- .../libm/src/math/scalbnf128.rs | 4 -- .../libm/src/math/scalbnf16.rs | 4 -- .../compiler-builtins/libm/src/math/sqrtf.rs | 15 ------- .../libm/src/math/sqrtf128.rs | 5 --- .../libm/src/math/sqrtf16.rs | 11 ------ .../compiler-builtins/libm/src/math/truncf.rs | 23 ----------- .../libm/src/math/truncf128.rs | 7 ---- .../libm/src/math/truncf16.rs | 7 ---- 30 files changed, 310 deletions(-) delete mode 100644 library/compiler-builtins/libm/src/math/copysignf.rs delete mode 100644 library/compiler-builtins/libm/src/math/copysignf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/copysignf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/fabsf.rs delete mode 100644 library/compiler-builtins/libm/src/math/fabsf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/fabsf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/fdimf.rs delete mode 100644 library/compiler-builtins/libm/src/math/fdimf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/fdimf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/floorf.rs delete mode 100644 library/compiler-builtins/libm/src/math/floorf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/floorf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/fmodf.rs delete mode 100644 library/compiler-builtins/libm/src/math/fmodf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/fmodf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/ldexpf.rs delete mode 100644 library/compiler-builtins/libm/src/math/ldexpf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/ldexpf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/roundf.rs delete mode 100644 library/compiler-builtins/libm/src/math/roundf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/roundf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/scalbnf.rs delete mode 100644 library/compiler-builtins/libm/src/math/scalbnf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/scalbnf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/sqrtf.rs delete mode 100644 library/compiler-builtins/libm/src/math/sqrtf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/sqrtf16.rs delete mode 100644 library/compiler-builtins/libm/src/math/truncf.rs delete mode 100644 library/compiler-builtins/libm/src/math/truncf128.rs delete mode 100644 library/compiler-builtins/libm/src/math/truncf16.rs diff --git a/library/compiler-builtins/libm/src/math/copysignf.rs b/library/compiler-builtins/libm/src/math/copysignf.rs deleted file mode 100644 index 8b9bed4c0c42..000000000000 --- a/library/compiler-builtins/libm/src/math/copysignf.rs +++ /dev/null @@ -1,8 +0,0 @@ -/// Sign of Y, magnitude of X (f32) -/// -/// Constructs a number with the magnitude (absolute value) of its -/// first argument, `x`, and the sign of its second argument, `y`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn copysignf(x: f32, y: f32) -> f32 { - super::generic::copysign(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/copysignf128.rs b/library/compiler-builtins/libm/src/math/copysignf128.rs deleted file mode 100644 index 7bd81d42b2e9..000000000000 --- a/library/compiler-builtins/libm/src/math/copysignf128.rs +++ /dev/null @@ -1,8 +0,0 @@ -/// Sign of Y, magnitude of X (f128) -/// -/// Constructs a number with the magnitude (absolute value) of its -/// first argument, `x`, and the sign of its second argument, `y`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn copysignf128(x: f128, y: f128) -> f128 { - super::generic::copysign(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/copysignf16.rs b/library/compiler-builtins/libm/src/math/copysignf16.rs deleted file mode 100644 index 820658686010..000000000000 --- a/library/compiler-builtins/libm/src/math/copysignf16.rs +++ /dev/null @@ -1,8 +0,0 @@ -/// Sign of Y, magnitude of X (f16) -/// -/// Constructs a number with the magnitude (absolute value) of its -/// first argument, `x`, and the sign of its second argument, `y`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn copysignf16(x: f16, y: f16) -> f16 { - super::generic::copysign(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/fabsf.rs b/library/compiler-builtins/libm/src/math/fabsf.rs deleted file mode 100644 index e5820a26c523..000000000000 --- a/library/compiler-builtins/libm/src/math/fabsf.rs +++ /dev/null @@ -1,39 +0,0 @@ -/// Absolute value (magnitude) (f32) -/// -/// Calculates the absolute value (magnitude) of the argument `x`, -/// by direct manipulation of the bit representation of `x`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fabsf(x: f32) -> f32 { - select_implementation! { - name: fabsf, - use_arch: all(target_arch = "wasm32", intrinsics_enabled), - args: x, - } - - super::generic::fabs(x) -} - -// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 -#[cfg(not(target_arch = "powerpc64"))] -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sanity_check() { - assert_eq!(fabsf(-1.0), 1.0); - assert_eq!(fabsf(2.8), 2.8); - } - - /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs - #[test] - fn spec_tests() { - assert!(fabsf(f32::NAN).is_nan()); - for f in [0.0, -0.0].iter().copied() { - assert_eq!(fabsf(f), 0.0); - } - for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() { - assert_eq!(fabsf(f), f32::INFINITY); - } - } -} diff --git a/library/compiler-builtins/libm/src/math/fabsf128.rs b/library/compiler-builtins/libm/src/math/fabsf128.rs deleted file mode 100644 index 46429ca49403..000000000000 --- a/library/compiler-builtins/libm/src/math/fabsf128.rs +++ /dev/null @@ -1,31 +0,0 @@ -/// Absolute value (magnitude) (f128) -/// -/// Calculates the absolute value (magnitude) of the argument `x`, -/// by direct manipulation of the bit representation of `x`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fabsf128(x: f128) -> f128 { - super::generic::fabs(x) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sanity_check() { - assert_eq!(fabsf128(-1.0), 1.0); - assert_eq!(fabsf128(2.8), 2.8); - } - - /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs - #[test] - fn spec_tests() { - assert!(fabsf128(f128::NAN).is_nan()); - for f in [0.0, -0.0].iter().copied() { - assert_eq!(fabsf128(f), 0.0); - } - for f in [f128::INFINITY, f128::NEG_INFINITY].iter().copied() { - assert_eq!(fabsf128(f), f128::INFINITY); - } - } -} diff --git a/library/compiler-builtins/libm/src/math/fabsf16.rs b/library/compiler-builtins/libm/src/math/fabsf16.rs deleted file mode 100644 index eee42ac6a3c6..000000000000 --- a/library/compiler-builtins/libm/src/math/fabsf16.rs +++ /dev/null @@ -1,31 +0,0 @@ -/// Absolute value (magnitude) (f16) -/// -/// Calculates the absolute value (magnitude) of the argument `x`, -/// by direct manipulation of the bit representation of `x`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fabsf16(x: f16) -> f16 { - super::generic::fabs(x) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sanity_check() { - assert_eq!(fabsf16(-1.0), 1.0); - assert_eq!(fabsf16(2.8), 2.8); - } - - /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs - #[test] - fn spec_tests() { - assert!(fabsf16(f16::NAN).is_nan()); - for f in [0.0, -0.0].iter().copied() { - assert_eq!(fabsf16(f), 0.0); - } - for f in [f16::INFINITY, f16::NEG_INFINITY].iter().copied() { - assert_eq!(fabsf16(f), f16::INFINITY); - } - } -} diff --git a/library/compiler-builtins/libm/src/math/fdimf.rs b/library/compiler-builtins/libm/src/math/fdimf.rs deleted file mode 100644 index 367ef517c63b..000000000000 --- a/library/compiler-builtins/libm/src/math/fdimf.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// Positive difference (f32) -/// -/// Determines the positive difference between arguments, returning: -/// * x - y if x > y, or -/// * +0 if x <= y, or -/// * NAN if either argument is NAN. -/// -/// A range error may occur. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fdimf(x: f32, y: f32) -> f32 { - super::generic::fdim(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/fdimf128.rs b/library/compiler-builtins/libm/src/math/fdimf128.rs deleted file mode 100644 index 6f3d1d0ff1d5..000000000000 --- a/library/compiler-builtins/libm/src/math/fdimf128.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// Positive difference (f128) -/// -/// Determines the positive difference between arguments, returning: -/// * x - y if x > y, or -/// * +0 if x <= y, or -/// * NAN if either argument is NAN. -/// -/// A range error may occur. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fdimf128(x: f128, y: f128) -> f128 { - super::generic::fdim(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/fdimf16.rs b/library/compiler-builtins/libm/src/math/fdimf16.rs deleted file mode 100644 index 37bd68858179..000000000000 --- a/library/compiler-builtins/libm/src/math/fdimf16.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// Positive difference (f16) -/// -/// Determines the positive difference between arguments, returning: -/// * x - y if x > y, or -/// * +0 if x <= y, or -/// * NAN if either argument is NAN. -/// -/// A range error may occur. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fdimf16(x: f16, y: f16) -> f16 { - super::generic::fdim(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/floorf.rs b/library/compiler-builtins/libm/src/math/floorf.rs deleted file mode 100644 index 16957b7f3557..000000000000 --- a/library/compiler-builtins/libm/src/math/floorf.rs +++ /dev/null @@ -1,13 +0,0 @@ -/// Floor (f32) -/// -/// Finds the nearest integer less than or equal to `x`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn floorf(x: f32) -> f32 { - select_implementation! { - name: floorf, - use_arch: all(target_arch = "wasm32", intrinsics_enabled), - args: x, - } - - return super::generic::floor(x); -} diff --git a/library/compiler-builtins/libm/src/math/floorf128.rs b/library/compiler-builtins/libm/src/math/floorf128.rs deleted file mode 100644 index 9a9fe4151152..000000000000 --- a/library/compiler-builtins/libm/src/math/floorf128.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// Floor (f128) -/// -/// Finds the nearest integer less than or equal to `x`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn floorf128(x: f128) -> f128 { - return super::generic::floor(x); -} diff --git a/library/compiler-builtins/libm/src/math/floorf16.rs b/library/compiler-builtins/libm/src/math/floorf16.rs deleted file mode 100644 index f9b868e04109..000000000000 --- a/library/compiler-builtins/libm/src/math/floorf16.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// Floor (f16) -/// -/// Finds the nearest integer less than or equal to `x`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn floorf16(x: f16) -> f16 { - return super::generic::floor(x); -} diff --git a/library/compiler-builtins/libm/src/math/fmodf.rs b/library/compiler-builtins/libm/src/math/fmodf.rs deleted file mode 100644 index 4e95696e20d6..000000000000 --- a/library/compiler-builtins/libm/src/math/fmodf.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmodf(x: f32, y: f32) -> f32 { - super::generic::fmod(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/fmodf128.rs b/library/compiler-builtins/libm/src/math/fmodf128.rs deleted file mode 100644 index ff0e0493e26b..000000000000 --- a/library/compiler-builtins/libm/src/math/fmodf128.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmodf128(x: f128, y: f128) -> f128 { - super::generic::fmod(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/fmodf16.rs b/library/compiler-builtins/libm/src/math/fmodf16.rs deleted file mode 100644 index 11972a7de4ff..000000000000 --- a/library/compiler-builtins/libm/src/math/fmodf16.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn fmodf16(x: f16, y: f16) -> f16 { - super::generic::fmod(x, y) -} diff --git a/library/compiler-builtins/libm/src/math/ldexpf.rs b/library/compiler-builtins/libm/src/math/ldexpf.rs deleted file mode 100644 index 95b27fc49d28..000000000000 --- a/library/compiler-builtins/libm/src/math/ldexpf.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ldexpf(x: f32, n: i32) -> f32 { - super::scalbnf(x, n) -} diff --git a/library/compiler-builtins/libm/src/math/ldexpf128.rs b/library/compiler-builtins/libm/src/math/ldexpf128.rs deleted file mode 100644 index b35277d15fba..000000000000 --- a/library/compiler-builtins/libm/src/math/ldexpf128.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ldexpf128(x: f128, n: i32) -> f128 { - super::scalbnf128(x, n) -} diff --git a/library/compiler-builtins/libm/src/math/ldexpf16.rs b/library/compiler-builtins/libm/src/math/ldexpf16.rs deleted file mode 100644 index 8de6cffd6998..000000000000 --- a/library/compiler-builtins/libm/src/math/ldexpf16.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ldexpf16(x: f16, n: i32) -> f16 { - super::scalbnf16(x, n) -} diff --git a/library/compiler-builtins/libm/src/math/roundf.rs b/library/compiler-builtins/libm/src/math/roundf.rs deleted file mode 100644 index b5d7c9d693e7..000000000000 --- a/library/compiler-builtins/libm/src/math/roundf.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Round `x` to the nearest integer, breaking ties away from zero. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn roundf(x: f32) -> f32 { - super::generic::round(x) -} diff --git a/library/compiler-builtins/libm/src/math/roundf128.rs b/library/compiler-builtins/libm/src/math/roundf128.rs deleted file mode 100644 index fc3164929fe4..000000000000 --- a/library/compiler-builtins/libm/src/math/roundf128.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Round `x` to the nearest integer, breaking ties away from zero. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn roundf128(x: f128) -> f128 { - super::generic::round(x) -} diff --git a/library/compiler-builtins/libm/src/math/roundf16.rs b/library/compiler-builtins/libm/src/math/roundf16.rs deleted file mode 100644 index 8b356eaabeec..000000000000 --- a/library/compiler-builtins/libm/src/math/roundf16.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Round `x` to the nearest integer, breaking ties away from zero. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn roundf16(x: f16) -> f16 { - super::generic::round(x) -} diff --git a/library/compiler-builtins/libm/src/math/scalbnf.rs b/library/compiler-builtins/libm/src/math/scalbnf.rs deleted file mode 100644 index 57e7ba76f60b..000000000000 --- a/library/compiler-builtins/libm/src/math/scalbnf.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn scalbnf(x: f32, n: i32) -> f32 { - super::generic::scalbn(x, n) -} diff --git a/library/compiler-builtins/libm/src/math/scalbnf128.rs b/library/compiler-builtins/libm/src/math/scalbnf128.rs deleted file mode 100644 index c1d2b4855856..000000000000 --- a/library/compiler-builtins/libm/src/math/scalbnf128.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn scalbnf128(x: f128, n: i32) -> f128 { - super::generic::scalbn(x, n) -} diff --git a/library/compiler-builtins/libm/src/math/scalbnf16.rs b/library/compiler-builtins/libm/src/math/scalbnf16.rs deleted file mode 100644 index 2209e1a17956..000000000000 --- a/library/compiler-builtins/libm/src/math/scalbnf16.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn scalbnf16(x: f16, n: i32) -> f16 { - super::generic::scalbn(x, n) -} diff --git a/library/compiler-builtins/libm/src/math/sqrtf.rs b/library/compiler-builtins/libm/src/math/sqrtf.rs deleted file mode 100644 index c28a705e378e..000000000000 --- a/library/compiler-builtins/libm/src/math/sqrtf.rs +++ /dev/null @@ -1,15 +0,0 @@ -/// The square root of `x` (f32). -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sqrtf(x: f32) -> f32 { - select_implementation! { - name: sqrtf, - use_arch: any( - all(target_arch = "aarch64", target_feature = "neon"), - all(target_arch = "wasm32", intrinsics_enabled), - target_feature = "sse2" - ), - args: x, - } - - super::generic::sqrt(x) -} diff --git a/library/compiler-builtins/libm/src/math/sqrtf128.rs b/library/compiler-builtins/libm/src/math/sqrtf128.rs deleted file mode 100644 index eaef6ae0c1c8..000000000000 --- a/library/compiler-builtins/libm/src/math/sqrtf128.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// The square root of `x` (f128). -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sqrtf128(x: f128) -> f128 { - return super::generic::sqrt(x); -} diff --git a/library/compiler-builtins/libm/src/math/sqrtf16.rs b/library/compiler-builtins/libm/src/math/sqrtf16.rs deleted file mode 100644 index 7bedb7f8bbb6..000000000000 --- a/library/compiler-builtins/libm/src/math/sqrtf16.rs +++ /dev/null @@ -1,11 +0,0 @@ -/// The square root of `x` (f16). -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn sqrtf16(x: f16) -> f16 { - select_implementation! { - name: sqrtf16, - use_arch: all(target_arch = "aarch64", target_feature = "fp16"), - args: x, - } - - return super::generic::sqrt(x); -} diff --git a/library/compiler-builtins/libm/src/math/truncf.rs b/library/compiler-builtins/libm/src/math/truncf.rs deleted file mode 100644 index 14533a267063..000000000000 --- a/library/compiler-builtins/libm/src/math/truncf.rs +++ /dev/null @@ -1,23 +0,0 @@ -/// Rounds the number toward 0 to the closest integral value (f32). -/// -/// This effectively removes the decimal part of the number, leaving the integral part. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn truncf(x: f32) -> f32 { - select_implementation! { - name: truncf, - use_arch: all(target_arch = "wasm32", intrinsics_enabled), - args: x, - } - - super::generic::trunc(x) -} - -// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 -#[cfg(not(target_arch = "powerpc64"))] -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::truncf(1.1), 1.0); - } -} diff --git a/library/compiler-builtins/libm/src/math/truncf128.rs b/library/compiler-builtins/libm/src/math/truncf128.rs deleted file mode 100644 index 9dccc0d0e9d7..000000000000 --- a/library/compiler-builtins/libm/src/math/truncf128.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// Rounds the number toward 0 to the closest integral value (f128). -/// -/// This effectively removes the decimal part of the number, leaving the integral part. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn truncf128(x: f128) -> f128 { - super::generic::trunc(x) -} diff --git a/library/compiler-builtins/libm/src/math/truncf16.rs b/library/compiler-builtins/libm/src/math/truncf16.rs deleted file mode 100644 index d7c3d225cf9b..000000000000 --- a/library/compiler-builtins/libm/src/math/truncf16.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// Rounds the number toward 0 to the closest integral value (f16). -/// -/// This effectively removes the decimal part of the number, leaving the integral part. -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn truncf16(x: f16) -> f16 { - super::generic::trunc(x) -} From 4c264c96ae30e45c503fbb24e452ea948c5229af Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 23 May 2025 17:26:39 +0000 Subject: [PATCH 04/59] Update `CmpResult` to use a pointer-sized return type As seen at [1], LLVM uses `long long` on LLP64 (to get a 64-bit integer matching pointer size) and `long` on everything else, with exceptions for AArch64 and AVR. Our current logic always uses an `i32`. This happens to work because LLVM uses 32-bit instructions to check the output on x86-64, but the GCC checks the full 64-bit register so garbage in the upper half leads to incorrect results. Update our return type to be `isize`, with exceptions for AArch64 and AVR. Fixes: https://github.com/rust-lang/compiler-builtins/issues/919 [1]: https://github.com/llvm/llvm-project/blob/0cf3c437c18ed27d9663d87804a9a15ff6874af2/compiler-rt/lib/builtins/fp_compare_impl.inc#L11-L27 --- .../builtins-test/benches/float_cmp.rs | 43 ++++++++++++------- .../builtins-test/src/bench.rs | 4 +- .../compiler-builtins/src/float/cmp.rs | 23 +++++++--- .../libm/src/math/support/mod.rs | 2 + 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/library/compiler-builtins/builtins-test/benches/float_cmp.rs b/library/compiler-builtins/builtins-test/benches/float_cmp.rs index 42d6652397dc..87a89efb5a4f 100644 --- a/library/compiler-builtins/builtins-test/benches/float_cmp.rs +++ b/library/compiler-builtins/builtins-test/benches/float_cmp.rs @@ -1,12 +1,23 @@ #![cfg_attr(f128_enabled, feature(f128))] use builtins_test::float_bench; -use compiler_builtins::float::cmp; +use compiler_builtins::float::cmp::{self, CmpResult}; use criterion::{Criterion, criterion_main}; /// `gt` symbols are allowed to return differing results, they just get compared /// to 0. -fn gt_res_eq(a: i32, b: i32) -> bool { +fn gt_res_eq(mut a: CmpResult, mut b: CmpResult) -> bool { + // FIXME: Our CmpResult used to be `i32`, but GCC/LLVM expect `isize`. on 64-bit platforms, + // this means the top half of the word may be garbage if built with an old version of + // `compiler-builtins`, so add a hack around this. + // + // This can be removed once a version of `compiler-builtins` with the return type fix makes + // it upstream. + if size_of::() == 8 { + a = a as i32 as CmpResult; + b = b as i32 as CmpResult; + } + let a_lt_0 = a <= 0; let b_lt_0 = b <= 0; (a_lt_0 && b_lt_0) || (!a_lt_0 && !b_lt_0) @@ -14,14 +25,14 @@ fn gt_res_eq(a: i32, b: i32) -> bool { float_bench! { name: cmp_f32_gt, - sig: (a: f32, b: f32) -> i32, + sig: (a: f32, b: f32) -> CmpResult, crate_fn: cmp::__gtsf2, sys_fn: __gtsf2, sys_available: all(), output_eq: gt_res_eq, asm: [ #[cfg(target_arch = "x86_64")] { - let ret: i32; + let ret: CmpResult; asm!( "xor {ret:e}, {ret:e}", "ucomiss {a}, {b}", @@ -36,7 +47,7 @@ float_bench! { }; #[cfg(target_arch = "aarch64")] { - let ret: i32; + let ret: CmpResult; asm!( "fcmp {a:s}, {b:s}", "cset {ret:w}, gt", @@ -53,13 +64,13 @@ float_bench! { float_bench! { name: cmp_f32_unord, - sig: (a: f32, b: f32) -> i32, + sig: (a: f32, b: f32) -> CmpResult, crate_fn: cmp::__unordsf2, sys_fn: __unordsf2, sys_available: all(), asm: [ #[cfg(target_arch = "x86_64")] { - let ret: i32; + let ret: CmpResult; asm!( "xor {ret:e}, {ret:e}", "ucomiss {a}, {b}", @@ -74,7 +85,7 @@ float_bench! { }; #[cfg(target_arch = "aarch64")] { - let ret: i32; + let ret: CmpResult; asm!( "fcmp {a:s}, {b:s}", "cset {ret:w}, vs", @@ -91,14 +102,14 @@ float_bench! { float_bench! { name: cmp_f64_gt, - sig: (a: f64, b: f64) -> i32, + sig: (a: f64, b: f64) -> CmpResult, crate_fn: cmp::__gtdf2, sys_fn: __gtdf2, sys_available: all(), output_eq: gt_res_eq, asm: [ #[cfg(target_arch = "x86_64")] { - let ret: i32; + let ret: CmpResult; asm!( "xor {ret:e}, {ret:e}", "ucomisd {a}, {b}", @@ -113,7 +124,7 @@ float_bench! { }; #[cfg(target_arch = "aarch64")] { - let ret: i32; + let ret: CmpResult; asm!( "fcmp {a:d}, {b:d}", "cset {ret:w}, gt", @@ -130,13 +141,13 @@ float_bench! { float_bench! { name: cmp_f64_unord, - sig: (a: f64, b: f64) -> i32, + sig: (a: f64, b: f64) -> CmpResult, crate_fn: cmp::__unorddf2, sys_fn: __unorddf2, sys_available: all(), asm: [ #[cfg(target_arch = "x86_64")] { - let ret: i32; + let ret: CmpResult; asm!( "xor {ret:e}, {ret:e}", "ucomisd {a}, {b}", @@ -151,7 +162,7 @@ float_bench! { }; #[cfg(target_arch = "aarch64")] { - let ret: i32; + let ret: CmpResult; asm!( "fcmp {a:d}, {b:d}", "cset {ret:w}, vs", @@ -168,7 +179,7 @@ float_bench! { float_bench! { name: cmp_f128_gt, - sig: (a: f128, b: f128) -> i32, + sig: (a: f128, b: f128) -> CmpResult, crate_fn: cmp::__gttf2, crate_fn_ppc: cmp::__gtkf2, sys_fn: __gttf2, @@ -180,7 +191,7 @@ float_bench! { float_bench! { name: cmp_f128_unord, - sig: (a: f128, b: f128) -> i32, + sig: (a: f128, b: f128) -> CmpResult, crate_fn: cmp::__unordtf2, crate_fn_ppc: cmp::__unordkf2, sys_fn: __unordtf2, diff --git a/library/compiler-builtins/builtins-test/src/bench.rs b/library/compiler-builtins/builtins-test/src/bench.rs index 2348f6bc9737..0987185670ee 100644 --- a/library/compiler-builtins/builtins-test/src/bench.rs +++ b/library/compiler-builtins/builtins-test/src/bench.rs @@ -358,8 +358,8 @@ impl_testio!(float f16); impl_testio!(float f32, f64); #[cfg(f128_enabled)] impl_testio!(float f128); -impl_testio!(int i16, i32, i64, i128); -impl_testio!(int u16, u32, u64, u128); +impl_testio!(int i8, i16, i32, i64, i128, isize); +impl_testio!(int u8, u16, u32, u64, u128, usize); impl_testio!((float, int)(f32, i32)); impl_testio!((float, int)(f64, i32)); #[cfg(f128_enabled)] diff --git a/library/compiler-builtins/compiler-builtins/src/float/cmp.rs b/library/compiler-builtins/compiler-builtins/src/float/cmp.rs index 296952821cb4..f1e54dc1c83d 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/cmp.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/cmp.rs @@ -2,14 +2,23 @@ use crate::float::Float; use crate::int::MinInt; +use crate::support::cfg_if; -// https://github.com/llvm/llvm-project/blob/1e6ba3cd2fe96be00b6ed6ba28b3d9f9271d784d/compiler-rt/lib/builtins/fp_compare_impl.inc#L22 -#[cfg(target_arch = "avr")] -pub type CmpResult = i8; - -// https://github.com/llvm/llvm-project/blob/1e6ba3cd2fe96be00b6ed6ba28b3d9f9271d784d/compiler-rt/lib/builtins/fp_compare_impl.inc#L25 -#[cfg(not(target_arch = "avr"))] -pub type CmpResult = i32; +// Taken from LLVM config: +// https://github.com/llvm/llvm-project/blob/0cf3c437c18ed27d9663d87804a9a15ff6874af2/compiler-rt/lib/builtins/fp_compare_impl.inc#L11-L27 +cfg_if! { + if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { + // Aarch64 uses `int` rather than a pointer-sized value. + pub type CmpResult = i32; + } else if #[cfg(target_arch = "avr")] { + // AVR uses a single byte. + pub type CmpResult = i8; + } else { + // In compiler-rt, LLP64 ABIs use `long long` and everything else uses `long`. In effect, + // this means the return value is always pointer-sized. + pub type CmpResult = isize; + } +} #[derive(Clone, Copy)] enum Result { diff --git a/library/compiler-builtins/libm/src/math/support/mod.rs b/library/compiler-builtins/libm/src/math/support/mod.rs index a4f596ab8442..2771cfd32c8b 100644 --- a/library/compiler-builtins/libm/src/math/support/mod.rs +++ b/library/compiler-builtins/libm/src/math/support/mod.rs @@ -11,6 +11,8 @@ mod int_traits; #[allow(unused_imports)] pub use big::{i256, u256}; +#[allow(unused_imports)] +pub(crate) use cfg_if; pub use env::{FpResult, Round, Status}; #[allow(unused_imports)] pub use float_traits::{DFloat, Float, HFloat, IntTy}; From 2b0797aef509e4e245128ff24277f72da23b133f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 May 2025 16:20:32 +0200 Subject: [PATCH 05/59] UnsafePinned: also include the effects of UnsafeCell --- library/core/src/pin/unsafe_pinned.rs | 33 +++++++++------------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/library/core/src/pin/unsafe_pinned.rs b/library/core/src/pin/unsafe_pinned.rs index f65e83662fef..dbcceb807aba 100644 --- a/library/core/src/pin/unsafe_pinned.rs +++ b/library/core/src/pin/unsafe_pinned.rs @@ -1,10 +1,13 @@ +use crate::cell::UnsafeCell; use crate::marker::{PointerLike, Unpin}; use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::pin::Pin; use crate::{fmt, ptr}; -/// This type provides a way to opt-out of typical aliasing rules; +/// This type provides a way to entirely opt-out of typical aliasing rules; /// specifically, `&mut UnsafePinned` is not guaranteed to be a unique pointer. +/// This also subsumes the effects of `UnsafeCell`, i.e., `&UnsafePinned` may point to data +/// that is being mutated. /// /// However, even if you define your type like `pub struct Wrapper(UnsafePinned<...>)`, it is still /// very risky to have an `&mut Wrapper` that aliases anything else. Many functions that work @@ -17,38 +20,24 @@ use crate::{fmt, ptr}; /// the public API of a library. It is an internal implementation detail of libraries that need to /// support aliasing mutable references. /// -/// Further note that this does *not* lift the requirement that shared references must be read-only! -/// Use `UnsafeCell` for that. -/// /// This type blocks niches the same way `UnsafeCell` does. #[lang = "unsafe_pinned"] #[repr(transparent)] #[unstable(feature = "unsafe_pinned", issue = "125735")] pub struct UnsafePinned { - value: T, + value: UnsafeCell, } +// Override the manual `!Sync` in `UnsafeCell`. +#[unstable(feature = "unsafe_pinned", issue = "125735")] +unsafe impl Sync for UnsafePinned {} + /// When this type is used, that almost certainly means safe APIs need to use pinning to avoid the /// aliases from becoming invalidated. Therefore let's mark this as `!Unpin`. You can always opt /// back in to `Unpin` with an `impl` block, provided your API is still sound while unpinned. #[unstable(feature = "unsafe_pinned", issue = "125735")] impl !Unpin for UnsafePinned {} -/// The type is `Copy` when `T` is to avoid people assuming that `Copy` implies there is no -/// `UnsafePinned` anywhere. (This is an issue with `UnsafeCell`: people use `Copy` bounds to mean -/// `Freeze`.) Given that there is no `unsafe impl Copy for ...`, this is also the option that -/// leaves the user more choices (as they can always wrap this in a `!Copy` type). -// FIXME(unsafe_pinned): this may be unsound or a footgun? -#[unstable(feature = "unsafe_pinned", issue = "125735")] -impl Copy for UnsafePinned {} - -#[unstable(feature = "unsafe_pinned", issue = "125735")] -impl Clone for UnsafePinned { - fn clone(&self) -> Self { - *self - } -} - // `Send` and `Sync` are inherited from `T`. This is similar to `SyncUnsafeCell`, since // we eventually concluded that `UnsafeCell` implicitly making things `!Sync` is sometimes // unergonomic. A type that needs to be `!Send`/`!Sync` should really have an explicit @@ -63,7 +52,7 @@ impl UnsafePinned { #[must_use] #[unstable(feature = "unsafe_pinned", issue = "125735")] pub const fn new(value: T) -> Self { - UnsafePinned { value } + UnsafePinned { value: UnsafeCell::new(value) } } /// Unwraps the value, consuming this `UnsafePinned`. @@ -72,7 +61,7 @@ impl UnsafePinned { #[unstable(feature = "unsafe_pinned", issue = "125735")] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] pub const fn into_inner(self) -> T { - self.value + self.value.into_inner() } } From 259d6f42179a9e0da3f2db1a088a290124eeb7c1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 May 2025 16:44:50 +0200 Subject: [PATCH 06/59] add Miri tests --- .../miri/tests/fail/async-shared-mutable.rs | 25 ++++++++++ .../fail/async-shared-mutable.stack.stderr | 43 +++++++++++++++++ .../fail/async-shared-mutable.tree.stderr | 47 +++++++++++++++++++ .../tests/pass/both_borrows/unsafe_pinned.rs | 16 +++++++ 4 files changed, 131 insertions(+) create mode 100644 src/tools/miri/tests/fail/async-shared-mutable.rs create mode 100644 src/tools/miri/tests/fail/async-shared-mutable.stack.stderr create mode 100644 src/tools/miri/tests/fail/async-shared-mutable.tree.stderr create mode 100644 src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs diff --git a/src/tools/miri/tests/fail/async-shared-mutable.rs b/src/tools/miri/tests/fail/async-shared-mutable.rs new file mode 100644 index 000000000000..62780e7a11c9 --- /dev/null +++ b/src/tools/miri/tests/fail/async-shared-mutable.rs @@ -0,0 +1,25 @@ +//! FIXME: This test should pass! However, `async fn` does not yet use `UnsafePinned`. +//! This is a regression test for : +//! `UnsafePinned` must include the effects of `UnsafeCell`. +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +//@normalize-stderr-test: "\[0x[a-fx\d.]+\]" -> "[OFFSET]" + +use core::future::Future; +use core::pin::{Pin, pin}; +use core::task::{Context, Poll, Waker}; + +fn main() { + let mut f = pin!(async move { + let x = &mut 0u8; + core::future::poll_fn(move |_| { + *x = 1; //~ERROR: write access + Poll::<()>::Pending + }) + .await + }); + let mut cx = Context::from_waker(&Waker::noop()); + assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending); + let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`. + assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending); +} diff --git a/src/tools/miri/tests/fail/async-shared-mutable.stack.stderr b/src/tools/miri/tests/fail/async-shared-mutable.stack.stderr new file mode 100644 index 000000000000..8f53a55cc3ee --- /dev/null +++ b/src/tools/miri/tests/fail/async-shared-mutable.stack.stderr @@ -0,0 +1,43 @@ +error: Undefined Behavior: attempting a write access using at ALLOC[OFFSET], but that tag does not exist in the borrow stack for this location + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | *x = 1; + | ^^^^^^ + | | + | attempting a write access using at ALLOC[OFFSET], but that tag does not exist in the borrow stack for this location + | this error occurs as part of an access at ALLOC[OFFSET] + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created by a Unique retag at offsets [OFFSET] + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | / core::future::poll_fn(move |_| { +LL | | *x = 1; +LL | | Poll::<()>::Pending +LL | | }) +LL | | .await + | |______________^ +help: was later invalidated at offsets [OFFSET] by a SharedReadOnly retag + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`. + | ^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside closure at tests/fail/async-shared-mutable.rs:LL:CC + = note: inside ` as std::future::Future>::poll` at RUSTLIB/core/src/future/poll_fn.rs:LL:CC +note: inside closure + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | .await + | ^^^^^ +note: inside `main` + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +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/fail/async-shared-mutable.tree.stderr b/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr new file mode 100644 index 000000000000..d1e66a0d043f --- /dev/null +++ b/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr @@ -0,0 +1,47 @@ +error: Undefined Behavior: write access through at ALLOC[OFFSET] is forbidden + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | *x = 1; + | ^^^^^^ write access through at ALLOC[OFFSET] is forbidden + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: the accessed tag has state Frozen which forbids this child write access +help: the accessed tag was created here, in the initial state Reserved + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | / core::future::poll_fn(move |_| { +LL | | *x = 1; +LL | | Poll::<()>::Pending +LL | | }) +LL | | .await + | |______________^ +help: the accessed tag later transitioned to Active due to a child write access at offsets [OFFSET] + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | *x = 1; + | ^^^^^^ + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference +help: the accessed tag later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [OFFSET] + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`. + | ^^^^^^^^^^ + = help: this transition corresponds to a loss of write permissions + = note: BACKTRACE (of the first span): + = note: inside closure at tests/fail/async-shared-mutable.rs:LL:CC + = note: inside ` as std::future::Future>::poll` at RUSTLIB/core/src/future/poll_fn.rs:LL:CC +note: inside closure + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | .await + | ^^^^^ +note: inside `main` + --> tests/fail/async-shared-mutable.rs:LL:CC + | +LL | assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +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/both_borrows/unsafe_pinned.rs b/src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs new file mode 100644 index 000000000000..0c75a07bfa2a --- /dev/null +++ b/src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs @@ -0,0 +1,16 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +#![feature(unsafe_pinned)] + +use std::pin::UnsafePinned; + +fn mutate(x: &UnsafePinned) { + let ptr = x as *const _ as *mut i32; + unsafe { ptr.write(42) }; +} + +fn main() { + let x = UnsafePinned::new(0); + mutate(&x); + assert_eq!(x.into_inner(), 42); +} From c04f133858c3208525ae8fe47057fb157c18d606 Mon Sep 17 00:00:00 2001 From: Dario Damiani <154735680+D-Dario0@users.noreply.github.com> Date: Wed, 28 May 2025 20:48:05 +0200 Subject: [PATCH 07/59] Typo in README.md Link to Apache License changed from htps:// to https:// --- library/compiler-builtins/libm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/compiler-builtins/libm/README.md b/library/compiler-builtins/libm/README.md index 349e892dfcf9..77608db3d0d7 100644 --- a/library/compiler-builtins/libm/README.md +++ b/library/compiler-builtins/libm/README.md @@ -34,7 +34,7 @@ Usage is under the MIT license, available at ### Contribution Contributions are licensed under both the MIT license and the Apache License, -Version 2.0, available at . Unless +Version 2.0, available at . Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as mentioned, without any additional terms or conditions. From 5978b8b875f6c53af9ca4c72f4ea5fcf74d55c84 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 28 May 2025 21:08:41 +0000 Subject: [PATCH 08/59] aarch64: Add a note saying why we use `frintx` rather than `frintn` --- library/compiler-builtins/libm/src/math/arch/aarch64.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/compiler-builtins/libm/src/math/arch/aarch64.rs b/library/compiler-builtins/libm/src/math/arch/aarch64.rs index 020bb731cdc7..8896804b5040 100644 --- a/library/compiler-builtins/libm/src/math/arch/aarch64.rs +++ b/library/compiler-builtins/libm/src/math/arch/aarch64.rs @@ -30,6 +30,12 @@ pub fn fmaf(mut x: f32, y: f32, z: f32) -> f32 { x } +// NB: `frintx` is technically the correct instruction for C's `rint`. However, in Rust (and LLVM +// by default), `rint` is identical to `roundeven` (no fpenv interaction) so we use the +// side-effect-free `frintn`. +// +// In general, C code that calls Rust's libm should assume that fpenv is ignored. + pub fn rint(mut x: f64) -> f64 { // SAFETY: `frintn` is available with neon and has no side effects. // From 2c9fb22f040094b4cb982c84254bb5d0589c38e8 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Wed, 28 May 2025 21:16:15 +0000 Subject: [PATCH 09/59] jsondoclint: Extract `Command`/`CommandKind` into its own file --- src/tools/jsondocck/src/directive.rs | 230 +++++++++++++++++++++++++++ src/tools/jsondocck/src/main.rs | 225 +------------------------- 2 files changed, 233 insertions(+), 222 deletions(-) create mode 100644 src/tools/jsondocck/src/directive.rs diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs new file mode 100644 index 000000000000..8617379ed270 --- /dev/null +++ b/src/tools/jsondocck/src/directive.rs @@ -0,0 +1,230 @@ +use std::borrow::Cow; +use serde_json::Value; +use crate::cache::Cache; + +#[derive(Debug)] +pub struct Command { + pub kind: CommandKind, + pub path: String, + pub lineno: usize, +} + +#[derive(Debug)] +pub enum CommandKind { + /// `//@ has ` + /// + /// Checks the path exists. + HasPath, + + /// `//@ has ` + /// + /// Check one thing at the path is equal to the value. + HasValue { value: String }, + + /// `//@ !has ` + /// + /// Checks the path doesn't exist. + HasNotPath, + + /// `//@ !has ` + /// + /// Checks the path exists, but doesn't have the given value. + HasNotValue { value: String }, + + /// `//@ is ` + /// + /// Check the path is the given value. + Is { value: String }, + + /// `//@ is ...` + /// + /// Check that the path matches to exactly every given value. + IsMany { values: Vec }, + + /// `//@ !is ` + /// + /// Check the path isn't the given value. + IsNot { value: String }, + + /// `//@ count ` + /// + /// Check the path has the expected number of matches. + CountIs { expected: usize }, + + /// `//@ set = ` + Set { variable: String }, +} + +impl CommandKind { + /// Returns both the kind and the path. + /// + /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest). + pub fn parse<'a>( + command_name: &str, + negated: bool, + args: &'a [String], + ) -> Option<(Self, &'a str)> { + let kind = match (command_name, negated) { + ("count", false) => { + assert_eq!(args.len(), 2); + let expected = args[1].parse().expect("invalid number for `count`"); + Self::CountIs { expected } + } + + ("ismany", false) => { + // FIXME: Make this >= 3, and migrate len(values)==1 cases to @is + assert!(args.len() >= 2, "Not enough args to `ismany`"); + let values = args[1..].to_owned(); + Self::IsMany { values } + } + + ("is", false) => { + assert_eq!(args.len(), 2); + Self::Is { value: args[1].clone() } + } + ("is", true) => { + assert_eq!(args.len(), 2); + Self::IsNot { value: args[1].clone() } + } + + ("set", false) => { + assert_eq!(args.len(), 3); + assert_eq!(args[1], "="); + return Some((Self::Set { variable: args[0].clone() }, &args[2])); + } + + ("has", false) => match args { + [_path] => Self::HasPath, + [_path, value] => Self::HasValue { value: value.clone() }, + _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"), + }, + ("has", true) => match args { + [_path] => Self::HasNotPath, + [_path, value] => Self::HasNotValue { value: value.clone() }, + _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), + }, + + (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { + return None; + } + _ => { + panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" }) + } + }; + + Some((kind, &args[0])) + } +} + +impl Command { + /// Performs the actual work of ensuring a command passes. + pub fn check(&self, cache: &mut Cache) -> Result<(), String> { + let matches = cache.select(&self.path); + match &self.kind { + CommandKind::HasPath => { + if matches.is_empty() { + return Err("matched to no values".to_owned()); + } + } + CommandKind::HasNotPath => { + if !matches.is_empty() { + return Err(format!("matched to {matches:?}, but wanted no matches")); + } + } + CommandKind::HasValue { value } => { + let want_value = string_to_value(value, cache); + if !matches.contains(&want_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which didn't contain {want_value:?}" + )); + } + } + CommandKind::HasNotValue { value } => { + let wantnt_value = string_to_value(value, cache); + if matches.contains(&wantnt_value.as_ref()) { + return Err(format!( + "matched to {matches:?}, which contains unwanted {wantnt_value:?}" + )); + } else if matches.is_empty() { + return Err(format!( + "got no matches, but expected some matched (not containing {wantnt_value:?}" + )); + } + } + + CommandKind::Is { value } => { + let want_value = string_to_value(value, cache); + let matched = get_one(&matches)?; + if matched != want_value.as_ref() { + return Err(format!("matched to {matched:?} but want {want_value:?}")); + } + } + CommandKind::IsNot { value } => { + let wantnt_value = string_to_value(value, cache); + let matched = get_one(&matches)?; + if matched == wantnt_value.as_ref() { + return Err(format!("got value {wantnt_value:?}, but want anything else")); + } + } + + CommandKind::IsMany { values } => { + // Serde json doesn't implement Ord or Hash for Value, so we must + // use a Vec here. While in theory that makes setwize equality + // O(n^2), in practice n will never be large enough to matter. + let expected_values = + values.iter().map(|v| string_to_value(v, cache)).collect::>(); + if expected_values.len() != matches.len() { + return Err(format!( + "Expected {} values, but matched to {} values ({:?})", + expected_values.len(), + matches.len(), + matches + )); + }; + for got_value in matches { + if !expected_values.iter().any(|exp| &**exp == got_value) { + return Err(format!("has match {got_value:?}, which was not expected",)); + } + } + } + CommandKind::CountIs { expected } => { + if *expected != matches.len() { + return Err(format!( + "matched to `{matches:?}` with length {}, but expected length {expected}", + matches.len(), + )); + } + } + CommandKind::Set { variable } => { + let value = get_one(&matches)?; + let r = cache.variables.insert(variable.to_owned(), value.clone()); + assert!(r.is_none(), "name collision: {variable:?} is duplicated"); + } + } + + Ok(()) + } +} + +fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> { + match matches { + [] => Err("matched to no values".to_owned()), + [matched] => Ok(matched), + _ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")), + } +} + +// FIXME: This setup is temporary until we figure out how to improve this situation. +// See . +include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs")); + +fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { + if s.starts_with("$") { + Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| { + // FIXME(adotinthevoid): Show line number + panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables) + })) + } else { + Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) + } +} \ No newline at end of file diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 65ad38da98b0..80557b682430 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -1,17 +1,17 @@ -use std::borrow::Cow; use std::process::ExitCode; use std::sync::LazyLock; use std::{env, fs}; use regex::{Regex, RegexBuilder}; -use serde_json::Value; mod cache; mod config; +mod directive; mod error; use cache::Cache; use config::parse_config; +use directive::{Command, CommandKind}; use error::CkError; fn main() -> ExitCode { @@ -25,7 +25,7 @@ fn main() -> ExitCode { }; for command in commands { - if let Err(message) = check_command(&command, &mut cache) { + if let Err(message) = command.check( &mut cache) { failed.push(CkError { command, message }); } } @@ -41,116 +41,6 @@ fn main() -> ExitCode { } } -#[derive(Debug)] -pub struct Command { - kind: CommandKind, - path: String, - lineno: usize, -} - -#[derive(Debug)] -enum CommandKind { - /// `//@ has ` - /// - /// Checks the path exists. - HasPath, - - /// `//@ has ` - /// - /// Check one thing at the path is equal to the value. - HasValue { value: String }, - - /// `//@ !has ` - /// - /// Checks the path doesn't exist. - HasNotPath, - - /// `//@ !has ` - /// - /// Checks the path exists, but doesn't have the given value. - HasNotValue { value: String }, - - /// `//@ is ` - /// - /// Check the path is the given value. - Is { value: String }, - - /// `//@ is ...` - /// - /// Check that the path matches to exactly every given value. - IsMany { values: Vec }, - - /// `//@ !is ` - /// - /// Check the path isn't the given value. - IsNot { value: String }, - - /// `//@ count ` - /// - /// Check the path has the expected number of matches. - CountIs { expected: usize }, - - /// `//@ set = ` - Set { variable: String }, -} - -impl CommandKind { - /// Returns both the kind and the path. - /// - /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest). - fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> { - let kind = match (command_name, negated) { - ("count", false) => { - assert_eq!(args.len(), 2); - let expected = args[1].parse().expect("invalid number for `count`"); - Self::CountIs { expected } - } - - ("ismany", false) => { - // FIXME: Make this >= 3, and migrate len(values)==1 cases to @is - assert!(args.len() >= 2, "Not enough args to `ismany`"); - let values = args[1..].to_owned(); - Self::IsMany { values } - } - - ("is", false) => { - assert_eq!(args.len(), 2); - Self::Is { value: args[1].clone() } - } - ("is", true) => { - assert_eq!(args.len(), 2); - Self::IsNot { value: args[1].clone() } - } - - ("set", false) => { - assert_eq!(args.len(), 3); - assert_eq!(args[1], "="); - return Some((Self::Set { variable: args[0].clone() }, &args[2])); - } - - ("has", false) => match args { - [_path] => Self::HasPath, - [_path, value] => Self::HasValue { value: value.clone() }, - _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"), - }, - ("has", true) => match args { - [_path] => Self::HasNotPath, - [_path, value] => Self::HasNotValue { value: value.clone() }, - _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), - }, - - (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { - return None; - } - _ => { - panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" }) - } - }; - - Some((kind, &args[0])) - } -} - static LINE_PATTERN: LazyLock = LazyLock::new(|| { RegexBuilder::new( r#" @@ -183,10 +73,6 @@ fn print_err(msg: &str, lineno: usize) { eprintln!("Invalid command: {} on line {}", msg, lineno) } -// FIXME: This setup is temporary until we figure out how to improve this situation. -// See . -include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs")); - /// Get a list of commands from a file. fn get_commands(template: &str) -> Result, ()> { let mut commands = Vec::new(); @@ -222,108 +108,3 @@ fn get_commands(template: &str) -> Result, ()> { if !errors { Ok(commands) } else { Err(()) } } - -/// Performs the actual work of ensuring a command passes. -fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> { - let matches = cache.select(&command.path); - match &command.kind { - CommandKind::HasPath => { - if matches.is_empty() { - return Err("matched to no values".to_owned()); - } - } - CommandKind::HasNotPath => { - if !matches.is_empty() { - return Err(format!("matched to {matches:?}, but wanted no matches")); - } - } - CommandKind::HasValue { value } => { - let want_value = string_to_value(value, cache); - if !matches.contains(&want_value.as_ref()) { - return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}")); - } - } - CommandKind::HasNotValue { value } => { - let wantnt_value = string_to_value(value, cache); - if matches.contains(&wantnt_value.as_ref()) { - return Err(format!( - "matched to {matches:?}, which contains unwanted {wantnt_value:?}" - )); - } else if matches.is_empty() { - return Err(format!( - "got no matches, but expected some matched (not containing {wantnt_value:?}" - )); - } - } - - CommandKind::Is { value } => { - let want_value = string_to_value(value, cache); - let matched = get_one(&matches)?; - if matched != want_value.as_ref() { - return Err(format!("matched to {matched:?} but want {want_value:?}")); - } - } - CommandKind::IsNot { value } => { - let wantnt_value = string_to_value(value, cache); - let matched = get_one(&matches)?; - if matched == wantnt_value.as_ref() { - return Err(format!("got value {wantnt_value:?}, but want anything else")); - } - } - - CommandKind::IsMany { values } => { - // Serde json doesn't implement Ord or Hash for Value, so we must - // use a Vec here. While in theory that makes setwize equality - // O(n^2), in practice n will never be large enough to matter. - let expected_values = - values.iter().map(|v| string_to_value(v, cache)).collect::>(); - if expected_values.len() != matches.len() { - return Err(format!( - "Expected {} values, but matched to {} values ({:?})", - expected_values.len(), - matches.len(), - matches - )); - }; - for got_value in matches { - if !expected_values.iter().any(|exp| &**exp == got_value) { - return Err(format!("has match {got_value:?}, which was not expected",)); - } - } - } - CommandKind::CountIs { expected } => { - if *expected != matches.len() { - return Err(format!( - "matched to `{matches:?}` with length {}, but expected length {expected}", - matches.len(), - )); - } - } - CommandKind::Set { variable } => { - let value = get_one(&matches)?; - let r = cache.variables.insert(variable.to_owned(), value.clone()); - assert!(r.is_none(), "name collision: {variable:?} is duplicated"); - } - } - - Ok(()) -} - -fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> { - match matches { - [] => Err("matched to no values".to_owned()), - [matched] => Ok(matched), - _ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")), - } -} - -fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { - if s.starts_with("$") { - Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| { - // FIXME(adotinthevoid): Show line number - panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables) - })) - } else { - Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) - } -} From 851aa05aa0addf41507967952f1ac2d183751682 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 21 Apr 2025 09:35:55 +0000 Subject: [PATCH 10/59] cleanup: Reuse `MinInt` and `Int` from `libm` in `compiler-builtins` Since the two crates are now in the same repo, it is easier to share code. Begin some deduplication with the integer traits. --- .../builtins-test/src/lib.rs | 78 ++++- .../compiler-builtins/src/float/add.rs | 22 +- .../compiler-builtins/src/float/conv.rs | 24 +- .../compiler-builtins/src/float/div.rs | 2 +- .../compiler-builtins/src/float/mul.rs | 2 +- .../compiler-builtins/src/float/traits.rs | 4 +- .../compiler-builtins/src/int/addsub.rs | 6 +- .../compiler-builtins/src/int/big.rs | 4 +- .../src/int/leading_zeros.rs | 64 ++-- .../src/int/trailing_zeros.rs | 25 +- .../compiler-builtins/src/int/traits.rs | 273 +----------------- .../libm/src/math/support/int_traits.rs | 9 + 12 files changed, 168 insertions(+), 345 deletions(-) diff --git a/library/compiler-builtins/builtins-test/src/lib.rs b/library/compiler-builtins/builtins-test/src/lib.rs index c596ac213807..f1673133be27 100644 --- a/library/compiler-builtins/builtins-test/src/lib.rs +++ b/library/compiler-builtins/builtins-test/src/lib.rs @@ -40,6 +40,75 @@ pub const N: u32 = if cfg!(target_arch = "x86_64") && !cfg!(debug_assertions) { 10_000 }; +/// Additional constants that determine how the integer gets fuzzed. +trait FuzzInt: MinInt { + /// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing + /// in `builtins-test`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96, + /// 111,112,119,120,125,126,127]. + const FUZZ_LENGTHS: [u8; 20] = make_fuzz_lengths(Self::BITS); + + /// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128. + const FUZZ_NUM: usize = { + let log2 = Self::BITS.ilog2() as usize; + if log2 == 3 { + // case for u8 + 6 + } else { + // 3 entries on each extreme, 2 in the middle, and 4 for each scale of intermediate + // boundaries. + 8 + (4 * (log2 - 4)) + } + }; +} + +impl FuzzInt for I where I: MinInt {} + +const fn make_fuzz_lengths(bits: u32) -> [u8; 20] { + let mut v = [0u8; 20]; + v[0] = 0; + v[1] = 1; + v[2] = 2; // important for parity and the iX::MIN case when reversed + let mut i = 3; + + // No need for any more until the byte boundary, because there should be no algorithms + // that are sensitive to anything not next to byte boundaries after 2. We also scale + // in powers of two, which is important to prevent u128 corner tests from getting too + // big. + let mut l = 8; + loop { + if l >= ((bits / 2) as u8) { + break; + } + // get both sides of the byte boundary + v[i] = l - 1; + i += 1; + v[i] = l; + i += 1; + l *= 2; + } + + if bits != 8 { + // add the lower side of the middle boundary + v[i] = ((bits / 2) - 1) as u8; + i += 1; + } + + // We do not want to jump directly from the Self::BITS/2 boundary to the Self::BITS + // boundary because of algorithms that split the high part up. We reverse the scaling + // as we go to Self::BITS. + let mid = i; + let mut j = 1; + loop { + v[i] = (bits as u8) - (v[mid - j]) - 1; + if j == mid { + break; + } + i += 1; + j += 1; + } + v +} + /// Random fuzzing step. When run several times, it results in excellent fuzzing entropy such as: /// 11110101010101011110111110011111 /// 10110101010100001011101011001010 @@ -92,10 +161,9 @@ fn fuzz_step(rng: &mut Xoshiro128StarStar, x: &mut I) { macro_rules! edge_cases { ($I:ident, $case:ident, $inner:block) => { for i0 in 0..$I::FUZZ_NUM { - let mask_lo = (!$I::UnsignedInt::ZERO).wrapping_shr($I::FUZZ_LENGTHS[i0] as u32); + let mask_lo = (!$I::Unsigned::ZERO).wrapping_shr($I::FUZZ_LENGTHS[i0] as u32); for i1 in i0..I::FUZZ_NUM { - let mask_hi = - (!$I::UnsignedInt::ZERO).wrapping_shl($I::FUZZ_LENGTHS[i1 - i0] as u32); + let mask_hi = (!$I::Unsigned::ZERO).wrapping_shl($I::FUZZ_LENGTHS[i1 - i0] as u32); let $case = I::from_unsigned(mask_lo & mask_hi); $inner } @@ -107,7 +175,7 @@ macro_rules! edge_cases { /// edge cases, followed by a more random fuzzer that runs `n` times. pub fn fuzz(n: u32, mut f: F) where - ::UnsignedInt: Int, + ::Unsigned: Int, { // edge case tester. Calls `f` 210 times for u128. // zero gets skipped by the loop @@ -128,7 +196,7 @@ where /// The same as `fuzz`, except `f` has two inputs. pub fn fuzz_2(n: u32, f: F) where - ::UnsignedInt: Int, + ::Unsigned: Int, { // Check cases where the first and second inputs are zero. Both call `f` 210 times for `u128`. edge_cases!(I, case, { diff --git a/library/compiler-builtins/compiler-builtins/src/float/add.rs b/library/compiler-builtins/compiler-builtins/src/float/add.rs index 0426c9cc44fb..43e3ae931ffa 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/add.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/add.rs @@ -1,5 +1,5 @@ use crate::float::Float; -use crate::int::{CastInto, Int, MinInt}; +use crate::int::{CastFrom, CastInto, Int, MinInt}; /// Returns `a + b` fn add(a: F, b: F) -> F @@ -12,7 +12,7 @@ where let one = F::Int::ONE; let zero = F::Int::ZERO; - let bits = F::BITS.cast(); + let bits: F::Int = F::BITS.cast(); let significand_bits = F::SIG_BITS; let max_exponent = F::EXP_SAT; @@ -115,9 +115,10 @@ where let align = a_exponent.wrapping_sub(b_exponent).cast(); if align != MinInt::ZERO { if align < bits { - let sticky = - F::Int::from_bool(b_significand << bits.wrapping_sub(align).cast() != MinInt::ZERO); - b_significand = (b_significand >> align.cast()) | sticky; + let sticky = F::Int::from_bool( + b_significand << u32::cast_from(bits.wrapping_sub(align)) != MinInt::ZERO, + ); + b_significand = (b_significand >> u32::cast_from(align)) | sticky; } else { b_significand = one; // sticky; b is known to be non-zero. } @@ -132,8 +133,8 @@ where // If partial cancellation occured, we need to left-shift the result // and adjust the exponent: if a_significand < implicit_bit << 3 { - let shift = - a_significand.leading_zeros() as i32 - (implicit_bit << 3).leading_zeros() as i32; + let shift = a_significand.leading_zeros() as i32 + - (implicit_bit << 3u32).leading_zeros() as i32; a_significand <<= shift; a_exponent -= shift; } @@ -159,9 +160,10 @@ where // Result is denormal before rounding; the exponent is zero and we // need to shift the significand. let shift = (1 - a_exponent).cast(); - let sticky = - F::Int::from_bool((a_significand << bits.wrapping_sub(shift).cast()) != MinInt::ZERO); - a_significand = (a_significand >> shift.cast()) | sticky; + let sticky = F::Int::from_bool( + (a_significand << u32::cast_from(bits.wrapping_sub(shift))) != MinInt::ZERO, + ); + a_significand = (a_significand >> u32::cast_from(shift)) | sticky; a_exponent = 0; } diff --git a/library/compiler-builtins/compiler-builtins/src/float/conv.rs b/library/compiler-builtins/compiler-builtins/src/float/conv.rs index f5427a11390f..9d732f2cdbc9 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/conv.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/conv.rs @@ -72,7 +72,7 @@ mod int_to_float { F: Float, I: Int, F::Int: CastFrom, - Conv: Fn(I::UnsignedInt) -> F::Int, + Conv: Fn(I::Unsigned) -> F::Int, { let sign_bit = F::Int::cast_from(i >> (I::BITS - 1)) << (F::BITS - 1); F::from_bits(conv(i.unsigned_abs()) | sign_bit) @@ -313,10 +313,10 @@ intrinsics! { fn float_to_unsigned_int(f: F) -> U where F: Float, - U: Int, + U: Int, F::Int: CastInto, F::Int: CastFrom, - F::Int: CastInto, + F::Int: CastInto, u32: CastFrom, { float_to_int_inner::(f.to_bits(), |i: U| i, || U::MAX) @@ -327,8 +327,8 @@ fn float_to_signed_int(f: F) -> I where F: Float, I: Int + Neg, - I::UnsignedInt: Int, - F::Int: CastInto, + I::Unsigned: Int, + F::Int: CastInto, F::Int: CastFrom, u32: CastFrom, { @@ -355,27 +355,27 @@ where I: Int, FnFoo: FnOnce(I) -> I, FnOob: FnOnce() -> I, - I::UnsignedInt: Int, - F::Int: CastInto, + I::Unsigned: Int, + F::Int: CastInto, F::Int: CastFrom, u32: CastFrom, { let int_max_exp = F::EXP_BIAS + I::MAX.ilog2() + 1; - let foobar = F::EXP_BIAS + I::UnsignedInt::BITS - 1; + let foobar = F::EXP_BIAS + I::Unsigned::BITS - 1; if fbits < F::ONE.to_bits() { // < 0 gets rounded to 0 I::ZERO } else if fbits < F::Int::cast_from(int_max_exp) << F::SIG_BITS { // >= 1, < integer max - let m_base = if I::UnsignedInt::BITS >= F::Int::BITS { - I::UnsignedInt::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1) + let m_base = if I::Unsigned::BITS >= F::Int::BITS { + I::Unsigned::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1) } else { - I::UnsignedInt::cast_from(fbits >> (F::SIG_BITS - I::BITS + 1)) + I::Unsigned::cast_from(fbits >> (F::SIG_BITS - I::BITS + 1)) }; // Set the implicit 1-bit. - let m: I::UnsignedInt = (I::UnsignedInt::ONE << (I::BITS - 1)) | m_base; + let m: I::Unsigned = (I::Unsigned::ONE << (I::BITS - 1)) | m_base; // Shift based on the exponent and bias. let s: u32 = (foobar) - u32::cast_from(fbits >> F::SIG_BITS); diff --git a/library/compiler-builtins/compiler-builtins/src/float/div.rs b/library/compiler-builtins/compiler-builtins/src/float/div.rs index 5df637c7e0f9..3e4f0e20d105 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/div.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/div.rs @@ -370,7 +370,7 @@ where let hi_corr: F::Int = corr_uq1 >> hw; // x_UQ0 * corr_UQ1 = (x_UQ0_hw * 2^HW) * (hi_corr * 2^HW + lo_corr) - corr_UQ1 - let mut x_uq0: F::Int = ((F::Int::from(x_uq0_hw) * hi_corr) << 1) + let mut x_uq0: F::Int = ((F::Int::from(x_uq0_hw) * hi_corr) << 1u32) .wrapping_add((F::Int::from(x_uq0_hw) * lo_corr) >> (hw - 1)) // 1 to account for the highest bit of corr_UQ1 can be 1 // 1 to account for possible carry diff --git a/library/compiler-builtins/compiler-builtins/src/float/mul.rs b/library/compiler-builtins/compiler-builtins/src/float/mul.rs index 7f1f19d9bd7f..c811f140670e 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/mul.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/mul.rs @@ -143,7 +143,7 @@ where // a zero of the appropriate sign. Mathematically there is no need to // handle this case separately, but we make it a special case to // simplify the shift logic. - let shift = one.wrapping_sub(product_exponent.cast()).cast(); + let shift: u32 = one.wrapping_sub(product_exponent.cast()).cast(); if shift >= bits { return F::from_bits(product_sign); } diff --git a/library/compiler-builtins/compiler-builtins/src/float/traits.rs b/library/compiler-builtins/compiler-builtins/src/float/traits.rs index 8ccaa7bcbd78..a30d20900b1c 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/traits.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/traits.rs @@ -20,10 +20,10 @@ pub trait Float: + ops::Rem { /// A uint of the same width as the float - type Int: Int; + type Int: Int; /// A int of the same width as the float - type SignedInt: Int + MinInt; + type SignedInt: Int + MinInt; /// An int capable of containing the exponent bits plus a sign bit. This is signed. type ExpInt: Int; diff --git a/library/compiler-builtins/compiler-builtins/src/int/addsub.rs b/library/compiler-builtins/compiler-builtins/src/int/addsub.rs index 1f84e8eb1e11..b2b21fc2c440 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/addsub.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/addsub.rs @@ -22,7 +22,7 @@ impl UAddSub for u128 {} trait AddSub: Int where - ::UnsignedInt: UAddSub, + ::Unsigned: UAddSub, { fn add(self, other: Self) -> Self { Self::from_unsigned(self.unsigned().uadd(other.unsigned())) @@ -37,7 +37,7 @@ impl AddSub for i128 {} trait Addo: AddSub where - ::UnsignedInt: UAddSub, + ::Unsigned: UAddSub, { fn addo(self, other: Self) -> (Self, bool) { let sum = AddSub::add(self, other); @@ -50,7 +50,7 @@ impl Addo for u128 {} trait Subo: AddSub where - ::UnsignedInt: UAddSub, + ::Unsigned: UAddSub, { fn subo(self, other: Self) -> (Self, bool) { let sum = AddSub::sub(self, other); diff --git a/library/compiler-builtins/compiler-builtins/src/int/big.rs b/library/compiler-builtins/compiler-builtins/src/int/big.rs index 1402efb8ed41..8e06009090c0 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/big.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/big.rs @@ -45,7 +45,7 @@ impl i256 { impl MinInt for u256 { type OtherSign = i256; - type UnsignedInt = u256; + type Unsigned = u256; const SIGNED: bool = false; const BITS: u32 = 256; @@ -58,7 +58,7 @@ impl MinInt for u256 { impl MinInt for i256 { type OtherSign = u256; - type UnsignedInt = u256; + type Unsigned = u256; const SIGNED: bool = false; const BITS: u32 = 256; diff --git a/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs b/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs index 112f4d036131..aa5cb39935ad 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs @@ -9,11 +9,14 @@ pub use implementation::{leading_zeros_default, leading_zeros_riscv}; pub(crate) use implementation::{leading_zeros_default, leading_zeros_riscv}; mod implementation { - use crate::int::{CastInto, Int}; + use crate::int::{CastFrom, Int}; /// Returns the number of leading binary zeros in `x`. #[allow(dead_code)] - pub fn leading_zeros_default>(x: T) -> usize { + pub fn leading_zeros_default(x: I) -> usize + where + usize: CastFrom, + { // The basic idea is to test if the higher bits of `x` are zero and bisect the number // of leading zeros. It is possible for all branches of the bisection to use the same // code path by conditionally shifting the higher parts down to let the next bisection @@ -23,44 +26,48 @@ mod implementation { // because it simplifies the final bisection step. let mut x = x; // the number of potential leading zeros - let mut z = T::BITS as usize; + let mut z = I::BITS as usize; // a temporary - let mut t: T; + let mut t: I; - const { assert!(T::BITS <= 64) }; - if T::BITS >= 64 { + const { assert!(I::BITS <= 64) }; + if I::BITS >= 64 { t = x >> 32; - if t != T::ZERO { + if t != I::ZERO { z -= 32; x = t; } } - if T::BITS >= 32 { + if I::BITS >= 32 { t = x >> 16; - if t != T::ZERO { + if t != I::ZERO { z -= 16; x = t; } } - const { assert!(T::BITS >= 16) }; + const { assert!(I::BITS >= 16) }; t = x >> 8; - if t != T::ZERO { + if t != I::ZERO { z -= 8; x = t; } t = x >> 4; - if t != T::ZERO { + if t != I::ZERO { z -= 4; x = t; } t = x >> 2; - if t != T::ZERO { + if t != I::ZERO { z -= 2; x = t; } // the last two bisections are combined into one conditional t = x >> 1; - if t != T::ZERO { z - 2 } else { z - x.cast() } + if t != I::ZERO { + z - 2 + } else { + z - usize::cast_from(x) + } // We could potentially save a few cycles by using the LUT trick from // "https://embeddedgurus.com/state-space/2014/09/ @@ -82,10 +89,13 @@ mod implementation { /// Returns the number of leading binary zeros in `x`. #[allow(dead_code)] - pub fn leading_zeros_riscv>(x: T) -> usize { + pub fn leading_zeros_riscv(x: I) -> usize + where + usize: CastFrom, + { let mut x = x; // the number of potential leading zeros - let mut z = T::BITS; + let mut z = I::BITS; // a temporary let mut t: u32; @@ -97,11 +107,11 @@ mod implementation { // right). If we try to save an instruction by using `x < imm` for each bisection, we // have to shift `x` left and compare with powers of two approaching `usize::MAX + 1`, // but the immediate will never fit into 12 bits and never save an instruction. - const { assert!(T::BITS <= 64) }; - if T::BITS >= 64 { + const { assert!(I::BITS <= 64) }; + if I::BITS >= 64 { // If the upper 32 bits of `x` are not all 0, `t` is set to `1 << 5`, otherwise // `t` is set to 0. - t = ((x >= (T::ONE << 32)) as u32) << 5; + t = ((x >= (I::ONE << 32)) as u32) << 5; // If `t` was set to `1 << 5`, then the upper 32 bits are shifted down for the // next step to process. x >>= t; @@ -109,27 +119,27 @@ mod implementation { // leading zeros z -= t; } - if T::BITS >= 32 { - t = ((x >= (T::ONE << 16)) as u32) << 4; + if I::BITS >= 32 { + t = ((x >= (I::ONE << 16)) as u32) << 4; x >>= t; z -= t; } - const { assert!(T::BITS >= 16) }; - t = ((x >= (T::ONE << 8)) as u32) << 3; + const { assert!(I::BITS >= 16) }; + t = ((x >= (I::ONE << 8)) as u32) << 3; x >>= t; z -= t; - t = ((x >= (T::ONE << 4)) as u32) << 2; + t = ((x >= (I::ONE << 4)) as u32) << 2; x >>= t; z -= t; - t = ((x >= (T::ONE << 2)) as u32) << 1; + t = ((x >= (I::ONE << 2)) as u32) << 1; x >>= t; z -= t; - t = (x >= (T::ONE << 1)) as u32; + t = (x >= (I::ONE << 1)) as u32; x >>= t; z -= t; // All bits except the LSB are guaranteed to be zero for this final bisection step. // If `x != 0` then `x == 1` and subtracts one potential zero from `z`. - z as usize - x.cast() + z as usize - usize::cast_from(x) } } diff --git a/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs b/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs index c45d6b1cfe8d..8f63c22c8f73 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs @@ -4,33 +4,38 @@ pub use implementation::trailing_zeros; pub(crate) use implementation::trailing_zeros; mod implementation { - use crate::int::{CastInto, Int}; + use crate::int::{CastFrom, Int}; /// Returns number of trailing binary zeros in `x`. #[allow(dead_code)] - pub fn trailing_zeros + CastInto + CastInto>(x: T) -> usize { + pub fn trailing_zeros(x: I) -> usize + where + u32: CastFrom, + u16: CastFrom, + u8: CastFrom, + { let mut x = x; let mut r: u32 = 0; let mut t: u32; - const { assert!(T::BITS <= 64) }; - if T::BITS >= 64 { - r += ((CastInto::::cast(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0 + const { assert!(I::BITS <= 64) }; + if I::BITS >= 64 { + r += ((u32::cast_from(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0 x >>= r; // remove 32 zero bits } - if T::BITS >= 32 { - t = ((CastInto::::cast(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0 + if I::BITS >= 32 { + t = ((u16::cast_from(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0 r += t; x >>= t; // x = [0 - 0xFFFF] + higher garbage bits } - const { assert!(T::BITS >= 16) }; - t = ((CastInto::::cast(x) == 0) as u32) << 3; + const { assert!(I::BITS >= 16) }; + t = ((u8::cast_from(x) == 0) as u32) << 3; x >>= t; // x = [0 - 0xFF] + higher garbage bits r += t; - let mut x: u8 = x.cast(); + let mut x: u8 = x.cast_lossy(); t = (((x & 0x0F) == 0) as u32) << 2; x >>= t; // x = [0 - 0xF] + higher garbage bits diff --git a/library/compiler-builtins/compiler-builtins/src/int/traits.rs b/library/compiler-builtins/compiler-builtins/src/int/traits.rs index 152cb2eee2ee..b474df366eb5 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/traits.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/traits.rs @@ -1,275 +1,4 @@ -use core::ops; - -/// Minimal integer implementations needed on all integer types, including wide integers. -#[allow(dead_code)] -pub trait MinInt: - Copy - + core::fmt::Debug - + ops::BitOr - + ops::Not - + ops::Shl -{ - /// Type with the same width but other signedness - type OtherSign: MinInt; - /// Unsigned version of Self - type UnsignedInt: MinInt; - - /// If `Self` is a signed integer - const SIGNED: bool; - - /// The bitwidth of the int type - const BITS: u32; - - const ZERO: Self; - const ONE: Self; - const MIN: Self; - const MAX: Self; -} - -/// Trait for some basic operations on integers -#[allow(dead_code)] -pub trait Int: - MinInt - + PartialEq - + PartialOrd - + ops::AddAssign - + ops::SubAssign - + ops::BitAndAssign - + ops::BitOrAssign - + ops::BitXorAssign - + ops::ShlAssign - + ops::ShrAssign - + ops::Add - + ops::Sub - + ops::Mul - + ops::Div - + ops::Shr - + ops::BitXor - + ops::BitAnd -{ - /// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing - /// in `builtins-test`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96, - /// 111,112,119,120,125,126,127]. - const FUZZ_LENGTHS: [u8; 20] = make_fuzz_lengths(::BITS); - - /// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128. - const FUZZ_NUM: usize = { - let log2 = (::BITS - 1).count_ones() as usize; - if log2 == 3 { - // case for u8 - 6 - } else { - // 3 entries on each extreme, 2 in the middle, and 4 for each scale of intermediate - // boundaries. - 8 + (4 * (log2 - 4)) - } - }; - - fn unsigned(self) -> Self::UnsignedInt; - fn from_unsigned(unsigned: Self::UnsignedInt) -> Self; - fn unsigned_abs(self) -> Self::UnsignedInt; - - fn from_bool(b: bool) -> Self; - - /// Prevents the need for excessive conversions between signed and unsigned - fn logical_shr(self, other: u32) -> Self; - - /// Absolute difference between two integers. - fn abs_diff(self, other: Self) -> Self::UnsignedInt; - - // copied from primitive integers, but put in a trait - fn is_zero(self) -> bool; - fn wrapping_neg(self) -> Self; - fn wrapping_add(self, other: Self) -> Self; - fn wrapping_mul(self, other: Self) -> Self; - fn wrapping_sub(self, other: Self) -> Self; - fn wrapping_shl(self, other: u32) -> Self; - fn wrapping_shr(self, other: u32) -> Self; - fn rotate_left(self, other: u32) -> Self; - fn overflowing_add(self, other: Self) -> (Self, bool); - fn leading_zeros(self) -> u32; - fn ilog2(self) -> u32; -} - -pub(crate) const fn make_fuzz_lengths(bits: u32) -> [u8; 20] { - let mut v = [0u8; 20]; - v[0] = 0; - v[1] = 1; - v[2] = 2; // important for parity and the iX::MIN case when reversed - let mut i = 3; - - // No need for any more until the byte boundary, because there should be no algorithms - // that are sensitive to anything not next to byte boundaries after 2. We also scale - // in powers of two, which is important to prevent u128 corner tests from getting too - // big. - let mut l = 8; - loop { - if l >= ((bits / 2) as u8) { - break; - } - // get both sides of the byte boundary - v[i] = l - 1; - i += 1; - v[i] = l; - i += 1; - l *= 2; - } - - if bits != 8 { - // add the lower side of the middle boundary - v[i] = ((bits / 2) - 1) as u8; - i += 1; - } - - // We do not want to jump directly from the Self::BITS/2 boundary to the Self::BITS - // boundary because of algorithms that split the high part up. We reverse the scaling - // as we go to Self::BITS. - let mid = i; - let mut j = 1; - loop { - v[i] = (bits as u8) - (v[mid - j]) - 1; - if j == mid { - break; - } - i += 1; - j += 1; - } - v -} - -macro_rules! int_impl_common { - ($ty:ty) => { - fn from_bool(b: bool) -> Self { - b as $ty - } - - fn logical_shr(self, other: u32) -> Self { - Self::from_unsigned(self.unsigned().wrapping_shr(other)) - } - - fn is_zero(self) -> bool { - self == Self::ZERO - } - - fn wrapping_neg(self) -> Self { - ::wrapping_neg(self) - } - - fn wrapping_add(self, other: Self) -> Self { - ::wrapping_add(self, other) - } - - fn wrapping_mul(self, other: Self) -> Self { - ::wrapping_mul(self, other) - } - fn wrapping_sub(self, other: Self) -> Self { - ::wrapping_sub(self, other) - } - - fn wrapping_shl(self, other: u32) -> Self { - ::wrapping_shl(self, other) - } - - fn wrapping_shr(self, other: u32) -> Self { - ::wrapping_shr(self, other) - } - - fn rotate_left(self, other: u32) -> Self { - ::rotate_left(self, other) - } - - fn overflowing_add(self, other: Self) -> (Self, bool) { - ::overflowing_add(self, other) - } - - fn leading_zeros(self) -> u32 { - ::leading_zeros(self) - } - - fn ilog2(self) -> u32 { - ::ilog2(self) - } - }; -} - -macro_rules! int_impl { - ($ity:ty, $uty:ty) => { - impl MinInt for $uty { - type OtherSign = $ity; - type UnsignedInt = $uty; - - const BITS: u32 = ::ZERO.count_zeros(); - const SIGNED: bool = Self::MIN != Self::ZERO; - - const ZERO: Self = 0; - const ONE: Self = 1; - const MIN: Self = ::MIN; - const MAX: Self = ::MAX; - } - - impl Int for $uty { - fn unsigned(self) -> $uty { - self - } - - // It makes writing macros easier if this is implemented for both signed and unsigned - #[allow(clippy::wrong_self_convention)] - fn from_unsigned(me: $uty) -> Self { - me - } - - fn unsigned_abs(self) -> Self { - self - } - - fn abs_diff(self, other: Self) -> Self { - self.abs_diff(other) - } - - int_impl_common!($uty); - } - - impl MinInt for $ity { - type OtherSign = $uty; - type UnsignedInt = $uty; - - const BITS: u32 = ::ZERO.count_zeros(); - const SIGNED: bool = Self::MIN != Self::ZERO; - - const ZERO: Self = 0; - const ONE: Self = 1; - const MIN: Self = ::MIN; - const MAX: Self = ::MAX; - } - - impl Int for $ity { - fn unsigned(self) -> $uty { - self as $uty - } - - fn from_unsigned(me: $uty) -> Self { - me as $ity - } - - fn unsigned_abs(self) -> Self::UnsignedInt { - self.unsigned_abs() - } - - fn abs_diff(self, other: Self) -> $uty { - self.abs_diff(other) - } - - int_impl_common!($ity); - } - }; -} - -int_impl!(isize, usize); -int_impl!(i8, u8); -int_impl!(i16, u16); -int_impl!(i32, u32); -int_impl!(i64, u64); -int_impl!(i128, u128); +pub use crate::support::{Int, MinInt}; /// Trait for integers twice the bit width of another integer. This is implemented for all /// primitives except for `u8`, because there is not a smaller primitive. diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs index 3ec1faba170c..fa9e06066d74 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs @@ -78,6 +78,7 @@ pub trait Int: fn unsigned(self) -> Self::Unsigned; fn from_unsigned(unsigned: Self::Unsigned) -> Self; fn abs(self) -> Self; + fn unsigned_abs(self) -> Self::Unsigned; fn from_bool(b: bool) -> Self; @@ -203,6 +204,10 @@ macro_rules! int_impl { unimplemented!() } + fn unsigned_abs(self) -> Self { + unimplemented!() + } + // It makes writing macros easier if this is implemented for both signed and unsigned #[allow(clippy::wrong_self_convention)] fn from_unsigned(me: $uty) -> Self { @@ -242,6 +247,10 @@ macro_rules! int_impl { self.abs() } + fn unsigned_abs(self) -> Self::Unsigned { + self.unsigned_abs() + } + fn from_unsigned(me: $uty) -> Self { me as $ity } From 877feef541542972f1bfeefe14624a17f23cef8d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 03:51:43 +0000 Subject: [PATCH 11/59] Reuse `libm`'s `Caat` and `CastFrom` in `compiler-builtins` --- .../compiler-builtins/src/float/add.rs | 2 +- .../compiler-builtins/src/float/conv.rs | 6 +-- .../compiler-builtins/src/float/div.rs | 2 +- .../compiler-builtins/src/float/mul.rs | 2 +- .../compiler-builtins/src/float/trunc.rs | 2 +- .../src/int/trailing_zeros.rs | 6 +-- .../compiler-builtins/src/int/traits.rs | 43 +------------------ .../libm/src/math/support/int_traits.rs | 5 +++ 8 files changed, 16 insertions(+), 52 deletions(-) diff --git a/library/compiler-builtins/compiler-builtins/src/float/add.rs b/library/compiler-builtins/compiler-builtins/src/float/add.rs index 43e3ae931ffa..0cc362f705b1 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/add.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/add.rs @@ -168,7 +168,7 @@ where } // Low three bits are round, guard, and sticky. - let a_significand_i32: i32 = a_significand.cast(); + let a_significand_i32: i32 = a_significand.cast_lossy(); let round_guard_sticky: i32 = a_significand_i32 & 0x7; // Shift the significand into place, and mask off the implicit bit. diff --git a/library/compiler-builtins/compiler-builtins/src/float/conv.rs b/library/compiler-builtins/compiler-builtins/src/float/conv.rs index 9d732f2cdbc9..75ea7ce02424 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/conv.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/conv.rs @@ -74,7 +74,7 @@ mod int_to_float { F::Int: CastFrom, Conv: Fn(I::Unsigned) -> F::Int, { - let sign_bit = F::Int::cast_from(i >> (I::BITS - 1)) << (F::BITS - 1); + let sign_bit = F::Int::cast_from_lossy(i >> (I::BITS - 1)) << (F::BITS - 1); F::from_bits(conv(i.unsigned_abs()) | sign_bit) } @@ -166,7 +166,7 @@ mod int_to_float { // Within the upper `F::BITS`, everything except for the signifcand // gets truncated - let d1: u32 = (i_m >> (u128::BITS - f32::BITS - f32::SIG_BITS - 1)).cast(); + let d1: u32 = (i_m >> (u128::BITS - f32::BITS - f32::SIG_BITS - 1)).cast_lossy(); // The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just // check if it is nonzero. @@ -371,7 +371,7 @@ where let m_base = if I::Unsigned::BITS >= F::Int::BITS { I::Unsigned::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1) } else { - I::Unsigned::cast_from(fbits >> (F::SIG_BITS - I::BITS + 1)) + I::Unsigned::cast_from_lossy(fbits >> (F::SIG_BITS - I::BITS + 1)) }; // Set the implicit 1-bit. diff --git a/library/compiler-builtins/compiler-builtins/src/float/div.rs b/library/compiler-builtins/compiler-builtins/src/float/div.rs index 3e4f0e20d105..fc1fc085105a 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/div.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/div.rs @@ -482,7 +482,7 @@ where let ret = quotient.wrapping_shr(u32::cast_from(res_exponent.wrapping_neg()) + 1); residual_lo = a_significand - .wrapping_shl(significand_bits.wrapping_add(CastInto::::cast(res_exponent))) + .wrapping_shl(significand_bits.wrapping_add(CastInto::::cast_lossy(res_exponent))) .wrapping_sub(ret.wrapping_mul(b_significand) << 1); ret }; diff --git a/library/compiler-builtins/compiler-builtins/src/float/mul.rs b/library/compiler-builtins/compiler-builtins/src/float/mul.rs index c811f140670e..dbed3095cda5 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/mul.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/mul.rs @@ -143,7 +143,7 @@ where // a zero of the appropriate sign. Mathematically there is no need to // handle this case separately, but we make it a special case to // simplify the shift logic. - let shift: u32 = one.wrapping_sub(product_exponent.cast()).cast(); + let shift: u32 = one.wrapping_sub(product_exponent.cast_lossy()).cast(); if shift >= bits { return F::from_bits(product_sign); } diff --git a/library/compiler-builtins/compiler-builtins/src/float/trunc.rs b/library/compiler-builtins/compiler-builtins/src/float/trunc.rs index ca8a0f368b56..93db5d8bbdeb 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/trunc.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/trunc.rs @@ -50,7 +50,7 @@ where // The exponent of a is within the range of normal numbers in the // destination format. We can convert by simply right-shifting with // rounding and adjusting the exponent. - abs_result = (a_abs >> sig_bits_delta).cast(); + abs_result = (a_abs >> sig_bits_delta).cast_lossy(); // Cast before shifting to prevent overflow. let bias_diff: R::Int = src_exp_bias.wrapping_sub(dst_exp_bias).cast(); let tmp = bias_diff << R::SIG_BITS; diff --git a/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs b/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs index 8f63c22c8f73..1b0ae5b73ad2 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs @@ -20,18 +20,18 @@ mod implementation { const { assert!(I::BITS <= 64) }; if I::BITS >= 64 { - r += ((u32::cast_from(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0 + r += ((u32::cast_from_lossy(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0 x >>= r; // remove 32 zero bits } if I::BITS >= 32 { - t = ((u16::cast_from(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0 + t = ((u16::cast_from_lossy(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0 r += t; x >>= t; // x = [0 - 0xFFFF] + higher garbage bits } const { assert!(I::BITS >= 16) }; - t = ((u8::cast_from(x) == 0) as u32) << 3; + t = ((u8::cast_from_lossy(x) == 0) as u32) << 3; x >>= t; // x = [0 - 0xFF] + higher garbage bits r += t; diff --git a/library/compiler-builtins/compiler-builtins/src/int/traits.rs b/library/compiler-builtins/compiler-builtins/src/int/traits.rs index b474df366eb5..25b9718ad53f 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/traits.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/traits.rs @@ -1,4 +1,4 @@ -pub use crate::support::{Int, MinInt}; +pub use crate::support::{CastFrom, CastInto, Int, MinInt}; /// Trait for integers twice the bit width of another integer. This is implemented for all /// primitives except for `u8`, because there is not a smaller primitive. @@ -97,44 +97,3 @@ impl_h_int!( i32 u32 i64, i64 u64 i128 ); - -/// Trait to express (possibly lossy) casting of integers -pub trait CastInto: Copy { - fn cast(self) -> T; -} - -pub trait CastFrom: Copy { - fn cast_from(value: T) -> Self; -} - -impl + Copy> CastFrom for T { - fn cast_from(value: U) -> Self { - value.cast() - } -} - -macro_rules! cast_into { - ($ty:ty) => { - cast_into!($ty; usize, isize, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128); - }; - ($ty:ty; $($into:ty),*) => {$( - impl CastInto<$into> for $ty { - fn cast(self) -> $into { - self as $into - } - } - )*}; -} - -cast_into!(usize); -cast_into!(isize); -cast_into!(u8); -cast_into!(i8); -cast_into!(u16); -cast_into!(i16); -cast_into!(u32); -cast_into!(i32); -cast_into!(u64); -cast_into!(i64); -cast_into!(u128); -cast_into!(i128); diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs index fa9e06066d74..716af748a9fb 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs @@ -374,14 +374,19 @@ impl_h_int!( /// Trait to express (possibly lossy) casting of integers pub trait CastInto: Copy { /// By default, casts should be exact. + #[track_caller] fn cast(self) -> T; /// Call for casts that are expected to truncate. + /// + /// In practice, this is exactly the same as `cast`; the main difference is to document intent + /// in code. `cast` may panic in debug mode. fn cast_lossy(self) -> T; } pub trait CastFrom: Copy { /// By default, casts should be exact. + #[track_caller] fn cast_from(value: T) -> Self; /// Call for casts that are expected to truncate. From 97c0bebdf7094434c0fb2f941d7136c9c3ade986 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 28 May 2025 18:15:41 +0000 Subject: [PATCH 12/59] Remove unneeded C symbols These are now provided by `compiler-builtins`, so there is no need to also build the C versions. This was detected by checking for duplicate symbols and not excluding weak symbols (like CI currently does). --- library/compiler-builtins/compiler-builtins/build.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/compiler-builtins/compiler-builtins/build.rs b/library/compiler-builtins/compiler-builtins/build.rs index 90d98ec7ce94..d37fdc5df507 100644 --- a/library/compiler-builtins/compiler-builtins/build.rs +++ b/library/compiler-builtins/compiler-builtins/build.rs @@ -555,7 +555,6 @@ mod c { if (target.arch == "aarch64" || target.arch == "arm64ec") && consider_float_intrinsics { sources.extend(&[ - ("__comparetf2", "comparetf2.c"), ("__fe_getround", "fp_mode.c"), ("__fe_raise_inexact", "fp_mode.c"), ]); @@ -570,11 +569,11 @@ mod c { } if target.arch == "mips64" { - sources.extend(&[("__netf2", "comparetf2.c"), ("__fe_getround", "fp_mode.c")]); + sources.extend(&[("__fe_getround", "fp_mode.c")]); } if target.arch == "loongarch64" { - sources.extend(&[("__netf2", "comparetf2.c"), ("__fe_getround", "fp_mode.c")]); + sources.extend(&[("__fe_getround", "fp_mode.c")]); } // Remove the assembly implementations that won't compile for the target From a63f4826cf2ccceeedff5fb19ba0cbffc1ef5d65 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 19 Apr 2025 07:38:43 +0000 Subject: [PATCH 13/59] Replace the `nm` symbol check with a Rust implementation This should be less error-prone and adaptable than the `nm` version, and have better cross-platform support without needing LLVM `nm` installed. --- library/compiler-builtins/Cargo.toml | 1 + library/compiler-builtins/ci/run.sh | 123 ++-------- .../crates/symbol-check/Cargo.toml | 13 + .../crates/symbol-check/src/main.rs | 231 ++++++++++++++++++ 4 files changed, 261 insertions(+), 107 deletions(-) create mode 100644 library/compiler-builtins/crates/symbol-check/Cargo.toml create mode 100644 library/compiler-builtins/crates/symbol-check/src/main.rs diff --git a/library/compiler-builtins/Cargo.toml b/library/compiler-builtins/Cargo.toml index b39ec8a25dac..bc6b4bd29795 100644 --- a/library/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/libm-macros", "crates/musl-math-sys", "crates/panic-handler", + "crates/symbol-check", "crates/util", "libm", "libm-test", diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index 68d13c130bcc..cf3f7dfda1da 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -47,87 +47,25 @@ else fi fi +# Ensure there are no duplicate symbols or references to `core` when +# `compiler-builtins` is built with various features. Symcheck invokes Cargo to +# build with the arguments we provide it, then validates the built artifacts. +symcheck=(cargo run -p symbol-check --release) +[[ "$target" = "wasm"* ]] && symcheck+=(--features wasm) +symcheck+=(-- build-and-check) -declare -a rlib_paths - -# Set the `rlib_paths` global array to a list of all compiler-builtins rlibs -update_rlib_paths() { - if [ -d /builtins-target ]; then - rlib_paths=( /builtins-target/"${target}"/debug/deps/libcompiler_builtins-*.rlib ) - else - rlib_paths=( target/"${target}"/debug/deps/libcompiler_builtins-*.rlib ) - fi -} - -# Remove any existing artifacts from previous tests that don't set #![compiler_builtins] -update_rlib_paths -rm -f "${rlib_paths[@]}" - -cargo build -p compiler_builtins --target "$target" -cargo build -p compiler_builtins --target "$target" --release -cargo build -p compiler_builtins --target "$target" --features c -cargo build -p compiler_builtins --target "$target" --features c --release -cargo build -p compiler_builtins --target "$target" --features no-asm -cargo build -p compiler_builtins --target "$target" --features no-asm --release -cargo build -p compiler_builtins --target "$target" --features no-f16-f128 -cargo build -p compiler_builtins --target "$target" --features no-f16-f128 --release - -PREFIX=${target//unknown-/}- -case "$target" in - armv7-*) - PREFIX=arm-linux-gnueabihf- - ;; - thumb*) - PREFIX=arm-none-eabi- - ;; - *86*-*) - PREFIX= - ;; -esac - -NM=$(find "$(rustc --print sysroot)" \( -name llvm-nm -o -name llvm-nm.exe \) ) -if [ "$NM" = "" ]; then - NM="${PREFIX}nm" -fi - -# i686-pc-windows-gnu tools have a dependency on some DLLs, so run it with -# rustup run to ensure that those are in PATH. -TOOLCHAIN="$(rustup show active-toolchain | sed 's/ (default)//')" -if [[ "$TOOLCHAIN" == *i686-pc-windows-gnu ]]; then - NM="rustup run $TOOLCHAIN $NM" -fi - -# Look out for duplicated symbols when we include the compiler-rt (C) implementation -update_rlib_paths -for rlib in "${rlib_paths[@]}"; do - set +x - echo "================================================================" - echo "checking $rlib for duplicate symbols" - echo "================================================================" - set -x - - duplicates_found=0 - - # NOTE On i586, It's normal that the get_pc_thunk symbol appears several - # times so ignore it - $NM -g --defined-only "$rlib" 2>&1 | - sort | - uniq -d | - grep -v __x86.get_pc_thunk --quiet | - grep 'T __' && duplicates_found=1 - - if [ "$duplicates_found" != 0 ]; then - echo "error: found duplicate symbols" - exit 1 - else - echo "success; no duplicate symbols found" - fi -done - -rm -f "${rlib_paths[@]}" +"${symcheck[@]}" -p compiler_builtins --target "$target" +"${symcheck[@]}" -p compiler_builtins --target "$target" --release +"${symcheck[@]}" -p compiler_builtins --target "$target" --features c +"${symcheck[@]}" -p compiler_builtins --target "$target" --features c --release +"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-asm +"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-asm --release +"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-f16-f128 +"${symcheck[@]}" -p compiler_builtins --target "$target" --features no-f16-f128 --release build_intrinsics_test() { - cargo build \ + # symcheck also checks the results of builtins-test-intrinsics + "${symcheck[@]}" \ --target "$target" --verbose \ --manifest-path builtins-test-intrinsics/Cargo.toml "$@" } @@ -143,35 +81,6 @@ build_intrinsics_test --features c --release CARGO_PROFILE_DEV_LTO=true build_intrinsics_test CARGO_PROFILE_RELEASE_LTO=true build_intrinsics_test --release -# Ensure no references to any symbols from core -update_rlib_paths -for rlib in "${rlib_paths[@]}"; do - set +x - echo "================================================================" - echo "checking $rlib for references to core" - echo "================================================================" - set -x - - tmpdir="${CARGO_TARGET_DIR:-target}/tmp" - test -d "$tmpdir" || mkdir "$tmpdir" - defined="$tmpdir/defined_symbols.txt" - undefined="$tmpdir/defined_symbols.txt" - - $NM --quiet -U "$rlib" | grep 'T _ZN4core' | awk '{print $3}' | sort | uniq > "$defined" - $NM --quiet -u "$rlib" | grep 'U _ZN4core' | awk '{print $2}' | sort | uniq > "$undefined" - grep_has_results=0 - grep -v -F -x -f "$defined" "$undefined" && grep_has_results=1 - - if [ "$target" = "powerpc64-unknown-linux-gnu" ]; then - echo "FIXME: powerpc64 fails these tests" - elif [ "$grep_has_results" != 0 ]; then - echo "error: found unexpected references to core" - exit 1 - else - echo "success; no references to core found" - fi -done - # Test libm # Make sure a simple build works diff --git a/library/compiler-builtins/crates/symbol-check/Cargo.toml b/library/compiler-builtins/crates/symbol-check/Cargo.toml new file mode 100644 index 000000000000..30969ee406ab --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "symbol-check" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] +# FIXME: used as a git dependency since the latest release does not support wasm +object = { git = "https://github.com/gimli-rs/object.git", rev = "013fac75da56a684377af4151b8164b78c1790e0" } +serde_json = "1.0.140" + +[features] +wasm = ["object/wasm"] diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs new file mode 100644 index 000000000000..104505438cc0 --- /dev/null +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -0,0 +1,231 @@ +//! Tool used by CI to inspect compiler-builtins archives and help ensure we won't run into any +//! linking errors. + +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::io::{BufRead, BufReader}; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; + +use object::read::archive::{ArchiveFile, ArchiveMember}; +use object::{Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection}; +use serde_json::Value; + +const CHECK_LIBRARIES: &[&str] = &["compiler_builtins", "builtins_test_intrinsics"]; +const CHECK_EXTENSIONS: &[Option<&str>] = &[Some("rlib"), Some("a"), Some("exe"), None]; + +const USAGE: &str = "Usage: + + symbol-check build-and-check CARGO_ARGS ... + +Cargo will get invoked with `CARGO_ARGS` and all output +`compiler_builtins*.rlib` files will be checked. +"; + +fn main() { + // Create a `&str` vec so we can match on it. + let args = std::env::args().collect::>(); + let args_ref = args.iter().map(String::as_str).collect::>(); + + match &args_ref[1..] { + ["build-and-check", rest @ ..] if !rest.is_empty() => { + let paths = exec_cargo_with_args(rest); + for path in paths { + println!("Checking {}", path.display()); + verify_no_duplicates(&path); + verify_core_symbols(&path); + } + } + _ => { + println!("{USAGE}"); + std::process::exit(1); + } + } +} + +/// Run `cargo build` with the provided additional arguments, collecting the list of created +/// libraries. +fn exec_cargo_with_args(args: &[&str]) -> Vec { + let mut cmd = Command::new("cargo") + .arg("build") + .arg("--message-format=json") + .args(args) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to launch Cargo"); + + let stdout = cmd.stdout.take().unwrap(); + let reader = BufReader::new(stdout); + let mut check_files = Vec::new(); + + for line in reader.lines() { + let line = line.expect("failed to read line"); + println!("{line}"); // tee to stdout + + // Select only steps that create files + let j: Value = serde_json::from_str(&line).expect("failed to deserialize"); + if j["reason"] != "compiler-artifact" { + continue; + } + + // Find rlibs in the created file list that match our expected library names and + // extensions. + for fpath in j["filenames"].as_array().expect("filenames not an array") { + let path = fpath.as_str().expect("file name not a string"); + let path = PathBuf::from(path); + + if CHECK_EXTENSIONS.contains(&path.extension().map(|ex| ex.to_str().unwrap())) { + let fname = path.file_name().unwrap().to_str().unwrap(); + + if CHECK_LIBRARIES.iter().any(|lib| fname.contains(lib)) { + check_files.push(path); + } + } + } + } + + cmd.wait().expect("failed to wait on Cargo"); + + assert!(!check_files.is_empty(), "no compiler_builtins rlibs found"); + println!("Collected the following rlibs to check: {check_files:#?}"); + + check_files +} + +/// Information collected from `object`, for convenience. +#[expect(unused)] // only for printing +#[derive(Clone, Debug)] +struct SymInfo { + name: String, + kind: SymbolKind, + scope: SymbolScope, + section: SymbolSection, + is_undefined: bool, + is_global: bool, + is_local: bool, + is_weak: bool, + is_common: bool, + address: u64, + object: String, +} + +impl SymInfo { + fn new(sym: &Symbol, member: &ArchiveMember) -> Self { + Self { + name: sym.name().expect("missing name").to_owned(), + kind: sym.kind(), + scope: sym.scope(), + section: sym.section(), + is_undefined: sym.is_undefined(), + is_global: sym.is_global(), + is_local: sym.is_local(), + is_weak: sym.is_weak(), + is_common: sym.is_common(), + address: sym.address(), + object: String::from_utf8_lossy(member.name()).into_owned(), + } + } +} + +/// Ensure that the same global symbol isn't defined in multiple object files within an archive. +/// +/// Note that this will also locate cases where a symbol is weakly defined in more than one place. +/// Technically there are no linker errors that will come from this, but it keeps our binary more +/// straightforward and saves some distribution size. +fn verify_no_duplicates(path: &Path) { + let mut syms = BTreeMap::::new(); + let mut dups = Vec::new(); + let mut found_any = false; + + for_each_symbol(path, |symbol, member| { + // Only check defined globals + if !symbol.is_global() || symbol.is_undefined() { + return; + } + + let sym = SymInfo::new(&symbol, member); + + // x86-32 includes multiple copies of thunk symbols + if sym.name.starts_with("__x86.get_pc_thunk") { + return; + } + + // Windows has symbols for literal numeric constants, string literals, and MinGW pseudo- + // relocations. These are allowed to have repeated definitions. + let win_allowed_dup_pfx = ["__real@", "__xmm@", "??_C@_", ".refptr"]; + if win_allowed_dup_pfx + .iter() + .any(|pfx| sym.name.starts_with(pfx)) + { + return; + } + + match syms.get(&sym.name) { + Some(existing) => { + dups.push(sym); + dups.push(existing.clone()); + } + None => { + syms.insert(sym.name.clone(), sym); + } + } + + found_any = true; + }); + + assert!(found_any, "no symbols found"); + + if !dups.is_empty() { + dups.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + panic!("found duplicate symbols: {dups:#?}"); + } + + println!(" success: no duplicate symbols found"); +} + +/// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined. +fn verify_core_symbols(path: &Path) { + let mut defined = BTreeSet::new(); + let mut undefined = Vec::new(); + let mut has_symbols = false; + + for_each_symbol(path, |symbol, member| { + has_symbols = true; + + // Find only symbols from `core` + if !symbol.name().unwrap().contains("_ZN4core") { + return; + } + + let sym = SymInfo::new(&symbol, member); + if sym.is_undefined { + undefined.push(sym); + } else { + defined.insert(sym.name); + } + }); + + assert!(has_symbols, "no symbols found"); + + // Discard any symbols that are defined somewhere in the archive + undefined.retain(|sym| !defined.contains(&sym.name)); + + if !undefined.is_empty() { + undefined.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + panic!("found undefined symbols from core: {undefined:#?}"); + } + + println!(" success: no undefined references to core found"); +} + +/// For a given archive path, do something with each symbol. +fn for_each_symbol(path: &Path, mut f: impl FnMut(Symbol, &ArchiveMember)) { + let data = fs::read(path).expect("reading file failed"); + let archive = ArchiveFile::parse(data.as_slice()).expect("archive parse failed"); + for member in archive.members() { + let member = member.expect("failed to access member"); + let obj_data = member.data(&*data).expect("failed to access object"); + let obj = object::File::parse(obj_data).expect("failed to parse object"); + obj.symbols().for_each(|sym| f(sym, &member)); + } +} From 8db9bd6a59c8c59b8d04ee647f071e81f79f6225 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 28 May 2025 19:59:16 +0000 Subject: [PATCH 14/59] Remove the now-unneeded llvm-tools-preview Since a working `nm` is no longer needed as part of CI, the rustup component can be removed. --- library/compiler-builtins/.github/workflows/main.yaml | 1 - library/compiler-builtins/crates/symbol-check/src/main.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index d13dd6b0f646..567ad1205a5d 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -119,7 +119,6 @@ jobs: rustup update "$channel" --no-self-update rustup default "$channel" rustup target add "${{ matrix.target }}" - rustup component add llvm-tools-preview - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 with: diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index 104505438cc0..4e6417fdf952 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -84,7 +84,7 @@ fn exec_cargo_with_args(args: &[&str]) -> Vec { } } - cmd.wait().expect("failed to wait on Cargo"); + assert!(cmd.wait().expect("failed to wait on Cargo").success()); assert!(!check_files.is_empty(), "no compiler_builtins rlibs found"); println!("Collected the following rlibs to check: {check_files:#?}"); From 151b1cb04777e1e9630045d52f14bc0e7adf5787 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 15:40:05 +0000 Subject: [PATCH 15/59] Change `compiler-builtins` to edition 2024 Do the same for `builtins-test-intrinsics`. Mostly this means updating `extern` to `unsafe extern`, and fixing a few new Clippy lints. --- .../builtins-test-intrinsics/Cargo.toml | 2 +- .../builtins-test-intrinsics/src/main.rs | 6 ++++-- .../builtins-test/tests/aeabi_memclr.rs | 3 ++- .../builtins-test/tests/aeabi_memcpy.rs | 3 ++- .../builtins-test/tests/aeabi_memset.rs | 3 ++- .../compiler-builtins/Cargo.toml | 2 +- .../compiler-builtins/src/arm.rs | 7 +++++-- .../src/int/specialized_div_rem/mod.rs | 16 ++++++++-------- .../compiler-builtins/src/macros.rs | 4 ++-- .../compiler-builtins/src/probestack.rs | 4 +++- 10 files changed, 30 insertions(+), 20 deletions(-) diff --git a/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml b/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml index 6e10628a41b2..704de20c5ad6 100644 --- a/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml +++ b/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "builtins-test-intrinsics" version = "0.1.0" -edition = "2021" +edition = "2024" publish = false license = "MIT OR Apache-2.0" diff --git a/library/compiler-builtins/builtins-test-intrinsics/src/main.rs b/library/compiler-builtins/builtins-test-intrinsics/src/main.rs index 1fa7b00916f7..96fe4a738511 100644 --- a/library/compiler-builtins/builtins-test-intrinsics/src/main.rs +++ b/library/compiler-builtins/builtins-test-intrinsics/src/main.rs @@ -15,9 +15,10 @@ extern crate panic_handler; +// SAFETY: no definitions, only used for linking #[cfg(all(not(thumb), not(windows), not(target_arch = "wasm32")))] #[link(name = "c")] -extern "C" {} +unsafe extern "C" {} // Every function in this module maps will be lowered to an intrinsic by LLVM, if the platform // doesn't have native support for the operation used in the function. ARM has a naming convention @@ -663,10 +664,11 @@ pub fn _start() -> ! { loop {} } +// SAFETY: no definitions, only used for linking #[cfg(windows)] #[link(name = "kernel32")] #[link(name = "msvcrt")] -extern "C" {} +unsafe extern "C" {} // ARM targets need these symbols #[unsafe(no_mangle)] diff --git a/library/compiler-builtins/builtins-test/tests/aeabi_memclr.rs b/library/compiler-builtins/builtins-test/tests/aeabi_memclr.rs index bfd15a391aab..0761feaffd9e 100644 --- a/library/compiler-builtins/builtins-test/tests/aeabi_memclr.rs +++ b/library/compiler-builtins/builtins-test/tests/aeabi_memclr.rs @@ -24,7 +24,8 @@ macro_rules! panic { }; } -extern "C" { +// SAFETY: defined in compiler-builtins +unsafe extern "aapcs" { fn __aeabi_memclr4(dest: *mut u8, n: usize); fn __aeabi_memset4(dest: *mut u8, n: usize, c: u32); } diff --git a/library/compiler-builtins/builtins-test/tests/aeabi_memcpy.rs b/library/compiler-builtins/builtins-test/tests/aeabi_memcpy.rs index c892c5aba0f7..e76e712a246f 100644 --- a/library/compiler-builtins/builtins-test/tests/aeabi_memcpy.rs +++ b/library/compiler-builtins/builtins-test/tests/aeabi_memcpy.rs @@ -22,7 +22,8 @@ macro_rules! panic { }; } -extern "C" { +// SAFETY: defined in compiler-builtins +unsafe extern "aapcs" { fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize); fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize); } diff --git a/library/compiler-builtins/builtins-test/tests/aeabi_memset.rs b/library/compiler-builtins/builtins-test/tests/aeabi_memset.rs index 34ab3acc78c1..8f9f80f969cc 100644 --- a/library/compiler-builtins/builtins-test/tests/aeabi_memset.rs +++ b/library/compiler-builtins/builtins-test/tests/aeabi_memset.rs @@ -24,7 +24,8 @@ macro_rules! panic { }; } -extern "C" { +// SAFETY: defined in compiler-builtins +unsafe extern "aapcs" { fn __aeabi_memset4(dest: *mut u8, n: usize, c: u32); } diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index d65a22152ef9..93eb3e01b3b8 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -7,7 +7,7 @@ readme = "README.md" repository = "https://github.com/rust-lang/compiler-builtins" homepage = "https://github.com/rust-lang/compiler-builtins" documentation = "https://docs.rs/compiler_builtins" -edition = "2021" +edition = "2024" description = "Compiler intrinsics used by the Rust compiler." links = "compiler-rt" diff --git a/library/compiler-builtins/compiler-builtins/src/arm.rs b/library/compiler-builtins/compiler-builtins/src/arm.rs index a9107e3cdfda..a7d84e49b349 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm.rs +++ b/library/compiler-builtins/compiler-builtins/src/arm.rs @@ -1,13 +1,16 @@ #![cfg(not(feature = "no-asm"))] // Interfaces used by naked trampolines. -extern "C" { +// SAFETY: these are defined in compiler-builtins +unsafe extern "C" { fn __udivmodsi4(a: u32, b: u32, rem: *mut u32) -> u32; fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64; fn __divmoddi4(a: i64, b: i64, rem: *mut i64) -> i64; } -extern "aapcs" { +// SAFETY: these are defined in compiler-builtins +// FIXME(extern_custom), this isn't always the correct ABI +unsafe extern "aapcs" { // AAPCS is not always the correct ABI for these intrinsics, but we only use this to // forward another `__aeabi_` call so it doesn't matter. fn __aeabi_idiv(a: i32, b: i32) -> i32; diff --git a/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs b/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs index 43f466e75ba4..7841e4f33cd6 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs @@ -125,10 +125,10 @@ impl_normalization_shift!( /// dependencies. #[inline] fn u64_by_u64_div_rem(duo: u64, div: u64) -> (u64, u64) { - if let Some(quo) = duo.checked_div(div) { - if let Some(rem) = duo.checked_rem(div) { - return (quo, rem); - } + if let Some(quo) = duo.checked_div(div) + && let Some(rem) = duo.checked_rem(div) + { + return (quo, rem); } zero_div_fn() } @@ -227,10 +227,10 @@ impl_asymmetric!( #[inline] #[allow(dead_code)] fn u32_by_u32_div_rem(duo: u32, div: u32) -> (u32, u32) { - if let Some(quo) = duo.checked_div(div) { - if let Some(rem) = duo.checked_rem(div) { - return (quo, rem); - } + if let Some(quo) = duo.checked_div(div) + && let Some(rem) = duo.checked_rem(div) + { + return (quo, rem); } zero_div_fn() } diff --git a/library/compiler-builtins/compiler-builtins/src/macros.rs b/library/compiler-builtins/compiler-builtins/src/macros.rs index 22e0dd27f2f5..203cd0949ac5 100644 --- a/library/compiler-builtins/compiler-builtins/src/macros.rs +++ b/library/compiler-builtins/compiler-builtins/src/macros.rs @@ -132,7 +132,7 @@ macro_rules! intrinsics { ) => ( #[cfg($name = "optimized-c")] pub $(unsafe $($empty)? )? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { - extern $abi { + unsafe extern $abi { fn $name($($argname: $ty),*) $(-> $ret)?; } unsafe { @@ -435,7 +435,7 @@ macro_rules! intrinsics { pub mod $name { #[unsafe(naked)] $(#[$($attr)*])* - #[cfg_attr(not(feature = "mangled-names"), no_mangle)] + #[cfg_attr(not(feature = "mangled-names"), unsafe(no_mangle))] #[cfg_attr(not(any(all(windows, target_env = "gnu"), target_os = "cygwin")), linkage = "weak")] pub unsafe extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { $($body)* diff --git a/library/compiler-builtins/compiler-builtins/src/probestack.rs b/library/compiler-builtins/compiler-builtins/src/probestack.rs index 5b6abd21a1db..c9070cf55c64 100644 --- a/library/compiler-builtins/compiler-builtins/src/probestack.rs +++ b/library/compiler-builtins/compiler-builtins/src/probestack.rs @@ -49,7 +49,9 @@ // We only define stack probing for these architectures today. #![cfg(any(target_arch = "x86_64", target_arch = "x86"))] -extern "C" { +// SAFETY: defined in this module. +// FIXME(extern_custom): the ABI is not correct. +unsafe extern "C" { pub fn __rust_probestack(); } From af16553893152ba74b0391b02c31f083c2f519b1 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 16:07:54 +0000 Subject: [PATCH 16/59] symcheck: Print the command to make reproducing errors easier --- .../crates/symbol-check/src/main.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index 4e6417fdf952..d83cd318d6a9 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -46,15 +46,16 @@ fn main() { /// Run `cargo build` with the provided additional arguments, collecting the list of created /// libraries. fn exec_cargo_with_args(args: &[&str]) -> Vec { - let mut cmd = Command::new("cargo") - .arg("build") + let mut cmd = Command::new("cargo"); + cmd.arg("build") .arg("--message-format=json") .args(args) - .stdout(Stdio::piped()) - .spawn() - .expect("failed to launch Cargo"); + .stdout(Stdio::piped()); - let stdout = cmd.stdout.take().unwrap(); + println!("running: {cmd:?}"); + let mut child = cmd.spawn().expect("failed to launch Cargo"); + + let stdout = child.stdout.take().unwrap(); let reader = BufReader::new(stdout); let mut check_files = Vec::new(); @@ -84,7 +85,7 @@ fn exec_cargo_with_args(args: &[&str]) -> Vec { } } - assert!(cmd.wait().expect("failed to wait on Cargo").success()); + assert!(child.wait().expect("failed to wait on Cargo").success()); assert!(!check_files.is_empty(), "no compiler_builtins rlibs found"); println!("Collected the following rlibs to check: {check_files:#?}"); From 7db8cf1fea87dd42082ac6eeb405e609628ac6b1 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 17:37:35 +0000 Subject: [PATCH 17/59] Add benchmarks for float parsing and printing As part of this, the u256 benchmarks are reorganized to a group. --- .../libm-test/benches/icount.rs | 100 +++++++++++++++--- 1 file changed, 83 insertions(+), 17 deletions(-) diff --git a/library/compiler-builtins/libm-test/benches/icount.rs b/library/compiler-builtins/libm-test/benches/icount.rs index da8c6bfd15a0..4bebbc41ccd8 100644 --- a/library/compiler-builtins/libm-test/benches/icount.rs +++ b/library/compiler-builtins/libm-test/benches/icount.rs @@ -1,9 +1,11 @@ //! Benchmarks that use `iai-cachegrind` to be reasonably CI-stable. +#![feature(f16)] +#![feature(f128)] use std::hint::black_box; use iai_callgrind::{library_benchmark, library_benchmark_group, main}; -use libm::support::{HInt, u256}; +use libm::support::{HInt, Hexf, hf16, hf32, hf64, hf128, u256}; use libm_test::generate::spaced; use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, OpRustArgs, TupleCall, op}; @@ -109,11 +111,6 @@ fn icount_bench_u128_widen_mul(cases: Vec<(u128, u128)>) { } } -library_benchmark_group!( - name = icount_bench_u128_widen_mul_group; - benchmarks = icount_bench_u128_widen_mul -); - #[library_benchmark] #[bench::linspace(setup_u256_add())] fn icount_bench_u256_add(cases: Vec<(u256, u256)>) { @@ -122,11 +119,6 @@ fn icount_bench_u256_add(cases: Vec<(u256, u256)>) { } } -library_benchmark_group!( - name = icount_bench_u256_add_group; - benchmarks = icount_bench_u256_add -); - #[library_benchmark] #[bench::linspace(setup_u256_shift())] fn icount_bench_u256_shr(cases: Vec<(u256, u32)>) { @@ -136,16 +128,90 @@ fn icount_bench_u256_shr(cases: Vec<(u256, u32)>) { } library_benchmark_group!( - name = icount_bench_u256_shr_group; - benchmarks = icount_bench_u256_shr + name = icount_bench_u128_group; + benchmarks = icount_bench_u128_widen_mul, icount_bench_u256_add, icount_bench_u256_shr +); + +#[library_benchmark] +#[bench::short("0x12.34p+8")] +#[bench::max("0x1.ffcp+15")] +fn icount_bench_hf16(s: &str) -> f16 { + black_box(hf16(s)) +} + +#[library_benchmark] +#[bench::short("0x12.34p+8")] +#[bench::max("0x1.fffffep+127")] +fn icount_bench_hf32(s: &str) -> f32 { + black_box(hf32(s)) +} + +#[library_benchmark] +#[bench::short("0x12.34p+8")] +#[bench::max("0x1.fffffffffffffp+1023")] +fn icount_bench_hf64(s: &str) -> f64 { + black_box(hf64(s)) +} + +#[library_benchmark] +#[bench::short("0x12.34p+8")] +#[bench::max("0x1.ffffffffffffffffffffffffffffp+16383")] +fn icount_bench_hf128(s: &str) -> f128 { + black_box(hf128(s)) +} + +library_benchmark_group!( + name = icount_bench_hf_parse_group; + benchmarks = + icount_bench_hf16, + icount_bench_hf32, + icount_bench_hf64, + icount_bench_hf128 +); + +#[library_benchmark] +#[bench::short(1.015625)] +#[bench::max(f16::MAX)] +fn icount_bench_print_hf16(x: f16) -> String { + black_box(Hexf(x).to_string()) +} + +#[library_benchmark] +#[bench::short(1.015625)] +#[bench::max(f32::MAX)] +fn icount_bench_print_hf32(x: f32) -> String { + black_box(Hexf(x).to_string()) +} + +#[library_benchmark] +#[bench::short(1.015625)] +#[bench::max(f64::MAX)] +fn icount_bench_print_hf64(x: f64) -> String { + black_box(Hexf(x).to_string()) +} + +#[library_benchmark] +#[bench::short(1.015625)] +#[bench::max(f128::MAX)] +fn icount_bench_print_hf128(x: f128) -> String { + black_box(Hexf(x).to_string()) +} + +library_benchmark_group!( + name = icount_bench_hf_print_group; + benchmarks = + icount_bench_print_hf16, + icount_bench_print_hf32, + icount_bench_print_hf64, + icount_bench_print_hf128 ); main!( library_benchmark_groups = - // u256-related benchmarks - icount_bench_u128_widen_mul_group, - icount_bench_u256_add_group, - icount_bench_u256_shr_group, + // Benchmarks not related to public libm math + icount_bench_u128_group, + icount_bench_hf_parse_group, + icount_bench_hf_print_group, // verify-apilist-start // verify-sorted-start icount_bench_acos_group, From c136fb7734716a078cd82a7364245128923eb7e4 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 19 Mar 2025 04:10:03 +0000 Subject: [PATCH 18/59] Run `builtins-test-intrinsics` when possible Currently we only build this, but it is possible to run the binary. Change the CI script to do so here. --- .../builtins-test-intrinsics/src/main.rs | 6 ++-- library/compiler-builtins/ci/run.sh | 30 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/library/compiler-builtins/builtins-test-intrinsics/src/main.rs b/library/compiler-builtins/builtins-test-intrinsics/src/main.rs index 96fe4a738511..66744a0817fe 100644 --- a/library/compiler-builtins/builtins-test-intrinsics/src/main.rs +++ b/library/compiler-builtins/builtins-test-intrinsics/src/main.rs @@ -13,6 +13,8 @@ #![no_std] #![no_main] +// Ensure this `compiler_builtins` gets used, rather than the version injected from the sysroot. +extern crate compiler_builtins; extern crate panic_handler; // SAFETY: no definitions, only used for linking @@ -652,14 +654,14 @@ fn something_with_a_dtor(f: &dyn Fn()) { #[unsafe(no_mangle)] #[cfg(not(thumb))] -fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { run(); 0 } #[unsafe(no_mangle)] #[cfg(thumb)] -pub fn _start() -> ! { +extern "C" fn _start() -> ! { run(); loop {} } diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index cf3f7dfda1da..27b9686eac64 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -63,23 +63,33 @@ symcheck+=(-- build-and-check) "${symcheck[@]}" -p compiler_builtins --target "$target" --features no-f16-f128 "${symcheck[@]}" -p compiler_builtins --target "$target" --features no-f16-f128 --release -build_intrinsics_test() { - # symcheck also checks the results of builtins-test-intrinsics - "${symcheck[@]}" \ +run_intrinsics_test() { + args=( --target "$target" --verbose \ - --manifest-path builtins-test-intrinsics/Cargo.toml "$@" + --manifest-path builtins-test-intrinsics/Cargo.toml + ) + args+=( "$@" ) + + # symcheck also checks the results of builtins-test-intrinsics + "${symcheck[@]}" "${args[@]}" + + # FIXME: we get access violations on Windows, our entrypoint may need to + # be tweaked. + if [ "${BUILD_ONLY:-}" != "1" ] && ! [[ "$target" = *"windows"* ]]; then + cargo run "${args[@]}" + fi } # Verify that we haven't dropped any intrinsics/symbols -build_intrinsics_test -build_intrinsics_test --release -build_intrinsics_test --features c -build_intrinsics_test --features c --release +run_intrinsics_test +run_intrinsics_test --release +run_intrinsics_test --features c +run_intrinsics_test --features c --release # Verify that there are no undefined symbols to `panic` within our # implementations -CARGO_PROFILE_DEV_LTO=true build_intrinsics_test -CARGO_PROFILE_RELEASE_LTO=true build_intrinsics_test --release +CARGO_PROFILE_DEV_LTO=true run_intrinsics_test +CARGO_PROFILE_RELEASE_LTO=true run_intrinsics_test --release # Test libm From 3464b4b231e3e4a09e102696228fe950eb1672e5 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 19:01:03 +0000 Subject: [PATCH 19/59] ci: Allow concurrency outside of pull requests When multiple merges to `master` happen before a CI run completes, the in-progress job is getting canceled. Fix this by using the commit sha for the group key if a pull request number is not available, rather than `github.ref` (which is always `refs/head/master` after merge). This should prevent jobs running on previous commits from getting cancelled, while still ensuring there is only ever one active run per pull request. --- library/compiler-builtins/.github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 567ad1205a5d..de433d8c74d5 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -5,7 +5,7 @@ on: concurrency: # Make sure that new pushes cancel running jobs - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true env: From 9f84e9904fd2be7e6b2c339b16ac9c2e5da8778b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 18:06:43 +0000 Subject: [PATCH 20/59] Increase the benchmark rustc version to 2025-05-28 We may soon want to use some new nightly features in `compiler-builtins` and `libm`, specifically `cfg_target_has_reliable_f16_f128` which was added in the past few weeks. This will mean we need a newer toolchain for benchmarks to continue building. Bump to the current latest nightly so we are not blocked on this down the line. --- library/compiler-builtins/.github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index de433d8c74d5..8e89cb472924 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -13,7 +13,7 @@ env: RUSTDOCFLAGS: -Dwarnings RUSTFLAGS: -Dwarnings RUST_BACKTRACE: full - BENCHMARK_RUSTC: nightly-2025-01-16 # Pin the toolchain for reproducable results + BENCHMARK_RUSTC: nightly-2025-05-28 # Pin the toolchain for reproducable results jobs: # Determine which tests should be run based on changed files. From 8edaa6e5c88e14df20b095016514d381c697e49a Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 20:53:48 +0000 Subject: [PATCH 21/59] libm-test: Make `extensive` an attribute rather than a test type Currently we run logspace tests for extensive tests, but there isn't any reason we couldn't also run more kinds of tests more extensively (e.g. more edge cases, combine edge cases with logspace for multi-input functions, etc). As a first step toward making this possible, make `extensive` a new field in `CheckCtx`, and rename `QuickSpaced` to `Spaced`. --- .../libm-test/benches/icount.rs | 2 +- .../libm-test/examples/plot_domains.rs | 2 +- .../libm-test/src/run_cfg.rs | 74 ++++++++++++++----- .../libm-test/tests/compare_built_musl.rs | 2 +- .../libm-test/tests/multiprecision.rs | 2 +- .../libm-test/tests/z_extensive/run.rs | 3 +- 6 files changed, 60 insertions(+), 25 deletions(-) diff --git a/library/compiler-builtins/libm-test/benches/icount.rs b/library/compiler-builtins/libm-test/benches/icount.rs index 4bebbc41ccd8..a0928a29f999 100644 --- a/library/compiler-builtins/libm-test/benches/icount.rs +++ b/library/compiler-builtins/libm-test/benches/icount.rs @@ -23,7 +23,7 @@ macro_rules! icount_benches { let mut ctx = CheckCtx::new( Op::IDENTIFIER, CheckBasis::None, - GeneratorKind::QuickSpaced + GeneratorKind::Spaced ); ctx.override_iterations(BENCH_ITER_ITEMS); let ret = spaced::get_test_cases::(&ctx).0.collect::>(); diff --git a/library/compiler-builtins/libm-test/examples/plot_domains.rs b/library/compiler-builtins/libm-test/examples/plot_domains.rs index 3563103b8cd7..7331d454f211 100644 --- a/library/compiler-builtins/libm-test/examples/plot_domains.rs +++ b/library/compiler-builtins/libm-test/examples/plot_domains.rs @@ -55,7 +55,7 @@ where Op: MathOp, Op::RustArgs: SpacedInput, { - let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::QuickSpaced); + let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::Spaced); plot_one_generator( out_dir, &ctx, diff --git a/library/compiler-builtins/libm-test/src/run_cfg.rs b/library/compiler-builtins/libm-test/src/run_cfg.rs index 3345a01d2de7..90f81195c856 100644 --- a/library/compiler-builtins/libm-test/src/run_cfg.rs +++ b/library/compiler-builtins/libm-test/src/run_cfg.rs @@ -22,13 +22,38 @@ static EXTENSIVE_ITER_OVERRIDE: LazyLock> = LazyLock::new(|| { /// Specific tests that need to have a reduced amount of iterations to complete in a reasonable /// amount of time. -/// -/// Contains the itentifier+generator combo to match on, plus the factor to reduce by. -const EXTEMELY_SLOW_TESTS: &[(Identifier, GeneratorKind, u64)] = &[ - (Identifier::Fmodf128, GeneratorKind::QuickSpaced, 50), - (Identifier::Fmodf128, GeneratorKind::Extensive, 50), +const EXTREMELY_SLOW_TESTS: &[SlowTest] = &[ + SlowTest { + ident: Identifier::Fmodf128, + gen_kind: GeneratorKind::Spaced, + extensive: false, + reduce_factor: 50, + }, + SlowTest { + ident: Identifier::Fmodf128, + gen_kind: GeneratorKind::Spaced, + extensive: true, + reduce_factor: 50, + }, ]; +/// A pattern to match a `CheckCtx`, plus a factor to reduce by. +struct SlowTest { + ident: Identifier, + gen_kind: GeneratorKind, + extensive: bool, + reduce_factor: u64, +} + +impl SlowTest { + /// True if the test in `CheckCtx` should be reduced by `reduce_factor`. + fn matches_ctx(&self, ctx: &CheckCtx) -> bool { + self.ident == ctx.fn_ident + && self.gen_kind == ctx.gen_kind + && self.extensive == ctx.extensive + } +} + /// Maximum number of iterations to run for a single routine. /// /// The default value of one greater than `u32::MAX` allows testing single-argument `f32` routines @@ -54,6 +79,7 @@ pub struct CheckCtx { /// Source of truth for tests. pub basis: CheckBasis, pub gen_kind: GeneratorKind, + pub extensive: bool, /// If specified, this value will override the value returned by [`iteration_count`]. pub override_iterations: Option, } @@ -69,12 +95,19 @@ impl CheckCtx { base_name_str: fn_ident.base_name().as_str(), basis, gen_kind, + extensive: false, override_iterations: None, }; ret.ulp = crate::default_ulp(&ret); ret } + /// Configure that this is an extensive test. + pub fn extensive(mut self, extensive: bool) -> Self { + self.extensive = extensive; + self + } + /// The number of input arguments for this function. pub fn input_count(&self) -> usize { self.fn_ident.math_op().rust_sig.args.len() @@ -100,14 +133,17 @@ pub enum CheckBasis { /// and quantity. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum GeneratorKind { + /// Extremes, zeros, nonstandard numbers, etc. EdgeCases, - Extensive, - QuickSpaced, + /// Spaced by logarithm (floats) or linear (integers). + Spaced, + /// Test inputs from an RNG. Random, + /// A provided test case list. List, } -/// A list of all functions that should get extensive tests. +/// A list of all functions that should get extensive tests, as configured by environment variable. /// /// This also supports the special test name `all` to run all tests, as well as `all_f16`, /// `all_f32`, `all_f64`, and `all_f128` to run all tests for a specific float type. @@ -216,17 +252,17 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 { let random_iter_count = domain_iter_count / 100; let mut total_iterations = match ctx.gen_kind { - GeneratorKind::QuickSpaced => domain_iter_count, + GeneratorKind::Spaced if ctx.extensive => extensive_max_iterations(), + GeneratorKind::Spaced => domain_iter_count, GeneratorKind::Random => random_iter_count, - GeneratorKind::Extensive => extensive_max_iterations(), GeneratorKind::EdgeCases | GeneratorKind::List => { unimplemented!("shoudn't need `iteration_count` for {:?}", ctx.gen_kind) } }; // Larger float types get more iterations. - if t_env.large_float_ty && ctx.gen_kind != GeneratorKind::Extensive { - if ctx.gen_kind == GeneratorKind::Extensive { + if t_env.large_float_ty { + if ctx.extensive { // Extensive already has a pretty high test count. total_iterations *= 2; } else { @@ -244,13 +280,13 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 { } // Some tests are significantly slower than others and need to be further reduced. - if let Some((_id, _gen, scale)) = EXTEMELY_SLOW_TESTS + if let Some(slow) = EXTREMELY_SLOW_TESTS .iter() - .find(|(id, generator, _scale)| *id == ctx.fn_ident && *generator == ctx.gen_kind) + .find(|slow| slow.matches_ctx(ctx)) { // However, do not override if the extensive iteration count has been manually set. - if !(ctx.gen_kind == GeneratorKind::Extensive && EXTENSIVE_ITER_OVERRIDE.is_some()) { - total_iterations /= scale; + if !(ctx.extensive && EXTENSIVE_ITER_OVERRIDE.is_some()) { + total_iterations /= slow.reduce_factor; } } @@ -279,7 +315,7 @@ pub fn iteration_count(ctx: &CheckCtx, argnum: usize) -> u64 { let total = ntests.pow(t_env.input_count.try_into().unwrap()); let seed_msg = match ctx.gen_kind { - GeneratorKind::QuickSpaced | GeneratorKind::Extensive => String::new(), + GeneratorKind::Spaced => String::new(), GeneratorKind::Random => { format!( " using `{SEED_ENV}={}`", @@ -327,8 +363,8 @@ pub fn int_range(ctx: &CheckCtx, argnum: usize) -> RangeInclusive { let extensive_range = (-0xfff)..=0xfffff; match ctx.gen_kind { - GeneratorKind::Extensive => extensive_range, - GeneratorKind::QuickSpaced | GeneratorKind::Random => non_extensive_range, + _ if ctx.extensive => extensive_range, + GeneratorKind::Spaced | GeneratorKind::Random => non_extensive_range, GeneratorKind::EdgeCases => extensive_range, GeneratorKind::List => unimplemented!("shoudn't need range for {:?}", ctx.gen_kind), } diff --git a/library/compiler-builtins/libm-test/tests/compare_built_musl.rs b/library/compiler-builtins/libm-test/tests/compare_built_musl.rs index 6ccbb6f4c51d..86f3b8b711ea 100644 --- a/library/compiler-builtins/libm-test/tests/compare_built_musl.rs +++ b/library/compiler-builtins/libm-test/tests/compare_built_musl.rs @@ -65,7 +65,7 @@ macro_rules! musl_tests { $(#[$attr])* fn [< musl_quickspace_ $fn_name >]() { type Op = libm_test::op::$fn_name::Routine; - let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::QuickSpaced); + let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Spaced); let cases = spaced::get_test_cases::(&ctx).0; musl_runner::(&ctx, cases, musl_math_sys::$fn_name); } diff --git a/library/compiler-builtins/libm-test/tests/multiprecision.rs b/library/compiler-builtins/libm-test/tests/multiprecision.rs index 80b2c78688ea..60175ae61569 100644 --- a/library/compiler-builtins/libm-test/tests/multiprecision.rs +++ b/library/compiler-builtins/libm-test/tests/multiprecision.rs @@ -55,7 +55,7 @@ macro_rules! mp_tests { $(#[$attr])* fn [< mp_quickspace_ $fn_name >]() { type Op = libm_test::op::$fn_name::Routine; - let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::QuickSpaced); + let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Spaced); let cases = spaced::get_test_cases::(&ctx).0; mp_runner::(&ctx, cases); } diff --git a/library/compiler-builtins/libm-test/tests/z_extensive/run.rs b/library/compiler-builtins/libm-test/tests/z_extensive/run.rs index 59c806ce73e2..f2ba6a4a0e3e 100644 --- a/library/compiler-builtins/libm-test/tests/z_extensive/run.rs +++ b/library/compiler-builtins/libm-test/tests/z_extensive/run.rs @@ -17,7 +17,6 @@ use rayon::prelude::*; use spaced::SpacedInput; const BASIS: CheckBasis = CheckBasis::Mpfr; -const GEN_KIND: GeneratorKind = GeneratorKind::Extensive; /// Run the extensive test suite. pub fn run() { @@ -77,7 +76,7 @@ where Op::RustArgs: SpacedInput + Send, { let test_name = format!("mp_extensive_{}", Op::NAME); - let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GEN_KIND); + let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Spaced).extensive(true); let skip = skip_extensive_test(&ctx); let runner = move || { From 4d325e8b2cca0139370a809a76183a880db52b24 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 21:22:47 +0000 Subject: [PATCH 22/59] ci: Allow for multiple icount benchmarks in the same run We don't actually need this for now, but eventually it would be nice to run icount benchmarks on multiple targets. Start tagging artifact names with the architecture, and allow passing `--tag` to `ci-util.py` in order to retrieve the correct one. --- .../.github/workflows/main.yaml | 12 ++++++++++-- library/compiler-builtins/ci/bench-icount.sh | 16 ++++++++++++++-- library/compiler-builtins/ci/ci-util.py | 17 +++++++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 8e89cb472924..9f389d8b4857 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -195,8 +195,14 @@ jobs: benchmarks: name: Benchmarks - runs-on: ubuntu-24.04 timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-24.04 + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@master with: @@ -215,12 +221,14 @@ jobs: cargo binstall -y iai-callgrind-runner --version "$iai_version" sudo apt-get install valgrind - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} - name: Run icount benchmarks env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} - run: ./ci/bench-icount.sh + run: ./ci/bench-icount.sh ${{ matrix.target }} - name: Upload the benchmark baseline uses: actions/upload-artifact@v4 diff --git a/library/compiler-builtins/ci/bench-icount.sh b/library/compiler-builtins/ci/bench-icount.sh index 4d93e257a6cb..5b6974fe4204 100755 --- a/library/compiler-builtins/ci/bench-icount.sh +++ b/library/compiler-builtins/ci/bench-icount.sh @@ -2,10 +2,21 @@ set -eux +target="${1:-}" + +if [ -z "$target" ]; then + host_target=$(rustc -vV | awk '/^host/ { print $2 }') + echo "Defaulted to host target $host_target" + target="$host_target" +fi + iai_home="iai-home" +# Use the arch as a tag to disambiguate artifacts +tag="$(echo "$target" | cut -d'-' -f1)" + # Download the baseline from master -./ci/ci-util.py locate-baseline --download --extract +./ci/ci-util.py locate-baseline --download --extract --tag "$tag" # Run benchmarks once function run_icount_benchmarks() { @@ -44,6 +55,7 @@ function run_icount_benchmarks() { # If this is for a pull request, ignore regressions if specified. ./ci/ci-util.py check-regressions --home "$iai_home" --allow-pr-override "$PR_NUMBER" else + # Disregard regressions after merge ./ci/ci-util.py check-regressions --home "$iai_home" || true fi } @@ -53,6 +65,6 @@ run_icount_benchmarks --features force-soft-floats -- --save-baseline=softfloat run_icount_benchmarks -- --save-baseline=hardfloat # Name and tar the new baseline -name="baseline-icount-$(date -u +'%Y%m%d%H%M')-${GITHUB_SHA:0:12}" +name="baseline-icount-$tag-$(date -u +'%Y%m%d%H%M')-${GITHUB_SHA:0:12}" echo "BASELINE_NAME=$name" >>"$GITHUB_ENV" tar cJf "$name.tar.xz" "$iai_home" diff --git a/library/compiler-builtins/ci/ci-util.py b/library/compiler-builtins/ci/ci-util.py index d785b2e9e1d8..6c8b439805b4 100755 --- a/library/compiler-builtins/ci/ci-util.py +++ b/library/compiler-builtins/ci/ci-util.py @@ -28,11 +28,14 @@ USAGE = cleandoc( Calculate a matrix of which functions had source change, print that as a JSON object. - locate-baseline [--download] [--extract] + locate-baseline [--download] [--extract] [--tag TAG] Locate the most recent benchmark baseline available in CI and, if flags specify, download and extract it. Never exits with nonzero status if downloading fails. + `--tag` can be specified to look for artifacts with a specific tag, such as + for a specific architecture. + Note that `--extract` will overwrite files in `iai-home`. check-regressions [--home iai-home] [--allow-pr-override pr_number] @@ -50,7 +53,7 @@ REPO_ROOT = Path(__file__).parent.parent GIT = ["git", "-C", REPO_ROOT] DEFAULT_BRANCH = "master" WORKFLOW_NAME = "CI" # Workflow that generates the benchmark artifacts -ARTIFACT_GLOB = "baseline-icount*" +ARTIFACT_PREFIX = "baseline-icount*" # Place this in a PR body to skip regression checks (must be at the start of a line). REGRESSION_DIRECTIVE = "ci: allow-regressions" # Place this in a PR body to skip extensive tests @@ -278,6 +281,7 @@ def locate_baseline(flags: list[str]) -> None: download = False extract = False + tag = "" while len(flags) > 0: match flags[0]: @@ -285,6 +289,9 @@ def locate_baseline(flags: list[str]) -> None: download = True case "--extract": extract = True + case "--tag": + tag = flags[1] + flags = flags[1:] case _: eprint(USAGE) exit(1) @@ -333,8 +340,10 @@ def locate_baseline(flags: list[str]) -> None: eprint("skipping download step") return + artifact_glob = f"{ARTIFACT_PREFIX}{f"-{tag}" if tag else ""}*" + sp.run( - ["gh", "run", "download", str(job_id), f"--pattern={ARTIFACT_GLOB}"], + ["gh", "run", "download", str(job_id), f"--pattern={artifact_glob}"], check=False, ) @@ -344,7 +353,7 @@ def locate_baseline(flags: list[str]) -> None: # Find the baseline with the most recent timestamp. GH downloads the files to e.g. # `some-dirname/some-dirname.tar.xz`, so just glob the whole thing together. - candidate_baselines = glob(f"{ARTIFACT_GLOB}/{ARTIFACT_GLOB}") + candidate_baselines = glob(f"{artifact_glob}/{artifact_glob}") if len(candidate_baselines) == 0: eprint("no possible baseline directories found") return From 14db1b5b1c2b55dfa89c7b958e70efb936a33e7c Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Wed, 28 May 2025 21:22:21 +0000 Subject: [PATCH 23/59] jsondocck: command -> directive --- src/tools/jsondocck/src/config.rs | 2 +- src/tools/jsondocck/src/directive.rs | 44 +++++++++++++++------------- src/tools/jsondocck/src/error.rs | 4 +-- src/tools/jsondocck/src/main.rs | 30 +++++++++---------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/tools/jsondocck/src/config.rs b/src/tools/jsondocck/src/config.rs index 9b3ba3f3fbe3..6bef37c22597 100644 --- a/src/tools/jsondocck/src/config.rs +++ b/src/tools/jsondocck/src/config.rs @@ -4,7 +4,7 @@ use getopts::Options; pub struct Config { /// The directory documentation output was generated in pub doc_dir: String, - /// The file documentation was generated for, with docck commands to check + /// The file documentation was generated for, with docck directives to check pub template: String, } diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs index 8617379ed270..5269ffb469fa 100644 --- a/src/tools/jsondocck/src/directive.rs +++ b/src/tools/jsondocck/src/directive.rs @@ -1,16 +1,18 @@ use std::borrow::Cow; + use serde_json::Value; + use crate::cache::Cache; #[derive(Debug)] -pub struct Command { - pub kind: CommandKind, +pub struct Directive { + pub kind: DirectiveKind, pub path: String, pub lineno: usize, } #[derive(Debug)] -pub enum CommandKind { +pub enum DirectiveKind { /// `//@ has ` /// /// Checks the path exists. @@ -55,16 +57,16 @@ pub enum CommandKind { Set { variable: String }, } -impl CommandKind { +impl DirectiveKind { /// Returns both the kind and the path. /// - /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest). + /// Returns `None` if the directive isn't from jsondocck (e.g. from compiletest). pub fn parse<'a>( - command_name: &str, + directive_name: &str, negated: bool, args: &'a [String], ) -> Option<(Self, &'a str)> { - let kind = match (command_name, negated) { + let kind = match (directive_name, negated) { ("count", false) => { assert_eq!(args.len(), 2); let expected = args[1].parse().expect("invalid number for `count`"); @@ -104,11 +106,11 @@ impl CommandKind { _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), }, - (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => { + (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => { return None; } _ => { - panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" }) + panic!("Invalid directive `//@ {}{directive_name}`", if negated { "!" } else { "" }) } }; @@ -116,22 +118,22 @@ impl CommandKind { } } -impl Command { - /// Performs the actual work of ensuring a command passes. +impl Directive { + /// Performs the actual work of ensuring a directive passes. pub fn check(&self, cache: &mut Cache) -> Result<(), String> { let matches = cache.select(&self.path); match &self.kind { - CommandKind::HasPath => { + DirectiveKind::HasPath => { if matches.is_empty() { return Err("matched to no values".to_owned()); } } - CommandKind::HasNotPath => { + DirectiveKind::HasNotPath => { if !matches.is_empty() { return Err(format!("matched to {matches:?}, but wanted no matches")); } } - CommandKind::HasValue { value } => { + DirectiveKind::HasValue { value } => { let want_value = string_to_value(value, cache); if !matches.contains(&want_value.as_ref()) { return Err(format!( @@ -139,7 +141,7 @@ impl Command { )); } } - CommandKind::HasNotValue { value } => { + DirectiveKind::HasNotValue { value } => { let wantnt_value = string_to_value(value, cache); if matches.contains(&wantnt_value.as_ref()) { return Err(format!( @@ -152,14 +154,14 @@ impl Command { } } - CommandKind::Is { value } => { + DirectiveKind::Is { value } => { let want_value = string_to_value(value, cache); let matched = get_one(&matches)?; if matched != want_value.as_ref() { return Err(format!("matched to {matched:?} but want {want_value:?}")); } } - CommandKind::IsNot { value } => { + DirectiveKind::IsNot { value } => { let wantnt_value = string_to_value(value, cache); let matched = get_one(&matches)?; if matched == wantnt_value.as_ref() { @@ -167,7 +169,7 @@ impl Command { } } - CommandKind::IsMany { values } => { + DirectiveKind::IsMany { values } => { // Serde json doesn't implement Ord or Hash for Value, so we must // use a Vec here. While in theory that makes setwize equality // O(n^2), in practice n will never be large enough to matter. @@ -187,7 +189,7 @@ impl Command { } } } - CommandKind::CountIs { expected } => { + DirectiveKind::CountIs { expected } => { if *expected != matches.len() { return Err(format!( "matched to `{matches:?}` with length {}, but expected length {expected}", @@ -195,7 +197,7 @@ impl Command { )); } } - CommandKind::Set { variable } => { + DirectiveKind::Set { variable } => { let value = get_one(&matches)?; let r = cache.variables.insert(variable.to_owned(), value.clone()); assert!(r.is_none(), "name collision: {variable:?} is duplicated"); @@ -227,4 +229,4 @@ fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { } else { Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s))) } -} \ No newline at end of file +} diff --git a/src/tools/jsondocck/src/error.rs b/src/tools/jsondocck/src/error.rs index 0a3e085b405b..eb2932f78032 100644 --- a/src/tools/jsondocck/src/error.rs +++ b/src/tools/jsondocck/src/error.rs @@ -1,7 +1,7 @@ -use crate::Command; +use crate::Directive; #[derive(Debug)] pub struct CkError { pub message: String, - pub command: Command, + pub directive: Directive, } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 80557b682430..d84be4d3a3a6 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -11,7 +11,7 @@ mod error; use cache::Cache; use config::parse_config; -use directive::{Command, CommandKind}; +use directive::{Directive, DirectiveKind}; use error::CkError; fn main() -> ExitCode { @@ -19,14 +19,14 @@ fn main() -> ExitCode { let mut failed = Vec::new(); let mut cache = Cache::new(&config); - let Ok(commands) = get_commands(&config.template) else { + let Ok(directives) = get_directives(&config.template) else { eprintln!("Jsondocck failed for {}", &config.template); return ExitCode::FAILURE; }; - for command in commands { - if let Err(message) = command.check( &mut cache) { - failed.push(CkError { command, message }); + for directive in directives { + if let Err(message) = directive.check(&mut cache) { + failed.push(CkError { directive, message }); } } @@ -34,7 +34,7 @@ fn main() -> ExitCode { ExitCode::SUCCESS } else { for i in failed { - eprintln!("{}:{}, command failed", config.template, i.command.lineno); + eprintln!("{}:{}, directive failed", config.template, i.directive.lineno); eprintln!("{}", i.message) } ExitCode::FAILURE @@ -47,7 +47,7 @@ static LINE_PATTERN: LazyLock = LazyLock::new(|| { ^\s* //@\s+ (?P!?) - (?P[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) + (?P[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) (?P.*)$ "#, ) @@ -70,12 +70,12 @@ static DEPRECATED_LINE_PATTERN: LazyLock = LazyLock::new(|| { }); fn print_err(msg: &str, lineno: usize) { - eprintln!("Invalid command: {} on line {}", msg, lineno) + eprintln!("Invalid directive: {} on line {}", msg, lineno) } -/// Get a list of commands from a file. -fn get_commands(template: &str) -> Result, ()> { - let mut commands = Vec::new(); +/// Get a list of directives from a file. +fn get_directives(template: &str) -> Result, ()> { + let mut directives = Vec::new(); let mut errors = false; let file = fs::read_to_string(template).unwrap(); @@ -83,7 +83,7 @@ fn get_commands(template: &str) -> Result, ()> { let lineno = lineno + 1; if DEPRECATED_LINE_PATTERN.is_match(line) { - print_err("Deprecated command syntax, replace `// @` with `//@ `", lineno); + print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno); errors = true; continue; } @@ -101,10 +101,10 @@ fn get_commands(template: &str) -> Result, ()> { continue; }; - if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) { - commands.push(Command { kind, lineno, path: path.to_owned() }) + if let Some((kind, path)) = DirectiveKind::parse(&cap["directive"], negated, &args) { + directives.push(Directive { kind, lineno, path: path.to_owned() }) } } - if !errors { Ok(commands) } else { Err(()) } + if !errors { Ok(directives) } else { Err(()) } } From 3c35505fe9c1a0f2c15c780709ed9812f8a332f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 22:08:24 +0000 Subject: [PATCH 24/59] chore: release --- .../compiler-builtins/CHANGELOG.md | 15 +++++++++++++++ .../compiler-builtins/Cargo.toml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/library/compiler-builtins/compiler-builtins/CHANGELOG.md b/library/compiler-builtins/compiler-builtins/CHANGELOG.md index a7c01c463ca6..880e56c443e3 100644 --- a/library/compiler-builtins/compiler-builtins/CHANGELOG.md +++ b/library/compiler-builtins/compiler-builtins/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.160](https://github.com/rust-lang/compiler-builtins/compare/compiler_builtins-v0.1.159...compiler_builtins-v0.1.160) - 2025-05-29 + +### Other + +- Change `compiler-builtins` to edition 2024 +- Remove unneeded C symbols +- Reuse `libm`'s `Caat` and `CastFrom` in `compiler-builtins` +- Reuse `MinInt` and `Int` from `libm` in `compiler-builtins` +- Update `CmpResult` to use a pointer-sized return type +- Enable `__powitf2` on MSVC +- Fix `i256::MAX` +- Add a note saying why we use `frintx` rather than `frintn` +- Typo in README.md +- Clean up unused files + ## [0.1.159](https://github.com/rust-lang/compiler-builtins/compare/compiler_builtins-v0.1.158...compiler_builtins-v0.1.159) - 2025-05-12 ### Other diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 93eb3e01b3b8..8ceef286f57e 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Jorge Aparicio "] name = "compiler_builtins" -version = "0.1.159" +version = "0.1.160" license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" readme = "README.md" repository = "https://github.com/rust-lang/compiler-builtins" From 033ea63639e11e3af2bd33092129465e4b9d8113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 30 May 2025 20:34:55 +0200 Subject: [PATCH 25/59] Do not run PGO/BOLT in x64 Linux alt builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jakub Beránek --- .../host-x86_64/dist-x86_64-linux/Dockerfile | 15 +++++++-------- .../host-x86_64/dist-x86_64-linux/dist-alt.sh | 8 ++++++++ .../docker/host-x86_64/dist-x86_64-linux/dist.sh | 13 +++++++++++++ src/ci/github-actions/jobs.yml | 3 +++ 4 files changed, 31 insertions(+), 8 deletions(-) create mode 100755 src/ci/docker/host-x86_64/dist-x86_64-linux/dist-alt.sh create mode 100755 src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index bedf45c8630c..44f6a8d2a155 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -96,14 +96,13 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.lto=thin \ --set rust.codegen-units=1 -# Note that `rust.debug` is set to true *only* for `opt-dist` -ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ - ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ - --host $HOSTS --target $HOSTS \ - --include-default-paths \ - build-manifest bootstrap && \ - # Use GCC for building GCC, as it seems to behave badly when built with Clang - CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc +ARG SCRIPT_ARG + +COPY host-x86_64/dist-x86_64-linux/dist.sh /scripts/ +COPY host-x86_64/dist-x86_64-linux/dist-alt.sh /scripts/ + +ENV SCRIPT /scripts/${SCRIPT_ARG} + ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang # This is the only builder which will create source tarballs diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist-alt.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist-alt.sh new file mode 100755 index 000000000000..8e756c32431f --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist-alt.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -eux + +python3 ../x.py dist \ + --host $HOSTS --target $HOSTS \ + --include-default-paths \ + build-manifest bootstrap diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh new file mode 100755 index 000000000000..064ac5b0a5e4 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -eux + +python3 ../x.py build --set rust.debug=true opt-dist + +./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ + --host $HOSTS --target $HOSTS \ + --include-default-paths \ + build-manifest bootstrap + +# Use GCC for building GCC, as it seems to behave badly when built with Clang +CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index dc6a0e1ebad8..56863b839dfd 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -141,6 +141,7 @@ try: - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift + DOCKER_SCRIPT: dist.sh <<: *job-linux-36c-codebuild # Main CI jobs that have to be green to merge a commit into master @@ -237,12 +238,14 @@ auto: - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift + DOCKER_SCRIPT: dist.sh <<: *job-linux-36c-codebuild - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift + DOCKER_SCRIPT: dist-alt.sh <<: *job-linux-16c - name: dist-x86_64-musl From c6df6a72d8f2463985b6b130673536e6d4cc7cbf Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 1 Jun 2025 19:41:03 +0000 Subject: [PATCH 26/59] Fix new `dead_code` warnings from recent nightlies --- .../libm/src/math/support/float_traits.rs | 1 + .../libm/src/math/support/hex_float.rs | 239 +++++++++--------- .../libm/src/math/support/int_traits.rs | 1 + .../libm/src/math/support/macros.rs | 6 +- .../libm/src/math/support/mod.rs | 4 +- 5 files changed, 132 insertions(+), 119 deletions(-) diff --git a/library/compiler-builtins/libm/src/math/support/float_traits.rs b/library/compiler-builtins/libm/src/math/support/float_traits.rs index 4c866ef10bdd..dd9f46209c11 100644 --- a/library/compiler-builtins/libm/src/math/support/float_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/float_traits.rs @@ -6,6 +6,7 @@ use super::int_traits::{CastFrom, Int, MinInt}; /// Trait for some basic operations on floats // #[allow(dead_code)] +#[allow(dead_code)] // Some constants are only used with tests pub trait Float: Copy + fmt::Debug diff --git a/library/compiler-builtins/libm/src/math/support/hex_float.rs b/library/compiler-builtins/libm/src/math/support/hex_float.rs index 85569d98aef4..c8558b90053d 100644 --- a/library/compiler-builtins/libm/src/math/support/hex_float.rs +++ b/library/compiler-builtins/libm/src/math/support/hex_float.rs @@ -1,8 +1,6 @@ //! Utilities for working with hex float formats. -use core::fmt; - -use super::{Float, Round, Status, f32_from_bits, f64_from_bits}; +use super::{Round, Status, f32_from_bits, f64_from_bits}; /// Construct a 16-bit float from hex float representation (C-style) #[cfg(f16_enabled)] @@ -352,132 +350,142 @@ const fn u128_ilog2(v: u128) -> u32 { u128::BITS - 1 - v.leading_zeros() } -/// Format a floating point number as its IEEE hex (`%a`) representation. -pub struct Hexf(pub F); +#[cfg(any(test, feature = "unstable-public-internals"))] +mod hex_fmt { + use core::fmt; -// Adapted from https://github.com/ericseppanen/hexfloat2/blob/a5c27932f0ff/src/format.rs -#[cfg(not(feature = "compiler-builtins"))] -fn fmt_any_hex(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if x.is_sign_negative() { - write!(f, "-")?; + use crate::support::Float; + + /// Format a floating point number as its IEEE hex (`%a`) representation. + pub struct Hexf(pub F); + + // Adapted from https://github.com/ericseppanen/hexfloat2/blob/a5c27932f0ff/src/format.rs + #[cfg(not(feature = "compiler-builtins"))] + pub(super) fn fmt_any_hex(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if x.is_sign_negative() { + write!(f, "-")?; + } + + if x.is_nan() { + return write!(f, "NaN"); + } else if x.is_infinite() { + return write!(f, "inf"); + } else if *x == F::ZERO { + return write!(f, "0x0p+0"); + } + + let mut exponent = x.exp_unbiased(); + let sig = x.to_bits() & F::SIG_MASK; + + let bias = F::EXP_BIAS as i32; + // The mantissa MSB needs to be shifted up to the nearest nibble. + let mshift = (4 - (F::SIG_BITS % 4)) % 4; + let sig = sig << mshift; + // The width is rounded up to the nearest char (4 bits) + let mwidth = (F::SIG_BITS as usize + 3) / 4; + let leading = if exponent == -bias { + // subnormal number means we shift our output by 1 bit. + exponent += 1; + "0." + } else { + "1." + }; + + write!(f, "0x{leading}{sig:0mwidth$x}p{exponent:+}") } - if x.is_nan() { - return write!(f, "NaN"); - } else if x.is_infinite() { - return write!(f, "inf"); - } else if *x == F::ZERO { - return write!(f, "0x0p+0"); + #[cfg(feature = "compiler-builtins")] + pub(super) fn fmt_any_hex(_x: &F, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() } - let mut exponent = x.exp_unbiased(); - let sig = x.to_bits() & F::SIG_MASK; + impl fmt::LowerHex for Hexf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + cfg_if! { + if #[cfg(feature = "compiler-builtins")] { + let _ = f; + unimplemented!() + } else { + fmt_any_hex(&self.0, f) + } + } + } + } - let bias = F::EXP_BIAS as i32; - // The mantissa MSB needs to be shifted up to the nearest nibble. - let mshift = (4 - (F::SIG_BITS % 4)) % 4; - let sig = sig << mshift; - // The width is rounded up to the nearest char (4 bits) - let mwidth = (F::SIG_BITS as usize + 3) / 4; - let leading = if exponent == -bias { - // subnormal number means we shift our output by 1 bit. - exponent += 1; - "0." - } else { - "1." - }; + impl fmt::LowerHex for Hexf<(F, F)> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + cfg_if! { + if #[cfg(feature = "compiler-builtins")] { + let _ = f; + unimplemented!() + } else { + write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) + } + } + } + } - write!(f, "0x{leading}{sig:0mwidth$x}p{exponent:+}") -} + impl fmt::LowerHex for Hexf<(F, i32)> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + cfg_if! { + if #[cfg(feature = "compiler-builtins")] { + let _ = f; + unimplemented!() + } else { + write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) + } + } + } + } -#[cfg(feature = "compiler-builtins")] -fn fmt_any_hex(_x: &F, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() -} + impl fmt::LowerHex for Hexf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + cfg_if! { + if #[cfg(feature = "compiler-builtins")] { + let _ = f; + unimplemented!() + } else { + fmt::LowerHex::fmt(&self.0, f) + } + } + } + } -impl fmt::LowerHex for Hexf { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - cfg_if! { - if #[cfg(feature = "compiler-builtins")] { - let _ = f; - unimplemented!() - } else { - fmt_any_hex(&self.0, f) + impl fmt::Debug for Hexf + where + Hexf: fmt::LowerHex, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + cfg_if! { + if #[cfg(feature = "compiler-builtins")] { + let _ = f; + unimplemented!() + } else { + fmt::LowerHex::fmt(self, f) + } + } + } + } + + impl fmt::Display for Hexf + where + Hexf: fmt::LowerHex, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + cfg_if! { + if #[cfg(feature = "compiler-builtins")] { + let _ = f; + unimplemented!() + } else { + fmt::LowerHex::fmt(self, f) + } } } } } -impl fmt::LowerHex for Hexf<(F, F)> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - cfg_if! { - if #[cfg(feature = "compiler-builtins")] { - let _ = f; - unimplemented!() - } else { - write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) - } - } - } -} - -impl fmt::LowerHex for Hexf<(F, i32)> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - cfg_if! { - if #[cfg(feature = "compiler-builtins")] { - let _ = f; - unimplemented!() - } else { - write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1)) - } - } - } -} - -impl fmt::LowerHex for Hexf { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - cfg_if! { - if #[cfg(feature = "compiler-builtins")] { - let _ = f; - unimplemented!() - } else { - fmt::LowerHex::fmt(&self.0, f) - } - } - } -} - -impl fmt::Debug for Hexf -where - Hexf: fmt::LowerHex, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - cfg_if! { - if #[cfg(feature = "compiler-builtins")] { - let _ = f; - unimplemented!() - } else { - fmt::LowerHex::fmt(self, f) - } - } - } -} - -impl fmt::Display for Hexf -where - Hexf: fmt::LowerHex, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - cfg_if! { - if #[cfg(feature = "compiler-builtins")] { - let _ = f; - unimplemented!() - } else { - fmt::LowerHex::fmt(self, f) - } - } - } -} +#[cfg(any(test, feature = "unstable-public-internals"))] +pub use hex_fmt::*; #[cfg(test)] mod parse_tests { @@ -1064,6 +1072,7 @@ mod print_tests { use std::string::ToString; use super::*; + use crate::support::Float; #[test] #[cfg(f16_enabled)] diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs index 716af748a9fb..9b29e2f4561d 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs @@ -1,6 +1,7 @@ use core::{cmp, fmt, ops}; /// Minimal integer implementations needed on all integer types, including wide integers. +#[allow(dead_code)] // Some constants are only used with tests pub trait MinInt: Copy + fmt::Debug diff --git a/library/compiler-builtins/libm/src/math/support/macros.rs b/library/compiler-builtins/libm/src/math/support/macros.rs index 0b72db0e46e8..2b8fd580a50e 100644 --- a/library/compiler-builtins/libm/src/math/support/macros.rs +++ b/library/compiler-builtins/libm/src/math/support/macros.rs @@ -137,12 +137,12 @@ macro_rules! hf128 { #[cfg(test)] macro_rules! assert_biteq { ($left:expr, $right:expr, $($tt:tt)*) => {{ - use $crate::support::Int; let l = $left; let r = $right; - let bits = Int::leading_zeros(l.to_bits() - l.to_bits()); // hack to get the width from the value + // hack to get width from a value + let bits = $crate::support::Int::leading_zeros(l.to_bits() - l.to_bits()); assert!( - l.biteq(r), + $crate::support::Float::biteq(l, r), "{}\nl: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", format_args!($($tt)*), lb = l.to_bits(), diff --git a/library/compiler-builtins/libm/src/math/support/mod.rs b/library/compiler-builtins/libm/src/math/support/mod.rs index 2771cfd32c8b..2e7edd03c421 100644 --- a/library/compiler-builtins/libm/src/math/support/mod.rs +++ b/library/compiler-builtins/libm/src/math/support/mod.rs @@ -17,6 +17,8 @@ pub use env::{FpResult, Round, Status}; #[allow(unused_imports)] pub use float_traits::{DFloat, Float, HFloat, IntTy}; pub(crate) use float_traits::{f32_from_bits, f64_from_bits}; +#[cfg(any(test, feature = "unstable-public-internals"))] +pub use hex_float::Hexf; #[cfg(f16_enabled)] #[allow(unused_imports)] pub use hex_float::hf16; @@ -24,7 +26,7 @@ pub use hex_float::hf16; #[allow(unused_imports)] pub use hex_float::hf128; #[allow(unused_imports)] -pub use hex_float::{Hexf, hf32, hf64}; +pub use hex_float::{hf32, hf64}; pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt}; /// Hint to the compiler that the current path is cold. From 0a7e59265a3432d6544b35d93120b66f45a67159 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 1 Jun 2025 19:22:42 +0000 Subject: [PATCH 27/59] Upgrade all dependencies to the latest available version In particular, this includes a fix to `iai-callgrind` that will allow us to simplify our benchmark runner. --- library/compiler-builtins/builtins-test/Cargo.toml | 8 ++++---- .../compiler-builtins/compiler-builtins/Cargo.toml | 4 ++-- .../crates/libm-macros/Cargo.toml | 4 ++-- .../crates/musl-math-sys/Cargo.toml | 2 +- library/compiler-builtins/libm-test/Cargo.toml | 14 +++++++------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index 10978c0bb7ed..c7742aa24275 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -10,11 +10,11 @@ license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" # For fuzzing tests we want a deterministic seedable RNG. We also eliminate potential # problems with system RNGs on the variety of platforms this crate is tested on. # `xoshiro128**` is used for its quality, size, and speed at generating `u32` shift amounts. -rand_xoshiro = "0.6" +rand_xoshiro = "0.7" # To compare float builtins against -rustc_apfloat = "0.2.1" +rustc_apfloat = "0.2.2" # Really a dev dependency, but dev dependencies can't be optional -iai-callgrind = { version = "0.14.0", optional = true } +iai-callgrind = { version = "0.14.1", optional = true } [dependencies.compiler_builtins] path = "../compiler-builtins" @@ -22,7 +22,7 @@ default-features = false features = ["unstable-public-internals"] [dev-dependencies] -criterion = { version = "0.5.1", default-features = false, features = ["cargo_bench_support"] } +criterion = { version = "0.6.0", default-features = false, features = ["cargo_bench_support"] } paste = "1.0.15" [target.'cfg(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), target_os = "linux"))'.dev-dependencies] diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 8ceef286f57e..6bee8da68a0a 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -19,10 +19,10 @@ test = false [dependencies] # For more information on this dependency see # https://github.com/rust-lang/rust/tree/master/library/rustc-std-workspace-core -core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } +core = { version = "1.0.1", optional = true, package = "rustc-std-workspace-core" } [build-dependencies] -cc = { optional = true, version = "1.0" } +cc = { optional = true, version = "1.2" } [dev-dependencies] panic-handler = { path = "../crates/panic-handler" } diff --git a/library/compiler-builtins/crates/libm-macros/Cargo.toml b/library/compiler-builtins/crates/libm-macros/Cargo.toml index 3929854f08e6..6bbf47784ff9 100644 --- a/library/compiler-builtins/crates/libm-macros/Cargo.toml +++ b/library/compiler-builtins/crates/libm-macros/Cargo.toml @@ -10,9 +10,9 @@ proc-macro = true [dependencies] heck = "0.5.0" -proc-macro2 = "1.0.94" +proc-macro2 = "1.0.95" quote = "1.0.40" -syn = { version = "2.0.100", features = ["full", "extra-traits", "visit-mut"] } +syn = { version = "2.0.101", features = ["full", "extra-traits", "visit-mut"] } [lints.rust] # Values used during testing diff --git a/library/compiler-builtins/crates/musl-math-sys/Cargo.toml b/library/compiler-builtins/crates/musl-math-sys/Cargo.toml index d3fb147e526a..3b88117343b6 100644 --- a/library/compiler-builtins/crates/musl-math-sys/Cargo.toml +++ b/library/compiler-builtins/crates/musl-math-sys/Cargo.toml @@ -11,4 +11,4 @@ license = "MIT OR Apache-2.0" libm = { path = "../../libm" } [build-dependencies] -cc = "1.2.16" +cc = "1.2.25" diff --git a/library/compiler-builtins/libm-test/Cargo.toml b/library/compiler-builtins/libm-test/Cargo.toml index 7a306e735577..01b45716b7a2 100644 --- a/library/compiler-builtins/libm-test/Cargo.toml +++ b/library/compiler-builtins/libm-test/Cargo.toml @@ -28,28 +28,28 @@ icount = ["dep:iai-callgrind"] short-benchmarks = [] [dependencies] -anyhow = "1.0.97" +anyhow = "1.0.98" # This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`. -gmp-mpfr-sys = { version = "1.6.4", optional = true, default-features = false } -iai-callgrind = { version = "0.14.0", optional = true } +gmp-mpfr-sys = { version = "1.6.5", optional = true, default-features = false } +iai-callgrind = { version = "0.14.1", optional = true } indicatif = { version = "0.17.11", default-features = false } libm = { path = "../libm", features = ["unstable-public-internals"] } libm-macros = { path = "../crates/libm-macros" } musl-math-sys = { path = "../crates/musl-math-sys", optional = true } paste = "1.0.15" -rand = "0.9.0" +rand = "0.9.1" rand_chacha = "0.9.0" rayon = "1.10.0" rug = { version = "1.27.0", optional = true, default-features = false, features = ["float", "integer", "std"] } [target.'cfg(target_family = "wasm")'.dependencies] -getrandom = { version = "0.3.2", features = ["wasm_js"] } +getrandom = { version = "0.3.3", features = ["wasm_js"] } [build-dependencies] -rand = { version = "0.9.0", optional = true } +rand = { version = "0.9.1", optional = true } [dev-dependencies] -criterion = { version = "0.5.1", default-features = false, features = ["cargo_bench_support"] } +criterion = { version = "0.6.0", default-features = false, features = ["cargo_bench_support"] } libtest-mimic = "0.8.1" [[bench]] From a62b7c8300d1cbde146b5b66739342a62d428941 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Jun 2025 12:30:08 +0200 Subject: [PATCH 28/59] use a relative path, so that this also works offline --- src/doc/rustc-dev-guide/src/normalization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md index 9705b1a244a3..beeeb9e30b7f 100644 --- a/src/doc/rustc-dev-guide/src/normalization.md +++ b/src/doc/rustc-dev-guide/src/normalization.md @@ -271,7 +271,7 @@ Leaving the alias unnormalized would also be wrong as the old solver expects all Ultimately this means that it is not always possible to ensure all aliases inside of a value are rigid. -[universes]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html#what-is-a-universe +[universes]: borrow_check/region_inference/placeholders_and_universes.md#what-is-a-universe [deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize ## Handling uses of diverging aliases From 23285efeaba21dc3fcb8904ece44dffc09777d8a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Jun 2025 12:34:19 +0200 Subject: [PATCH 29/59] distracting indirection --- src/doc/rustc-dev-guide/src/normalization.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md index beeeb9e30b7f..eb0962a41223 100644 --- a/src/doc/rustc-dev-guide/src/normalization.md +++ b/src/doc/rustc-dev-guide/src/normalization.md @@ -265,13 +265,13 @@ Another problem was that it was not possible to normalize `ParamEnv`s correctly Given a type such as `for<'a> fn(::Assoc>)`, it is not possible to correctly handle this with the old solver's approach to normalization. -If we were to normalize it to `for<'a> fn(?y)` and register a goal to normalize `for<'a> >::Assoc -> ?y`, this would result in errors in cases where `>::Assoc` normalized to `&'a u32`. The inference variable `?y` would be in a lower [universe][universes] than the placeholders made when instantiating the `for<'a>` binder. +If we were to normalize it to `for<'a> fn(?y)` and register a goal to normalize `for<'a> >::Assoc -> ?y`, this would result in errors in cases where `>::Assoc` normalized to `&'a u32`. The inference variable `?y` would be in a lower [universe] than the placeholders made when instantiating the `for<'a>` binder. Leaving the alias unnormalized would also be wrong as the old solver expects all aliases to be rigid. This was a soundness bug before the new solver was stabilized in coherence: [relating projection substs is unsound during coherence](https://github.com/rust-lang/rust/issues/102048). Ultimately this means that it is not always possible to ensure all aliases inside of a value are rigid. -[universes]: borrow_check/region_inference/placeholders_and_universes.md#what-is-a-universe +[universe]: borrow_check/region_inference/placeholders_and_universes.md#what-is-a-universe [deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize ## Handling uses of diverging aliases From 3b85b2f550ef5511aa86b07f28c759ea5a838693 Mon Sep 17 00:00:00 2001 From: Stan Manilov Date: Mon, 2 Jun 2025 17:02:34 +0300 Subject: [PATCH 30/59] Trivial: dedup word --- src/doc/rustc-dev-guide/src/coroutine-closures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index 04742d03c50d..4a708fc8892c 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -102,7 +102,7 @@ See the "follow-up: when do..." section below for an elaborated answer. The full When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped. -To work around around this limitation, we synthesize a separate by-move MIR body for calling `AsyncFnOnce::call_once` on a coroutine-closure that can be called by-ref. +To work around this limitation, we synthesize a separate by-move MIR body for calling `AsyncFnOnce::call_once` on a coroutine-closure that can be called by-ref. This body operates identically to the "normal" coroutine returned from calling the coroutine-closure, except for the fact that it has a different set of upvars, since we must *move* the captures from the parent coroutine-closure into the child coroutine. From 63ecc0ed08ec4e00a720f2ad7db75e8f6ceaac86 Mon Sep 17 00:00:00 2001 From: Stan Manilov Date: Mon, 2 Jun 2025 17:16:29 +0300 Subject: [PATCH 31/59] Add title and toc to Async chapter This is standard for other chapters. --- .../rustc-dev-guide/src/coroutine-closures.md | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index 04742d03c50d..314bd13b0a24 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -1,6 +1,10 @@ +# Async closures/"coroutine-closures" + + + Please read [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) to understand the general motivation of the feature. This is a very technical and somewhat "vertical" chapter; ideally we'd split this and sprinkle it across all the relevant chapters, but for the purposes of understanding async closures *holistically*, I've put this together all here in one chapter. -# Coroutine-closures -- a technical deep dive +## Coroutine-closures -- a technical deep dive Coroutine-closures are a generalization of async closures, being special syntax for closure expressions which return a coroutine, notably one that is allowed to capture from the closure's upvars. @@ -8,7 +12,7 @@ For now, the only usable kind of coroutine-closure is the async closure, and sup As a consequence of the code being somewhat general, this document may flip between calling them "async closures" and "coroutine-closures". The future that is returned by the async closure will generally be called the "coroutine" or the "child coroutine". -## HIR +### HIR Async closures (and in the future, other coroutine flavors such as `gen`) are represented in HIR as a `hir::Closure` whose closure-kind is `ClosureKind::CoroutineClosure(_)`[^k1], which wraps an async block, which is also represented in HIR as a `hir::Closure`) and whose closure-kind is `ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))`[^k2]. @@ -24,7 +28,7 @@ Like `async fn`, when lowering an async closure's body, we need to unconditional [^l3]: -## `rustc_middle::ty` Representation +### `rustc_middle::ty` Representation For the purposes of keeping the implementation mostly future-compatible (i.e. with gen `|| {}` and `async gen || {}`), most of this section calls async closures "coroutine-closures". @@ -72,7 +76,7 @@ To most easily construct the `Coroutine` that a coroutine-closure returns, you c Most of the args to that function will be components that you can get out of the `CoroutineArgs`, except for the `goal_kind: ClosureKind` which controls which flavor of coroutine to return based off of the `ClosureKind` passed in -- i.e. it will prepare the by-ref coroutine if `ClosureKind::Fn | ClosureKind::FnMut`, and the by-move coroutine if `ClosureKind::FnOnce`. -## Trait Hierarchy +### Trait Hierarchy We introduce a parallel hierarchy of `Fn*` traits that are implemented for . The motivation for the introduction was covered in a blog post: [Async Closures](https://hackmd.io/@compiler-errors/async-closures). @@ -98,7 +102,7 @@ We mention above that "regular" callable types can implement `AsyncFn*`, but the See the "follow-up: when do..." section below for an elaborated answer. The full answer describes a pretty interesting and hopefully thorough heuristic that is used to ensure that most async closures "just work". -## Tale of two bodies... +### Tale of two bodies... When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped. @@ -120,7 +124,7 @@ Since we've synthesized a new def id, this query is also responsible for feeding [^b3]: -## Closure signature inference +### Closure signature inference The closure signature inference algorithm for async closures is a bit more complicated than the inference algorithm for "traditional" closures. Like closures, we iterate through all of the clauses that may be relevant (for the expectation type passed in)[^deduce1]. @@ -173,7 +177,7 @@ s.as_bytes(); So *instead*, we use this alias (in this case, a projection: `AsyncFnKindHelper::Upvars<'env, ...>`) to delay the computation of the *tupled upvars* and give us something to put in its place, while still allowing us to return a `TyKind::Coroutine` (which is a rigid type) and we may successfully confirm the built-in traits we need (in our case, `Future`), since the `Future` implementation doesn't depend on the upvars at all. -## Upvar analysis +### Upvar analysis By and large, the upvar analysis for coroutine-closures and their child coroutines proceeds like normal upvar analysis. However, there are several interesting bits that happen to account for async closures' special natures: @@ -262,7 +266,7 @@ let c = async || { If either of these cases apply, then we should capture the borrow with the lifetime of the parent coroutine-closure's env. Luckily, if this function is not correct, then the program is not unsound, since we still borrowck and validate the choices made from this function -- the only side-effect is that the user may receive unnecessary borrowck errors. -## Instance resolution +### Instance resolution If a coroutine-closure has a closure-kind of `FnOnce`, then its `AsyncFnOnce::call_once` and `FnOnce::call_once` implementations resolve to the coroutine-closure's body[^res1], and the `Future::poll` of the coroutine that gets returned resolves to the body of the child closure. @@ -282,7 +286,7 @@ This is represented by the `ConstructCoroutineInClosureShim`[^i1]. The `receiver [^i3]: -## Borrow-checking +### Borrow-checking It turns out that borrow-checking async closures is pretty straightforward. After adding a new `DefiningTy::CoroutineClosure`[^bck1] variant, and teaching borrowck how to generate the signature of the coroutine-closure[^bck2], borrowck proceeds totally fine. From 0718d8f29fbdba50fa559eb6ffa05e9bdb93b3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 2 Jun 2025 16:21:40 +0200 Subject: [PATCH 32/59] Fix some warning blocks that contain Markdown Contents inside of an HTML element only get interpreted as Markdown (as opposed to HTML) if its separated from the HTML tags with line breaks. --- src/doc/rustc-dev-guide/src/autodiff/flags.md | 2 ++ src/doc/rustc-dev-guide/src/tests/ci.md | 4 +++- src/doc/rustc-dev-guide/src/tests/compiletest.md | 2 ++ src/doc/rustc-dev-guide/src/tests/directives.md | 2 ++ src/doc/rustc-dev-guide/src/tests/minicore.md | 2 ++ src/doc/rustc-dev-guide/src/tests/running.md | 4 ++++ 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/flags.md b/src/doc/rustc-dev-guide/src/autodiff/flags.md index 946ae1d03ae6..65287d9ba4c1 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/flags.md +++ b/src/doc/rustc-dev-guide/src/autodiff/flags.md @@ -16,7 +16,9 @@ LooseTypes // Risk incorrect derivatives instead of aborting when missing Type I ```
+ `LooseTypes` is often helpful to get rid of Enzyme errors stating `Can not deduce type of ` and to be able to run some code. But please keep in mind that this flag absolutely has the chance to cause incorrect gradients. Even worse, the gradients might be correct for certain input values, but not for others. So please create issues about such bugs and only use this flag temporarily while you wait for your bug to be fixed. +
### Benchmark flags diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index d8be8224dd0a..edfc870e8df8 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -186,9 +186,11 @@ Note that if you start the default try job using `@bors try`, it will skip build Multiple try builds can execute concurrently across different PRs.
-bors identify try jobs by commit hash. This means that if you have two PRs + +Bors identifies try jobs by commit hash. This means that if you have two PRs containing the same (latest) commits, running `@bors try` will result in the *same* try job and it really confuses `bors`. Please refrain from doing so. +
[rustc-perf]: https://github.com/rust-lang/rustc-perf diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index e1b23748de33..ee06ca3b6985 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -438,7 +438,9 @@ To work around this when working on a particular test, temporarily create a with these contents:
+ Be careful not to add this `Cargo.toml` or its `Cargo.lock` to your actual PR! +
```toml diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 8a862417b0da..2dff21ed61c2 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -248,11 +248,13 @@ ignoring debuggers. | `no-prefer-dynamic` | Don't use `-C prefer-dynamic`, don't build as a dylib via a `--crate-type=dylib` preset flag | `ui`, `crashes` | N/A |
+ Tests (outside of `run-make`) that want to use incremental tests not in the incremental test-suite must not pass `-C incremental` via `compile-flags`, and must instead use the `//@ incremental` directive. Consider writing the test as a proper incremental test instead. +
### Rustdoc diff --git a/src/doc/rustc-dev-guide/src/tests/minicore.md b/src/doc/rustc-dev-guide/src/tests/minicore.md index 507b259e0275..def9aaf87334 100644 --- a/src/doc/rustc-dev-guide/src/tests/minicore.md +++ b/src/doc/rustc-dev-guide/src/tests/minicore.md @@ -7,9 +7,11 @@ ui/codegen/assembly test suites. It provides `core` stubs for tests that need to build for cross-compiled targets but do not need/want to run.
+ Please note that [`minicore`] is only intended for `core` items, and explicitly **not** `std` or `alloc` items because `core` items are applicable to a wider range of tests. +
A test can use [`minicore`] by specifying the `//@ add-core-stubs` directive. diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index 73c38736812a..6526fe9c2357 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -8,6 +8,7 @@ development because it takes a really long time. For local development, see the subsection after on how to run a subset of tests.
+ Running plain `./x test` will build the stage 1 compiler and then run the whole test suite. This not only include `tests/`, but also `library/`, `compiler/`, `src/tools/` package tests and more. @@ -16,6 +17,7 @@ You usually only want to run a subset of the test suites (or even a smaller set of tests than that) which you expect will exercise your changes. PR CI exercises a subset of test collections, and merge queue CI will exercise all of the test collection. +
```text @@ -116,8 +118,10 @@ By listing which test suites you want to run, you avoid having to run tests for components you did not change at all.
+ Note that bors only runs the tests with the full stage 2 build; therefore, while the tests **usually** work fine with stage 1, there are some limitations. +
### Run all tests using a stage 2 compiler From e74c2b522594522aadb4ac0380d071dad1f10ff7 Mon Sep 17 00:00:00 2001 From: Stan Manilov Date: Mon, 2 Jun 2025 16:49:06 +0300 Subject: [PATCH 33/59] Simplify long sentence --- src/doc/rustc-dev-guide/src/coroutine-closures.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index 04742d03c50d..5a6f28e0f9c8 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -10,7 +10,9 @@ As a consequence of the code being somewhat general, this document may flip betw ## HIR -Async closures (and in the future, other coroutine flavors such as `gen`) are represented in HIR as a `hir::Closure` whose closure-kind is `ClosureKind::CoroutineClosure(_)`[^k1], which wraps an async block, which is also represented in HIR as a `hir::Closure`) and whose closure-kind is `ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))`[^k2]. +Async closures (and in the future, other coroutine flavors such as `gen`) are represented in HIR as a `hir::Closure`. +The closure-kind of the `hir::Closure` is `ClosureKind::CoroutineClosure(_)`[^k1], which wraps an async block, which is also represented in HIR as a `hir::Closure`. +The closure-kind of the async block is `ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))`[^k2]. [^k1]: From e83ca863412a950f19fbc4e8fc632f19107dd0a2 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 2 Jun 2025 16:10:49 +0000 Subject: [PATCH 34/59] cleanup: Use `x.biteq(y)` rather than `x.to_bits() == y.to_bits()` --- library/compiler-builtins/libm-test/src/precision.rs | 2 +- .../compiler-builtins/libm-test/src/test_traits.rs | 5 +---- .../libm/src/math/generic/fmaximum.rs | 2 +- .../libm/src/math/generic/fmaximum_num.rs | 11 +++++------ .../libm/src/math/generic/fminimum.rs | 2 +- .../libm/src/math/generic/fminimum_num.rs | 11 +++++------ 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/library/compiler-builtins/libm-test/src/precision.rs b/library/compiler-builtins/libm-test/src/precision.rs index f5fb5f6707b0..f6cdd015a593 100644 --- a/library/compiler-builtins/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm-test/src/precision.rs @@ -381,7 +381,7 @@ fn unop_common( } // abs and copysign require signaling NaNs to be propagated, so verify bit equality. - if actual.to_bits() == expected.to_bits() { + if actual.biteq(expected) { return CheckAction::Custom(Ok(())); } else { return CheckAction::Custom(Err(anyhow::anyhow!("NaNs have different bitpatterns"))); diff --git a/library/compiler-builtins/libm-test/src/test_traits.rs b/library/compiler-builtins/libm-test/src/test_traits.rs index dbb97016153c..2af6af60ba27 100644 --- a/library/compiler-builtins/libm-test/src/test_traits.rs +++ b/library/compiler-builtins/libm-test/src/test_traits.rs @@ -328,10 +328,7 @@ where // Check when both are NaNs if actual.is_nan() && expected.is_nan() { if require_biteq && ctx.basis == CheckBasis::None { - ensure!( - actual.to_bits() == expected.to_bits(), - "mismatched NaN bitpatterns" - ); + ensure!(actual.biteq(expected), "mismatched NaN bitpatterns"); } // By default, NaNs have nothing special to check. return Ok(()); diff --git a/library/compiler-builtins/libm/src/math/generic/fmaximum.rs b/library/compiler-builtins/libm/src/math/generic/fmaximum.rs index 4b6295bc0c6b..898828b80c7f 100644 --- a/library/compiler-builtins/libm/src/math/generic/fmaximum.rs +++ b/library/compiler-builtins/libm/src/math/generic/fmaximum.rs @@ -17,7 +17,7 @@ pub fn fmaximum(x: F, y: F) -> F { x } else if y.is_nan() { y - } else if x > y || (y.to_bits() == F::NEG_ZERO.to_bits() && x.is_sign_positive()) { + } else if x > y || (y.biteq(F::NEG_ZERO) && x.is_sign_positive()) { x } else { y diff --git a/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs b/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs index 2e97ff6d3690..05df6cbd4643 100644 --- a/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs +++ b/library/compiler-builtins/libm/src/math/generic/fmaximum_num.rs @@ -15,12 +15,11 @@ use crate::support::Float; #[inline] pub fn fmaximum_num(x: F, y: F) -> F { - let res = - if x.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { - y - } else { - x - }; + let res = if x.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) { + y + } else { + x + }; // Canonicalize res * F::ONE diff --git a/library/compiler-builtins/libm/src/math/generic/fminimum.rs b/library/compiler-builtins/libm/src/math/generic/fminimum.rs index 9dc0b64be3f2..8592ac5460ef 100644 --- a/library/compiler-builtins/libm/src/math/generic/fminimum.rs +++ b/library/compiler-builtins/libm/src/math/generic/fminimum.rs @@ -17,7 +17,7 @@ pub fn fminimum(x: F, y: F) -> F { x } else if y.is_nan() { y - } else if x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { + } else if x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) { x } else { y diff --git a/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs b/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs index 40db8b18957b..6777bbf87721 100644 --- a/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs +++ b/library/compiler-builtins/libm/src/math/generic/fminimum_num.rs @@ -15,12 +15,11 @@ use crate::support::Float; #[inline] pub fn fminimum_num(x: F, y: F) -> F { - let res = - if y.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { - x - } else { - y - }; + let res = if y.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) { + x + } else { + y + }; // Canonicalize res * F::ONE From ba7cdb681468ef7617bfa5a2bde7e3fb3d79a2be Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 1 Jun 2025 19:52:57 +0000 Subject: [PATCH 35/59] ci: Refactor benchmark regression checks iai-callgrind now correctly exits with error if regressions were found [1], so we no longer need to check for regressions manually. Remove this check and instead exit based on the exit status of the benchmark run. [1] https://github.com/iai-callgrind/iai-callgrind/issues/337 --- library/compiler-builtins/ci/bench-icount.sh | 19 ++--- library/compiler-builtins/ci/ci-util.py | 84 +++++--------------- 2 files changed, 29 insertions(+), 74 deletions(-) diff --git a/library/compiler-builtins/ci/bench-icount.sh b/library/compiler-builtins/ci/bench-icount.sh index 5b6974fe4204..5724955fe367 100755 --- a/library/compiler-builtins/ci/bench-icount.sh +++ b/library/compiler-builtins/ci/bench-icount.sh @@ -46,17 +46,18 @@ function run_icount_benchmarks() { shift done - # Run iai-callgrind benchmarks - cargo bench "${cargo_args[@]}" -- "${iai_args[@]}" + # Run iai-callgrind benchmarks. Do this in a subshell with `&& true` to + # capture rather than exit on error. + (cargo bench "${cargo_args[@]}" -- "${iai_args[@]}") && true + exit_code="$?" - # NB: iai-callgrind should exit on error but does not, so we inspect the sumary - # for errors. See https://github.com/iai-callgrind/iai-callgrind/issues/337 - if [ -n "${PR_NUMBER:-}" ]; then - # If this is for a pull request, ignore regressions if specified. - ./ci/ci-util.py check-regressions --home "$iai_home" --allow-pr-override "$PR_NUMBER" - else + if [ "$exit_code" -eq 0 ]; then + echo "Benchmarks completed with no regressions" + elif [ -z "${PR_NUMBER:-}" ]; then # Disregard regressions after merge - ./ci/ci-util.py check-regressions --home "$iai_home" || true + echo "Benchmarks completed with regressions; ignoring (not in a PR)" + else + ./ci/ci-util.py handle-banch-regressions "$PR_NUMBER" fi } diff --git a/library/compiler-builtins/ci/ci-util.py b/library/compiler-builtins/ci/ci-util.py index 6c8b439805b4..3437d304f48c 100755 --- a/library/compiler-builtins/ci/ci-util.py +++ b/library/compiler-builtins/ci/ci-util.py @@ -11,7 +11,7 @@ import re import subprocess as sp import sys from dataclasses import dataclass -from glob import glob, iglob +from glob import glob from inspect import cleandoc from os import getenv from pathlib import Path @@ -38,14 +38,10 @@ USAGE = cleandoc( Note that `--extract` will overwrite files in `iai-home`. - check-regressions [--home iai-home] [--allow-pr-override pr_number] - Check `iai-home` (or `iai-home` if unspecified) for `summary.json` - files and see if there are any regressions. This is used as a workaround - for `iai-callgrind` not exiting with error status; see - . - - If `--allow-pr-override` is specified, the regression check will not exit - with failure if any line in the PR starts with `allow-regressions`. + handle-bench-regressions PR_NUMBER + Exit with success if the pull request contains a line starting with + `ci: allow-regressions`, indicating that regressions in benchmarks should + be accepted. Otherwise, exit 1. """ ) @@ -365,64 +361,22 @@ def locate_baseline(flags: list[str]) -> None: eprint("baseline extracted successfully") -def check_iai_regressions(args: list[str]): - """Find regressions in iai summary.json files, exit with failure if any are - found. - """ +def handle_bench_regressions(args: list[str]): + """Exit with error unless the PR message contains an ignore directive.""" - iai_home_str = "iai-home" - pr_number = None + match args: + case [pr_number]: + pr_number = pr_number + case _: + eprint(USAGE) + exit(1) - while len(args) > 0: - match args: - case ["--home", home, *rest]: - iai_home_str = home - args = rest - case ["--allow-pr-override", pr_num, *rest]: - pr_number = pr_num - args = rest - case _: - eprint(USAGE) - exit(1) - - iai_home = Path(iai_home_str) - - found_summaries = False - regressions: list[dict] = [] - for summary_path in iglob("**/summary.json", root_dir=iai_home, recursive=True): - found_summaries = True - with open(iai_home / summary_path, "r") as f: - summary = json.load(f) - - summary_regs = [] - run = summary["callgrind_summary"]["callgrind_run"] - fname = summary["function_name"] - id = summary["id"] - name_entry = {"name": f"{fname}.{id}"} - - for segment in run["segments"]: - summary_regs.extend(segment["regressions"]) - - summary_regs.extend(run["total"]["regressions"]) - - regressions.extend(name_entry | reg for reg in summary_regs) - - if not found_summaries: - eprint(f"did not find any summary.json files within {iai_home}") - exit(1) - - if len(regressions) == 0: - eprint("No regressions found") + pr = PrInfo.load(pr_number) + if pr.contains_directive(REGRESSION_DIRECTIVE): + eprint("PR allows regressions") return - eprint("Found regressions:", json.dumps(regressions, indent=4)) - - if pr_number is not None: - pr = PrInfo.load(pr_number) - if pr.contains_directive(REGRESSION_DIRECTIVE): - eprint("PR allows regressions, returning") - return - + eprint("Regressions were found; benchmark failed") exit(1) @@ -433,8 +387,8 @@ def main(): ctx.emit_workflow_output() case ["locate-baseline", *flags]: locate_baseline(flags) - case ["check-regressions", *args]: - check_iai_regressions(args) + case ["handle-bench-regressions", *args]: + handle_bench_regressions(args) case ["--help" | "-h"]: print(USAGE) exit() From 57786431746f495f4205e26ee1badd142a380fff Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 2 Jun 2025 20:20:23 +0000 Subject: [PATCH 36/59] libm-test: Fix unintentional skips in `binop_common` `binop_common` emits a `SKIP` that is intended to apply only to `copysign`, but is instead applying to all binary operators. Correct the general case but leave the currently-failing `maximum_num` tests as a FIXME, to be resolved separately in [1]. Also simplify skip logic and NaN checking, and add a few more `copysign` checks. [1]: https://github.com/rust-lang/compiler-builtins/pull/939 --- .../libm-test/src/generate/edge_cases.rs | 1 + .../libm-test/src/precision.rs | 15 +++++++++----- .../libm-test/src/test_traits.rs | 20 +++++++++++++------ .../libm/src/math/copysign.rs | 10 +++++++++- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/library/compiler-builtins/libm-test/src/generate/edge_cases.rs b/library/compiler-builtins/libm-test/src/generate/edge_cases.rs index 2fb0746388c1..4e4a782a1698 100644 --- a/library/compiler-builtins/libm-test/src/generate/edge_cases.rs +++ b/library/compiler-builtins/libm-test/src/generate/edge_cases.rs @@ -51,6 +51,7 @@ where // Check some special values that aren't included in the above ranges values.push(Op::FTy::NAN); + values.push(Op::FTy::NEG_NAN); values.extend(Op::FTy::consts().iter()); // Check around the maximum subnormal value diff --git a/library/compiler-builtins/libm-test/src/precision.rs b/library/compiler-builtins/libm-test/src/precision.rs index f6cdd015a593..32825b15d476 100644 --- a/library/compiler-builtins/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm-test/src/precision.rs @@ -444,13 +444,18 @@ fn binop_common( expected: F2, ctx: &CheckCtx, ) -> CheckAction { - // MPFR only has one NaN bitpattern; allow the default `.is_nan()` checks to validate. Skip if - // the first input (magnitude source) is NaN and the output is also a NaN, or if the second - // input (sign source) is NaN. - if ctx.basis == CheckBasis::Mpfr + // MPFR only has one NaN bitpattern; skip tests in cases where the first argument would take + // the sign of a NaN second argument. The default NaN checks cover other cases. + if ctx.base_name == BaseName::Copysign && ctx.basis == CheckBasis::Mpfr && input.1.is_nan() { + return SKIP; + } + + // FIXME(#939): this should not be skipped, there is a bug in our implementationi. + if ctx.base_name == BaseName::FmaximumNum + && ctx.basis == CheckBasis::Mpfr && ((input.0.is_nan() && actual.is_nan() && expected.is_nan()) || input.1.is_nan()) { - return SKIP; + return XFAIL_NOCHECK; } /* FIXME(#439): our fmin and fmax do not compare signed zeros */ diff --git a/library/compiler-builtins/libm-test/src/test_traits.rs b/library/compiler-builtins/libm-test/src/test_traits.rs index 2af6af60ba27..278274d917b3 100644 --- a/library/compiler-builtins/libm-test/src/test_traits.rs +++ b/library/compiler-builtins/libm-test/src/test_traits.rs @@ -312,12 +312,9 @@ where let mut inner = || -> TestResult { let mut allowed_ulp = ctx.ulp; - // Forbid overrides if the items came from an explicit list, as long as we are checking - // against either MPFR or the result itself. - let require_biteq = ctx.gen_kind == GeneratorKind::List && ctx.basis != CheckBasis::Musl; - match SpecialCase::check_float(input, actual, expected, ctx) { - _ if require_biteq => (), + // Forbid overrides if the items came from an explicit list + _ if ctx.gen_kind == GeneratorKind::List => (), CheckAction::AssertSuccess => (), CheckAction::AssertFailure(msg) => assert_failure_msg = Some(msg), CheckAction::Custom(res) => return res, @@ -327,9 +324,20 @@ where // Check when both are NaNs if actual.is_nan() && expected.is_nan() { - if require_biteq && ctx.basis == CheckBasis::None { + // Don't assert NaN bitwise equality if: + // + // * Testing against MPFR (there is a single NaN representation) + // * Testing against Musl except for explicit tests (Musl does some NaN quieting) + // + // In these cases, just the check that actual and expected are both NaNs is + // sufficient. + let skip_nan_biteq = ctx.basis == CheckBasis::Mpfr + || (ctx.basis == CheckBasis::Musl && ctx.gen_kind != GeneratorKind::List); + + if !skip_nan_biteq { ensure!(actual.biteq(expected), "mismatched NaN bitpatterns"); } + // By default, NaNs have nothing special to check. return Ok(()); } else if actual.is_nan() || expected.is_nan() { diff --git a/library/compiler-builtins/libm/src/math/copysign.rs b/library/compiler-builtins/libm/src/math/copysign.rs index d2a86e7fd545..d093d6107273 100644 --- a/library/compiler-builtins/libm/src/math/copysign.rs +++ b/library/compiler-builtins/libm/src/math/copysign.rs @@ -59,9 +59,17 @@ mod tests { // Not required but we expect it assert_biteq!(f(F::NAN, F::NAN), F::NAN); - assert_biteq!(f(F::NEG_NAN, F::NAN), F::NAN); + assert_biteq!(f(F::NAN, F::ONE), F::NAN); + assert_biteq!(f(F::NAN, F::NEG_ONE), F::NEG_NAN); assert_biteq!(f(F::NAN, F::NEG_NAN), F::NEG_NAN); + assert_biteq!(f(F::NEG_NAN, F::NAN), F::NAN); + assert_biteq!(f(F::NEG_NAN, F::ONE), F::NAN); + assert_biteq!(f(F::NEG_NAN, F::NEG_ONE), F::NEG_NAN); assert_biteq!(f(F::NEG_NAN, F::NEG_NAN), F::NEG_NAN); + assert_biteq!(f(F::ONE, F::NAN), F::ONE); + assert_biteq!(f(F::ONE, F::NEG_NAN), F::NEG_ONE); + assert_biteq!(f(F::NEG_ONE, F::NAN), F::ONE); + assert_biteq!(f(F::NEG_ONE, F::NEG_NAN), F::NEG_ONE); } #[test] From 8f22a507c1e7fd7313c1421db25d80057f07bac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 30 May 2025 22:13:47 +0200 Subject: [PATCH 37/59] Use 4-core large disk runner for the alt job --- src/ci/github-actions/jobs.yml | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 56863b839dfd..8d5f4647301b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -10,6 +10,10 @@ runners: free_disk: true <<: *base-job + - &job-linux-4c-largedisk + os: ubuntu-24.04-4core-16gb + <<: *base-job + - &job-linux-8c os: ubuntu-24.04-8core-32gb <<: *base-job @@ -107,6 +111,15 @@ envs: pr: PR_CI_JOB: 1 +jobs: + dist-x86_64-linux: &job-dist-x86_64-linux + name: dist-x86_64-linux + env: + CODEGEN_BACKENDS: llvm,cranelift + DOCKER_SCRIPT: dist.sh + <<: *job-linux-36c-codebuild + + # Jobs that run on each push to a pull request (PR) # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. @@ -138,11 +151,7 @@ pr: # These jobs automatically inherit envs.try, to avoid repeating # it in each job definition. try: - - name: dist-x86_64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - DOCKER_SCRIPT: dist.sh - <<: *job-linux-36c-codebuild + - <<: *job-dist-x86_64-linux # Main CI jobs that have to be green to merge a commit into master # These jobs automatically inherit envs.auto, to avoid repeating @@ -235,18 +244,14 @@ auto: - name: dist-x86_64-illumos <<: *job-linux-4c - - name: dist-x86_64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - DOCKER_SCRIPT: dist.sh - <<: *job-linux-36c-codebuild + - <<: *job-dist-x86_64-linux - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift DOCKER_SCRIPT: dist-alt.sh - <<: *job-linux-16c + <<: *job-linux-4c-largedisk - name: dist-x86_64-musl env: From aca36fd12a3277ce17eb94e4e89567843bf137f7 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Tue, 29 Apr 2025 17:12:12 +0200 Subject: [PATCH 38/59] Move placeholder handling to a proper preprocessing step This commit breaks out the logic of placheolder rewriting into its own preprocessing step. The only functional change from this is that the preprocessing step (where extra `r: 'static` constraints are added) is performed upstream of Polonius legacy, finally affecting Polonius. That is mostly a by-product, though. --- .../rustc_borrowck/src/constraints/mod.rs | 110 +----- .../src/eliminate_placeholders.rs | 371 ++++++++++++++++++ compiler/rustc_borrowck/src/lib.rs | 1 + compiler/rustc_borrowck/src/nll.rs | 17 +- .../rustc_borrowck/src/polonius/legacy/mod.rs | 6 +- .../rustc_borrowck/src/region_infer/mod.rs | 210 ++-------- 6 files changed, 415 insertions(+), 300 deletions(-) create mode 100644 compiler/rustc_borrowck/src/eliminate_placeholders.rs diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 514bbfe359b1..99ddccabd15f 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -5,11 +5,9 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; use rustc_span::Span; -use tracing::{debug, instrument}; +use tracing::debug; -use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations}; use crate::type_check::Locations; -use crate::universal_regions::UniversalRegions; pub(crate) mod graph; @@ -53,112 +51,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { ) -> &IndexSlice> { &self.outlives } - - /// Computes cycles (SCCs) in the graph of regions. In particular, - /// find all regions R1, R2 such that R1: R2 and R2: R1 and group - /// them into an SCC, and find the relationships between SCCs. - pub(crate) fn compute_sccs( - &self, - static_region: RegionVid, - definitions: &IndexVec>, - ) -> AnnotatedSccs { - let constraint_graph = self.graph(definitions.len()); - let region_graph = &constraint_graph.region_graph(self, static_region); - let mut annotation_visitor = SccAnnotations::new(definitions); - ( - ConstraintSccs::new_with_annotation(®ion_graph, &mut annotation_visitor), - annotation_visitor.scc_to_annotation, - ) - } - - /// This method handles Universe errors by rewriting the constraint - /// graph. For each strongly connected component in the constraint - /// graph such that there is a series of constraints - /// A: B: C: ... : X where - /// A's universe is smaller than X's and A is a placeholder, - /// add a constraint that A: 'static. This is a safe upper bound - /// in the face of borrow checker/trait solver limitations that will - /// eventually go away. - /// - /// For a more precise definition, see the documentation for - /// [`crate::region_infer::RegionTracker`]. - /// - /// This edge case used to be handled during constraint propagation - /// by iterating over the strongly connected components in the constraint - /// graph while maintaining a set of bookkeeping mappings similar - /// to what is stored in `RegionTracker` and manually adding 'static as - /// needed. - /// - /// It was rewritten as part of the Polonius project with the goal of moving - /// higher-kindedness concerns out of the path of the borrow checker, - /// for two reasons: - /// - /// 1. Implementing Polonius is difficult enough without also - /// handling them. - /// 2. The long-term goal is to handle higher-kinded concerns - /// in the trait solver, where they belong. This avoids - /// logic duplication and allows future trait solvers - /// to compute better bounds than for example our - /// "must outlive 'static" here. - /// - /// This code is a stop-gap measure in preparation for the future trait solver. - /// - /// Every constraint added by this method is an - /// internal `IllegalUniverse` constraint. - #[instrument(skip(self, universal_regions, definitions))] - pub(crate) fn add_outlives_static( - &mut self, - universal_regions: &UniversalRegions<'tcx>, - definitions: &IndexVec>, - ) -> AnnotatedSccs { - let fr_static = universal_regions.fr_static; - let (sccs, annotations) = self.compute_sccs(fr_static, definitions); - - // Changed to `true` if we added any constraints to `self` and need to - // recompute SCCs. - let mut added_constraints = false; - - for scc in sccs.all_sccs() { - // No point in adding 'static: 'static! - // This micro-optimisation makes somewhat sense - // because static outlives *everything*. - if scc == sccs.scc(fr_static) { - continue; - } - - let annotation = annotations[scc]; - - // If this SCC participates in a universe violation, - // e.g. if it reaches a region with a universe smaller than - // the largest region reached, add a requirement that it must - // outlive `'static`. - if annotation.has_incompatible_universes() { - // Optimisation opportunity: this will add more constraints than - // needed for correctness, since an SCC upstream of another with - // a universe violation will "infect" its downstream SCCs to also - // outlive static. - added_constraints = true; - let scc_representative_outlives_static = OutlivesConstraint { - sup: annotation.representative, - sub: fr_static, - category: ConstraintCategory::IllegalUniverse, - locations: Locations::All(rustc_span::DUMMY_SP), - span: rustc_span::DUMMY_SP, - variance_info: VarianceDiagInfo::None, - from_closure: false, - }; - self.push(scc_representative_outlives_static); - } - } - - if added_constraints { - // We changed the constraint set and so must recompute SCCs. - self.compute_sccs(fr_static, definitions) - } else { - // If we didn't add any back-edges; no more work needs doing - (sccs, annotations) - } - } } impl<'tcx> Index for OutlivesConstraintSet<'tcx> { diff --git a/compiler/rustc_borrowck/src/eliminate_placeholders.rs b/compiler/rustc_borrowck/src/eliminate_placeholders.rs new file mode 100644 index 000000000000..e8e1acbd7a5a --- /dev/null +++ b/compiler/rustc_borrowck/src/eliminate_placeholders.rs @@ -0,0 +1,371 @@ +//! Logic for lowering higher-kinded outlives constraints +//! (with placeholders and universes) and turn them into regular +//! outlives constraints. +//! +//! This logic is provisional and should be removed once the trait +//! solver can handle this kind of constraint. +use rustc_data_structures::frozen::Frozen; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::graph::scc; +use rustc_data_structures::graph::scc::Sccs; +use rustc_index::IndexVec; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::{RegionVid, UniverseIndex}; +use tracing::debug; + +use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet}; +use crate::consumers::OutlivesConstraint; +use crate::diagnostics::UniverseInfo; +use crate::member_constraints::MemberConstraintSet; +use crate::region_infer::values::{LivenessValues, PlaceholderIndices}; +use crate::region_infer::{ConstraintSccs, RegionDefinition, TypeTest}; +use crate::ty::VarianceDiagInfo; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::type_check::{Locations, MirTypeckRegionConstraints}; +use crate::universal_regions::UniversalRegions; +use crate::{BorrowckInferCtxt, NllRegionVariableOrigin}; + +/// A set of outlives constraints after rewriting to remove +/// higher-kinded constraints. +pub(crate) struct LoweredConstraints<'tcx> { + pub(crate) constraint_sccs: Sccs, + pub(crate) definitions: Frozen>>, + pub(crate) scc_annotations: IndexVec, + pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, + pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, + pub(crate) type_tests: Vec>, + pub(crate) liveness_constraints: LivenessValues, + pub(crate) universe_causes: FxIndexMap>, + pub(crate) placeholder_indices: PlaceholderIndices, +} + +impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { + pub(crate) fn init(definitions: &'d IndexVec>) -> Self { + Self { scc_to_annotation: IndexVec::new(), definitions } + } +} + +/// A Visitor for SCC annotation construction. +pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { + pub(crate) scc_to_annotation: IndexVec, + definitions: &'d IndexVec>, +} + +impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { + fn new(&self, element: RegionVid) -> RegionTracker { + RegionTracker::new(element, &self.definitions[element]) + } + + fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { + let idx = self.scc_to_annotation.push(annotation); + assert!(idx == scc); + } + + type Ann = RegionTracker; + type SccIdx = ConstraintSccIndex; +} + +/// An annotation for region graph SCCs that tracks +/// the values of its elements. This annotates a single SCC. +#[derive(Copy, Debug, Clone)] +pub(crate) struct RegionTracker { + /// The largest universe of a placeholder reached from this SCC. + /// This includes placeholders within this SCC. + max_placeholder_universe_reached: UniverseIndex, + + /// The smallest universe index reachable form the nodes of this SCC. + min_reachable_universe: UniverseIndex, + + /// The representative Region Variable Id for this SCC. We prefer + /// placeholders over existentially quantified variables, otherwise + /// it's the one with the smallest Region Variable ID. + pub(crate) representative: RegionVid, + + /// Is the current representative a placeholder? + representative_is_placeholder: bool, + + /// Is the current representative existentially quantified? + representative_is_existential: bool, +} + +impl RegionTracker { + pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { + let (representative_is_placeholder, representative_is_existential) = match definition.origin + { + NllRegionVariableOrigin::FreeRegion => (false, false), + NllRegionVariableOrigin::Placeholder(_) => (true, false), + NllRegionVariableOrigin::Existential { .. } => (false, true), + }; + + let placeholder_universe = + if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT }; + + Self { + max_placeholder_universe_reached: placeholder_universe, + min_reachable_universe: definition.universe, + representative: rvid, + representative_is_placeholder, + representative_is_existential, + } + } + + /// The smallest-indexed universe reachable from and/or in this SCC. + pub(crate) fn min_universe(self) -> UniverseIndex { + self.min_reachable_universe + } + + fn merge_min_max_seen(&mut self, other: &Self) { + self.max_placeholder_universe_reached = std::cmp::max( + self.max_placeholder_universe_reached, + other.max_placeholder_universe_reached, + ); + + self.min_reachable_universe = + std::cmp::min(self.min_reachable_universe, other.min_reachable_universe); + } + + /// Returns `true` if during the annotated SCC reaches a placeholder + /// with a universe larger than the smallest reachable one, `false` otherwise. + pub(crate) fn has_incompatible_universes(&self) -> bool { + self.min_universe().cannot_name(self.max_placeholder_universe_reached) + } + + /// Determine if the tracked universes of the two SCCs + /// are compatible. + pub(crate) fn universe_compatible_with(&self, other: Self) -> bool { + self.min_universe().can_name(other.min_universe()) + || self.min_universe().can_name(other.max_placeholder_universe_reached) + } +} + +impl scc::Annotation for RegionTracker { + fn merge_scc(mut self, mut other: Self) -> Self { + // Prefer any placeholder over any existential + if other.representative_is_placeholder && self.representative_is_existential { + other.merge_min_max_seen(&self); + return other; + } + + if self.representative_is_placeholder && other.representative_is_existential + || (self.representative <= other.representative) + { + self.merge_min_max_seen(&other); + return self; + } + other.merge_min_max_seen(&self); + other + } + + fn merge_reached(mut self, other: Self) -> Self { + // No update to in-component values, only add seen values. + self.merge_min_max_seen(&other); + self + } +} + +/// Determines if the region variable definitions contain +/// placeholers, and compute them for later use. +fn region_definitions<'tcx>( + universal_regions: &UniversalRegions<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, +) -> (Frozen>>, bool) { + let var_infos = infcx.get_region_var_infos(); + // Create a RegionDefinition for each inference variable. This happens here because + // it allows us to sneak in a cheap check for placeholders. Otherwise, its proper home + // is in `RegionInferenceContext::new()`, probably. + let mut definitions = IndexVec::with_capacity(var_infos.len()); + let mut has_placeholders = false; + + for info in var_infos.iter() { + let definition = RegionDefinition::new(info); + has_placeholders |= matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)); + definitions.push(definition); + } + + // Add external names from universal regions in fun function definitions. + for (external_name, variable) in universal_regions.named_universal_regions_iter() { + debug!("region {:?} has external name {:?}", variable, external_name); + definitions[variable].external_name = Some(external_name); + } + (Frozen::freeze(definitions), has_placeholders) +} + +/// This method handles Universe errors by rewriting the constraint +/// graph. For each strongly connected component in the constraint +/// graph such that there is a series of constraints +/// A: B: C: ... : X where +/// A's universe is smaller than X's and A is a placeholder, +/// add a constraint that A: 'static. This is a safe upper bound +/// in the face of borrow checker/trait solver limitations that will +/// eventually go away. +/// +/// For a more precise definition, see the documentation for +/// [`RegionTracker`] and its methods!. +/// +/// Since universes can also be involved in errors (if one placeholder +/// transitively outlives another), this function also flags those. +/// +/// Additionally, it similarly rewrites type-tests. +/// +/// This edge case used to be handled during constraint propagation +/// by iterating over the strongly connected components in the constraint +/// graph while maintaining a set of bookkeeping mappings similar +/// to what is stored in `RegionTracker` and manually adding 'sttaic as +/// needed. +/// +/// It was rewritten as part of the Polonius project with the goal of moving +/// higher-kindedness concerns out of the path of the borrow checker, +/// for two reasons: +/// +/// 1. Implementing Polonius is difficult enough without also +/// handling them. +/// 2. The long-term goal is to handle higher-kinded concerns +/// in the trait solver, where they belong. This avoids +/// logic duplication and allows future trait solvers +/// to compute better bounds than for example our +/// "must outlive 'static" here. +/// +/// This code is a stop-gap measure in preparation for the future trait solver. +/// +/// Every constraint added by this method is an internal `IllegalUniverse` constraint. +pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( + constraints: MirTypeckRegionConstraints<'tcx>, + universal_region_relations: &Frozen>, + infcx: &BorrowckInferCtxt<'tcx>, +) -> LoweredConstraints<'tcx> { + let universal_regions = &universal_region_relations.universal_regions; + let (definitions, has_placeholders) = region_definitions(universal_regions, infcx); + + let MirTypeckRegionConstraints { + placeholder_indices, + placeholder_index_to_region: _, + liveness_constraints, + mut outlives_constraints, + mut member_constraints, + universe_causes, + type_tests, + } = constraints; + + if let Some(guar) = universal_regions.tainted_by_errors() { + debug!("Universal regions tainted by errors; removing constraints!"); + // Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all + // outlives bounds that we may end up checking. + outlives_constraints = Default::default(); + member_constraints = Default::default(); + + // Also taint the entire scope. + infcx.set_tainted_by_errors(guar); + } + + let fr_static = universal_regions.fr_static; + let compute_sccs = + |constraints: &OutlivesConstraintSet<'tcx>, + annotations: &mut SccAnnotations<'_, 'tcx, RegionTracker>| { + ConstraintSccs::new_with_annotation( + &constraints.graph(definitions.len()).region_graph(constraints, fr_static), + annotations, + ) + }; + + // This code structure is a bit convoluted because it allows for a planned + // future change where the early return here has a different type of annotation + // that does much less work. + if !has_placeholders { + debug!("No placeholder regions found; skipping rewriting logic!"); + let mut scc_annotations = SccAnnotations::init(&definitions); + let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); + + return LoweredConstraints { + type_tests, + member_constraints, + constraint_sccs, + scc_annotations: scc_annotations.scc_to_annotation, + definitions, + outlives_constraints, + liveness_constraints, + universe_causes, + placeholder_indices, + }; + } + debug!("Placeholders present; activating placeholder handling logic!"); + + let mut annotations = SccAnnotations::init(&definitions); + let sccs = compute_sccs(&outlives_constraints, &mut annotations); + + let outlives_static = + rewrite_outlives(&sccs, &annotations, fr_static, &mut outlives_constraints); + + let (sccs, scc_annotations) = if !outlives_static.is_empty() { + debug!("The following SCCs had :'static constraints added: {:?}", outlives_static); + let mut annotations = SccAnnotations::init(&definitions); + + // We changed the constraint set and so must recompute SCCs. + // Optimisation opportunity: if we can add them incrementally (and that's + // possible because edges to 'static always only merge SCCs into 'static), + // we would potentially save a lot of work here. + (compute_sccs(&outlives_constraints, &mut annotations), annotations.scc_to_annotation) + } else { + // If we didn't add any back-edges; no more work needs doing + debug!("No constraints rewritten!"); + (sccs, annotations.scc_to_annotation) + }; + + LoweredConstraints { + constraint_sccs: sccs, + definitions, + scc_annotations, + member_constraints, + outlives_constraints, + type_tests, + liveness_constraints, + universe_causes, + placeholder_indices, + } +} + +fn rewrite_outlives<'tcx>( + sccs: &Sccs, + annotations: &SccAnnotations<'_, '_, RegionTracker>, + fr_static: RegionVid, + outlives_constraints: &mut OutlivesConstraintSet<'tcx>, +) -> FxHashSet { + // Changed to `true` if we added any constraints to `self` and need to + // recompute SCCs. + let mut outlives_static = FxHashSet::default(); + + let annotations = &annotations.scc_to_annotation; + + for scc in sccs.all_sccs() { + // No point in adding 'static: 'static! + // This micro-optimisation makes somewhat sense + // because static outlives *everything*. + if scc == sccs.scc(fr_static) { + continue; + } + + let annotation = annotations[scc]; + + // If this SCC participates in a universe violation, + // e.g. if it reaches a region with a universe smaller than + // the largest region reached, add a requirement that it must + // outlive `'static`. + if annotation.has_incompatible_universes() { + // Optimisation opportunity: this will add more constraints than + // needed for correctness, since an SCC upstream of another with + // a universe violation will "infect" its downstream SCCs to also + // outlive static. + outlives_static.insert(scc); + let scc_representative_outlives_static = OutlivesConstraint { + sup: annotation.representative, + sub: fr_static, + category: ConstraintCategory::IllegalUniverse, + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }; + outlives_constraints.push(scc_representative_outlives_static); + } + } + outlives_static +} diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 44af1b765399..edf5494e8867 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -75,6 +75,7 @@ mod constraints; mod dataflow; mod def_use; mod diagnostics; +mod eliminate_placeholders; mod member_constraints; mod nll; mod path_utils; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index fe899bb054fa..9b1a5df8d06b 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -22,6 +22,7 @@ use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; use crate::diagnostics::RegionErrors; +use crate::eliminate_placeholders::rewrite_higher_kinded_outlives_as_constraints; use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, @@ -117,6 +118,12 @@ pub(crate) fn compute_regions<'a, 'tcx>( Rc::clone(&location_map), ); + let lowered_constraints = rewrite_higher_kinded_outlives_as_constraints( + constraints, + &universal_region_relations, + infcx, + ); + // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( &mut polonius_facts, @@ -126,11 +133,15 @@ pub(crate) fn compute_regions<'a, 'tcx>( borrow_set, move_data, &universal_region_relations, - &constraints, + &lowered_constraints, ); - let mut regioncx = - RegionInferenceContext::new(infcx, constraints, universal_region_relations, location_map); + let mut regioncx = RegionInferenceContext::new( + infcx, + lowered_constraints, + universal_region_relations, + location_map, + ); // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 95820c07a02f..67da43ca5788 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -13,7 +13,7 @@ use tracing::debug; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; -use crate::type_check::MirTypeckRegionConstraints; +use crate::eliminate_placeholders::LoweredConstraints; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -43,7 +43,7 @@ pub(crate) fn emit_facts<'tcx>( borrow_set: &BorrowSet<'tcx>, move_data: &MoveData<'tcx>, universal_region_relations: &UniversalRegionRelations<'tcx>, - constraints: &MirTypeckRegionConstraints<'tcx>, + constraints: &LoweredConstraints<'tcx>, ) { let Some(facts) = facts else { // We don't do anything if there are no facts to fill. @@ -203,7 +203,7 @@ pub(crate) fn emit_drop_facts<'tcx>( fn emit_outlives_facts<'tcx>( facts: &mut PoloniusFacts, location_table: &PoloniusLocationTable, - constraints: &MirTypeckRegionConstraints<'tcx>, + constraints: &LoweredConstraints<'tcx>, ) { facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map( |constraint: &OutlivesConstraint<'_>| { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b4ff3d66f3d5..cfad071460b4 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -4,12 +4,14 @@ use std::rc::Rc; use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::graph::scc::{self, Sccs}; +use rustc_data_structures::graph::scc::Sccs; use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::IndexVec; use rustc_infer::infer::outlives::test_type_match; -use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq}; +use rustc_infer::infer::region_constraints::{ + GenericKind, RegionVariableInfo, VerifyBound, VerifyIfEq, +}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::bug; use rustc_middle::mir::{ @@ -27,13 +29,14 @@ use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; +use crate::eliminate_placeholders::{LoweredConstraints, RegionTracker}; use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::reverse_sccs::ReverseSccGraph; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; +use crate::type_check::Locations; use crate::type_check::free_region_relations::UniversalRegionRelations; -use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; use crate::{ BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject, @@ -48,124 +51,6 @@ mod reverse_sccs; pub(crate) mod values; pub(crate) type ConstraintSccs = Sccs; -pub(crate) type AnnotatedSccs = (ConstraintSccs, IndexVec); - -/// An annotation for region graph SCCs that tracks -/// the values of its elements. This annotates a single SCC. -#[derive(Copy, Debug, Clone)] -pub(crate) struct RegionTracker { - /// The largest universe of a placeholder reached from this SCC. - /// This includes placeholders within this SCC. - max_placeholder_universe_reached: UniverseIndex, - - /// The smallest universe index reachable form the nodes of this SCC. - min_reachable_universe: UniverseIndex, - - /// The representative Region Variable Id for this SCC. We prefer - /// placeholders over existentially quantified variables, otherwise - /// it's the one with the smallest Region Variable ID. - pub(crate) representative: RegionVid, - - /// Is the current representative a placeholder? - representative_is_placeholder: bool, - - /// Is the current representative existentially quantified? - representative_is_existential: bool, -} - -impl scc::Annotation for RegionTracker { - fn merge_scc(mut self, mut other: Self) -> Self { - // Prefer any placeholder over any existential - if other.representative_is_placeholder && self.representative_is_existential { - other.merge_min_max_seen(&self); - return other; - } - - if self.representative_is_placeholder && other.representative_is_existential - || (self.representative <= other.representative) - { - self.merge_min_max_seen(&other); - return self; - } - other.merge_min_max_seen(&self); - other - } - - fn merge_reached(mut self, other: Self) -> Self { - // No update to in-component values, only add seen values. - self.merge_min_max_seen(&other); - self - } -} - -/// A Visitor for SCC annotation construction. -pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { - pub(crate) scc_to_annotation: IndexVec, - definitions: &'d IndexVec>, -} - -impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { - pub(crate) fn new(definitions: &'d IndexVec>) -> Self { - Self { scc_to_annotation: IndexVec::new(), definitions } - } -} - -impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { - fn new(&self, element: RegionVid) -> RegionTracker { - RegionTracker::new(element, &self.definitions[element]) - } - - fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { - let idx = self.scc_to_annotation.push(annotation); - assert!(idx == scc); - } - - type Ann = RegionTracker; - type SccIdx = ConstraintSccIndex; -} - -impl RegionTracker { - pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { - let (representative_is_placeholder, representative_is_existential) = match definition.origin - { - NllRegionVariableOrigin::FreeRegion => (false, false), - NllRegionVariableOrigin::Placeholder(_) => (true, false), - NllRegionVariableOrigin::Existential { .. } => (false, true), - }; - - let placeholder_universe = - if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT }; - - Self { - max_placeholder_universe_reached: placeholder_universe, - min_reachable_universe: definition.universe, - representative: rvid, - representative_is_placeholder, - representative_is_existential, - } - } - - /// The smallest-indexed universe reachable from and/or in this SCC. - fn min_universe(self) -> UniverseIndex { - self.min_reachable_universe - } - - fn merge_min_max_seen(&mut self, other: &Self) { - self.max_placeholder_universe_reached = std::cmp::max( - self.max_placeholder_universe_reached, - other.max_placeholder_universe_reached, - ); - - self.min_reachable_universe = - std::cmp::min(self.min_reachable_universe, other.min_reachable_universe); - } - - /// Returns `true` if during the annotated SCC reaches a placeholder - /// with a universe larger than the smallest reachable one, `false` otherwise. - pub(crate) fn has_incompatible_universes(&self) -> bool { - self.min_universe().cannot_name(self.max_placeholder_universe_reached) - } -} pub struct RegionInferenceContext<'tcx> { /// Contains the definition for every region variable. Region @@ -413,26 +298,6 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { debug!("SCC edges {:#?}", scc_node_to_edges); } -fn create_definitions<'tcx>( - infcx: &BorrowckInferCtxt<'tcx>, - universal_regions: &UniversalRegions<'tcx>, -) -> Frozen>> { - // Create a RegionDefinition for each inference variable. - let mut definitions: IndexVec<_, _> = infcx - .get_region_var_infos() - .iter() - .map(|info| RegionDefinition::new(info.universe, info.origin)) - .collect(); - - // Add the external name for all universal regions. - for (external_name, variable) in universal_regions.named_universal_regions_iter() { - debug!("region {variable:?} has external name {external_name:?}"); - definitions[variable].external_name = Some(external_name); - } - - Frozen::freeze(definitions) -} - impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -443,42 +308,30 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// of constraints produced by the MIR type check. pub(crate) fn new( infcx: &BorrowckInferCtxt<'tcx>, - constraints: MirTypeckRegionConstraints<'tcx>, + lowered_constraints: LoweredConstraints<'tcx>, universal_region_relations: Frozen>, location_map: Rc, ) -> Self { let universal_regions = &universal_region_relations.universal_regions; - let MirTypeckRegionConstraints { - placeholder_indices, - placeholder_index_to_region: _, - liveness_constraints, - mut outlives_constraints, - mut member_constraints, - universe_causes, + + let LoweredConstraints { + constraint_sccs, + definitions, + outlives_constraints, + scc_annotations, type_tests, - } = constraints; + liveness_constraints, + universe_causes, + placeholder_indices, + member_constraints, + } = lowered_constraints; debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); debug!("outlives constraints: {:#?}", outlives_constraints); debug!("placeholder_indices: {:#?}", placeholder_indices); debug!("type tests: {:#?}", type_tests); - if let Some(guar) = universal_region_relations.universal_regions.tainted_by_errors() { - // Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all - // outlives bounds that we may end up checking. - outlives_constraints = Default::default(); - member_constraints = Default::default(); - - // Also taint the entire scope. - infcx.set_tainted_by_errors(guar); - } - - let definitions = create_definitions(infcx, &universal_regions); - - let (constraint_sccs, scc_annotations) = - outlives_constraints.add_outlives_static(&universal_regions, &definitions); - let constraints = Frozen::freeze(outlives_constraints); - let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); + let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); if cfg!(debug_assertions) { sccs_info(infcx, &constraint_sccs); @@ -498,7 +351,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut result = Self { definitions, liveness_constraints, - constraints, + constraints: Frozen::freeze(outlives_constraints), constraint_graph, constraint_sccs, scc_annotations, @@ -904,20 +757,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - let a_annotation = self.scc_annotations[scc_a]; - let b_annotation = self.scc_annotations[scc_b]; - let a_universe = a_annotation.min_universe(); - - // If scc_b's declared universe is a subset of - // scc_a's declared universe (typically, both are ROOT), then - // it cannot contain any problematic universe elements. - if a_universe.can_name(b_annotation.min_universe()) { - return true; - } - - // Otherwise, there can be no placeholder in `b` with a too high - // universe index to name from `a`. - a_universe.can_name(b_annotation.max_placeholder_universe_reached) + self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b]) } /// Once regions have been propagated, this method is used to see @@ -2269,17 +2109,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { } impl<'tcx> RegionDefinition<'tcx> { - fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self { + pub(crate) fn new(rv_info: &RegionVariableInfo) -> Self { // Create a new region definition. Note that, for free // regions, the `external_name` field gets updated later in - // `init_free_and_bound_regions`. + // [[crate::eliminate_placeholders]]. - let origin = match rv_origin { + let origin = match rv_info.origin { RegionVariableOrigin::Nll(origin) => origin, _ => NllRegionVariableOrigin::Existential { from_forall: false }, }; - Self { origin, universe, external_name: None } + Self { origin, universe: rv_info.universe, external_name: None } } } From bf9c9d2f6e11f164dc528a810d990ce6e82a0aeb Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 3 Jun 2025 20:16:00 +0800 Subject: [PATCH 39/59] Preparing for merge from rustc --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index b1e9eec529e6..8b48bd518bd6 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -99e7c15e81385b38a8186b51edc4577d5d7b5bdd +c68032fd4c442d275f4daa571ba19c076106b490 From d2bf16ad6d80827800798a00ba584f95e9689573 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Fri, 9 May 2025 09:38:17 -0400 Subject: [PATCH 40/59] Rename `LifetimeSyntax` variants to lang-team-approved names --- compiler/rustc_ast_lowering/src/lib.rs | 6 +- compiler/rustc_hir/src/hir.rs | 72 +++++++++----------- compiler/rustc_hir/src/hir/tests.rs | 2 +- compiler/rustc_trait_selection/src/errors.rs | 2 +- 4 files changed, 39 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index f41627e479ff..64d4a00ea4d8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1406,7 +1406,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); let region = Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }; - (region, LifetimeSyntax::Hidden) + (region, LifetimeSyntax::Implicit) } }; self.lower_lifetime(®ion, LifetimeSource::Reference, syntax) @@ -1790,7 +1790,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { id, Ident::new(kw::UnderscoreLifetime, span), LifetimeSource::Path { angle_brackets }, - LifetimeSyntax::Hidden, + LifetimeSyntax::Implicit, ) } @@ -2422,7 +2422,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Ident::new(kw::UnderscoreLifetime, self.lower_span(span)), hir::LifetimeKind::ImplicitObjectLifetimeDefault, LifetimeSource::Other, - LifetimeSyntax::Hidden, + LifetimeSyntax::Implicit, ); debug!("elided_dyn_bound: r={:?}", r); self.arena.alloc(r) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4f05e1c816c3..974c24ef0f87 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -72,13 +72,13 @@ pub enum LifetimeSource { #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] pub enum LifetimeSyntax { /// E.g. `&Type`, `ContainsLifetime` - Hidden, + Implicit, /// E.g. `&'_ Type`, `ContainsLifetime<'_>`, `impl Trait + '_`, `impl Trait + use<'_>` - Anonymous, + ExplicitAnonymous, /// E.g. `&'a Type`, `ContainsLifetime<'a>`, `impl Trait + 'a`, `impl Trait + use<'a>` - Named, + ExplicitBound, } impl From for LifetimeSyntax { @@ -88,10 +88,10 @@ impl From for LifetimeSyntax { if name == sym::empty { unreachable!("A lifetime name should never be empty"); } else if name == kw::UnderscoreLifetime { - LifetimeSyntax::Anonymous + LifetimeSyntax::ExplicitAnonymous } else { debug_assert!(name.as_str().starts_with('\'')); - LifetimeSyntax::Named + LifetimeSyntax::ExplicitBound } } } @@ -102,48 +102,48 @@ impl From for LifetimeSyntax { /// /// ``` /// #[repr(C)] -/// struct S<'a>(&'a u32); // res=Param, name='a, source=Reference, syntax=Named +/// struct S<'a>(&'a u32); // res=Param, name='a, source=Reference, syntax=ExplicitBound /// unsafe extern "C" { -/// fn f1(s: S); // res=Param, name='_, source=Path, syntax=Hidden -/// fn f2(s: S<'_>); // res=Param, name='_, source=Path, syntax=Anonymous -/// fn f3<'a>(s: S<'a>); // res=Param, name='a, source=Path, syntax=Named +/// fn f1(s: S); // res=Param, name='_, source=Path, syntax=Implicit +/// fn f2(s: S<'_>); // res=Param, name='_, source=Path, syntax=ExplicitAnonymous +/// fn f3<'a>(s: S<'a>); // res=Param, name='a, source=Path, syntax=ExplicitBound /// } /// -/// struct St<'a> { x: &'a u32 } // res=Param, name='a, source=Reference, syntax=Named +/// struct St<'a> { x: &'a u32 } // res=Param, name='a, source=Reference, syntax=ExplicitBound /// fn f() { -/// _ = St { x: &0 }; // res=Infer, name='_, source=Path, syntax=Hidden -/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, source=Path, syntax=Anonymous +/// _ = St { x: &0 }; // res=Infer, name='_, source=Path, syntax=Implicit +/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, source=Path, syntax=ExplicitAnonymous /// } /// -/// struct Name<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named -/// const A: Name = Name("a"); // res=Static, name='_, source=Path, syntax=Hidden -/// const B: &str = ""; // res=Static, name='_, source=Reference, syntax=Hidden -/// static C: &'_ str = ""; // res=Static, name='_, source=Reference, syntax=Anonymous -/// static D: &'static str = ""; // res=Static, name='static, source=Reference, syntax=Named +/// struct Name<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=ExplicitBound +/// const A: Name = Name("a"); // res=Static, name='_, source=Path, syntax=Implicit +/// const B: &str = ""; // res=Static, name='_, source=Reference, syntax=Implicit +/// static C: &'_ str = ""; // res=Static, name='_, source=Reference, syntax=ExplicitAnonymous +/// static D: &'static str = ""; // res=Static, name='static, source=Reference, syntax=ExplicitBound /// /// trait Tr {} -/// fn tr(_: Box) {} // res=ImplicitObjectLifetimeDefault, name='_, source=Other, syntax=Hidden +/// fn tr(_: Box) {} // res=ImplicitObjectLifetimeDefault, name='_, source=Other, syntax=Implicit /// /// fn capture_outlives<'a>() -> -/// impl FnOnce() + 'a // res=Param, ident='a, source=OutlivesBound, syntax=Named +/// impl FnOnce() + 'a // res=Param, ident='a, source=OutlivesBound, syntax=ExplicitBound /// { /// || {} /// } /// /// fn capture_precise<'a>() -> -/// impl FnOnce() + use<'a> // res=Param, ident='a, source=PreciseCapturing, syntax=Named +/// impl FnOnce() + use<'a> // res=Param, ident='a, source=PreciseCapturing, syntax=ExplicitBound /// { /// || {} /// } /// /// // (commented out because these cases trigger errors) -/// // struct S1<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named -/// // struct S2(S1); // res=Error, name='_, source=Path, syntax=Hidden -/// // struct S3(S1<'_>); // res=Error, name='_, source=Path, syntax=Anonymous -/// // struct S4(S1<'a>); // res=Error, name='a, source=Path, syntax=Named +/// // struct S1<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=ExplicitBound +/// // struct S2(S1); // res=Error, name='_, source=Path, syntax=Implicit +/// // struct S3(S1<'_>); // res=Error, name='_, source=Path, syntax=ExplicitAnonymous +/// // struct S4(S1<'a>); // res=Error, name='a, source=Path, syntax=ExplicitBound /// ``` /// -/// Some combinations that cannot occur are `LifetimeSyntax::Hidden` with +/// Some combinations that cannot occur are `LifetimeSyntax::Implicit` with /// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing` /// — there's no way to "elide" these lifetimes. #[derive(Debug, Copy, Clone, HashStable_Generic)] @@ -287,12 +287,8 @@ impl Lifetime { self.ident.name == kw::UnderscoreLifetime } - pub fn is_syntactically_hidden(&self) -> bool { - matches!(self.syntax, LifetimeSyntax::Hidden) - } - - pub fn is_syntactically_anonymous(&self) -> bool { - matches!(self.syntax, LifetimeSyntax::Anonymous) + pub fn is_implicit(&self) -> bool { + matches!(self.syntax, LifetimeSyntax::Implicit) } pub fn is_static(&self) -> bool { @@ -307,28 +303,28 @@ impl Lifetime { match (self.syntax, self.source) { // The user wrote `'a` or `'_`. - (Named | Anonymous, _) => (self.ident.span, format!("{new_lifetime}")), + (ExplicitBound | ExplicitAnonymous, _) => (self.ident.span, format!("{new_lifetime}")), // The user wrote `Path`, and omitted the `'_,`. - (Hidden, Path { angle_brackets: AngleBrackets::Full }) => { + (Implicit, Path { angle_brackets: AngleBrackets::Full }) => { (self.ident.span, format!("{new_lifetime}, ")) } // The user wrote `Path<>`, and omitted the `'_`.. - (Hidden, Path { angle_brackets: AngleBrackets::Empty }) => { + (Implicit, Path { angle_brackets: AngleBrackets::Empty }) => { (self.ident.span, format!("{new_lifetime}")) } // The user wrote `Path` and omitted the `<'_>`. - (Hidden, Path { angle_brackets: AngleBrackets::Missing }) => { + (Implicit, Path { angle_brackets: AngleBrackets::Missing }) => { (self.ident.span.shrink_to_hi(), format!("<{new_lifetime}>")) } // The user wrote `&type` or `&mut type`. - (Hidden, Reference) => (self.ident.span, format!("{new_lifetime} ")), + (Implicit, Reference) => (self.ident.span, format!("{new_lifetime} ")), - (Hidden, source) => { - unreachable!("can't suggest for a hidden lifetime of {source:?}") + (Implicit, source) => { + unreachable!("can't suggest for a implicit lifetime of {source:?}") } } } diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs index 8684adee29c9..1fd793bc1616 100644 --- a/compiler/rustc_hir/src/hir/tests.rs +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -55,7 +55,7 @@ fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) { ident: Ident::new(sym::name, DUMMY_SP), kind: LifetimeKind::Static, source: LifetimeSource::Other, - syntax: LifetimeSyntax::Hidden, + syntax: LifetimeSyntax::Implicit, }; let unambig = TyKind::TraitObject::<'_, ()>(&[], TaggedRef::new(<, syntax)); let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) }; diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 779c861637a4..ee79d31ac696 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -592,7 +592,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { matches!( arg, hir::GenericArg::Lifetime(lifetime) - if lifetime.is_syntactically_hidden() + if lifetime.is_implicit() ) }) { self.suggestions.push(( From 674a4358e8d37bcf859f55f5f23e61d0e1be7a63 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Tue, 3 Jun 2025 13:10:58 -0700 Subject: [PATCH 41/59] update gpu offload build command --- src/bootstrap/src/core/build_steps/llvm.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 20a4d1a15158..2b7703000cbf 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -443,23 +443,26 @@ impl Step for Llvm { // See https://github.com/rust-lang/rust/pull/50104 cfg.define("LLVM_ENABLE_LIBXML2", "OFF"); - if !enabled_llvm_projects.is_empty() { - enabled_llvm_projects.sort(); - enabled_llvm_projects.dedup(); - cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";")); - } - let mut enabled_llvm_runtimes = Vec::new(); if helpers::forcing_clang_based_tests() { enabled_llvm_runtimes.push("compiler-rt"); } + // This is an experimental flag, which likely builds more than necessary. + // We will optimize it when we get closer to releasing it on nightly. if builder.config.llvm_offload { enabled_llvm_runtimes.push("offload"); //FIXME(ZuseZ4): LLVM intends to drop the offload dependency on openmp. //Remove this line once they achieved it. enabled_llvm_runtimes.push("openmp"); + enabled_llvm_projects.push("compiler-rt"); + } + + if !enabled_llvm_projects.is_empty() { + enabled_llvm_projects.sort(); + enabled_llvm_projects.dedup(); + cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";")); } if !enabled_llvm_runtimes.is_empty() { From 763663ad56672b0f0635ad25c0c1a52e7773e0b9 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Tue, 3 Jun 2025 21:17:33 +0000 Subject: [PATCH 42/59] rustdoc-json-type: Depend on `serde` and `serde_derive` seperately Before this commit, serde_derive is built before serde. But serde does not depend on serde_derive, so that is not needed. Instead, build serde and serde_derive in parallel. This speeds up compilation for users depending on rustdoc-json-types out of tree. Imports: https://www.github.com/rust-lang/rustdoc-types/pull/49 Co-authored-by: Martin Nordholts --- Cargo.lock | 1 + src/rustdoc-json-types/Cargo.toml | 3 ++- src/rustdoc-json-types/lib.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43f5f40925b6..164617c909fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4673,6 +4673,7 @@ dependencies = [ "bincode", "rustc-hash 2.1.1", "serde", + "serde_derive", "serde_json", ] diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index 14ff1d088163..a38d34ef0e7d 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -10,7 +10,8 @@ path = "lib.rs" default = ["rustc-hash"] [dependencies] -serde = { version = "1.0", features = ["derive"] } +serde = "1.0" +serde_derive = "1.0" rustc-hash = { version = "2.0", optional = true } [dev-dependencies] diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index f1c375bd2fe1..8a3ab6f86407 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -21,7 +21,7 @@ use std::path::PathBuf; #[cfg(feature = "rustc-hash")] use rustc_hash::FxHashMap as HashMap; -use serde::{Deserialize, Serialize}; +use serde_derive::{Deserialize, Serialize}; pub type FxHashMap = HashMap; // re-export for use in src/librustdoc From f455c4fc39bcc04d104bc440ebc1c23f183f20f9 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Fri, 2 May 2025 10:50:03 +0200 Subject: [PATCH 43/59] Use an enum for SCC representatives, plus other code review Co-authored-by: lcnr --- ...placeholders.rs => handle_placeholders.rs} | 165 ++++++++---------- compiler/rustc_borrowck/src/lib.rs | 2 +- compiler/rustc_borrowck/src/nll.rs | 4 +- .../rustc_borrowck/src/polonius/legacy/mod.rs | 2 +- .../src/region_infer/dump_mir.rs | 2 +- .../rustc_borrowck/src/region_infer/mod.rs | 90 ++++++---- .../src/region_infer/opaque_types.rs | 2 +- 7 files changed, 131 insertions(+), 136 deletions(-) rename compiler/rustc_borrowck/src/{eliminate_placeholders.rs => handle_placeholders.rs} (70%) diff --git a/compiler/rustc_borrowck/src/eliminate_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs similarity index 70% rename from compiler/rustc_borrowck/src/eliminate_placeholders.rs rename to compiler/rustc_borrowck/src/handle_placeholders.rs index e8e1acbd7a5a..aaaf2f45c869 100644 --- a/compiler/rustc_borrowck/src/eliminate_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -1,14 +1,13 @@ //! Logic for lowering higher-kinded outlives constraints //! (with placeholders and universes) and turn them into regular //! outlives constraints. -//! -//! This logic is provisional and should be removed once the trait -//! solver can handle this kind of constraint. + use rustc_data_structures::frozen::Frozen; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::scc; use rustc_data_structures::graph::scc::Sccs; use rustc_index::IndexVec; +use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, UniverseIndex}; use tracing::debug; @@ -18,7 +17,7 @@ use crate::consumers::OutlivesConstraint; use crate::diagnostics::UniverseInfo; use crate::member_constraints::MemberConstraintSet; use crate::region_infer::values::{LivenessValues, PlaceholderIndices}; -use crate::region_infer::{ConstraintSccs, RegionDefinition, TypeTest}; +use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest}; use crate::ty::VarianceDiagInfo; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; @@ -32,7 +31,7 @@ pub(crate) struct LoweredConstraints<'tcx> { pub(crate) definitions: Frozen>>, pub(crate) scc_annotations: IndexVec, pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, - pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, + pub(crate) outlives_constraints: Frozen>, pub(crate) type_tests: Vec>, pub(crate) liveness_constraints: LivenessValues, pub(crate) universe_causes: FxIndexMap>, @@ -73,45 +72,35 @@ pub(crate) struct RegionTracker { /// This includes placeholders within this SCC. max_placeholder_universe_reached: UniverseIndex, - /// The smallest universe index reachable form the nodes of this SCC. - min_reachable_universe: UniverseIndex, + /// The largest universe nameable from this SCC. + /// It is the smallest nameable universes of all + /// existential regions reachable from it. + max_nameable_universe: UniverseIndex, - /// The representative Region Variable Id for this SCC. We prefer - /// placeholders over existentially quantified variables, otherwise - /// it's the one with the smallest Region Variable ID. - pub(crate) representative: RegionVid, - - /// Is the current representative a placeholder? - representative_is_placeholder: bool, - - /// Is the current representative existentially quantified? - representative_is_existential: bool, + /// The representative Region Variable Id for this SCC. + pub(crate) representative: Representative, } impl RegionTracker { pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { - let (representative_is_placeholder, representative_is_existential) = match definition.origin - { - NllRegionVariableOrigin::FreeRegion => (false, false), - NllRegionVariableOrigin::Placeholder(_) => (true, false), - NllRegionVariableOrigin::Existential { .. } => (false, true), - }; - let placeholder_universe = - if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT }; + if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) { + definition.universe + } else { + UniverseIndex::ROOT + }; Self { max_placeholder_universe_reached: placeholder_universe, - min_reachable_universe: definition.universe, - representative: rvid, - representative_is_placeholder, - representative_is_existential, + max_nameable_universe: definition.universe, + representative: Representative::new(rvid, definition), } } - /// The smallest-indexed universe reachable from and/or in this SCC. - pub(crate) fn min_universe(self) -> UniverseIndex { - self.min_reachable_universe + /// The largest universe this SCC can name. It's the smallest + /// largest nameable uninverse of any reachable region. + pub(crate) fn max_nameable_universe(self) -> UniverseIndex { + self.max_nameable_universe } fn merge_min_max_seen(&mut self, other: &Self) { @@ -120,40 +109,29 @@ impl RegionTracker { other.max_placeholder_universe_reached, ); - self.min_reachable_universe = - std::cmp::min(self.min_reachable_universe, other.min_reachable_universe); + self.max_nameable_universe = + std::cmp::min(self.max_nameable_universe, other.max_nameable_universe); } /// Returns `true` if during the annotated SCC reaches a placeholder - /// with a universe larger than the smallest reachable one, `false` otherwise. + /// with a universe larger than the smallest nameable universe of any + /// reachable existential region. pub(crate) fn has_incompatible_universes(&self) -> bool { - self.min_universe().cannot_name(self.max_placeholder_universe_reached) + self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached) } - /// Determine if the tracked universes of the two SCCs - /// are compatible. + /// Determine if the tracked universes of the two SCCs are compatible. pub(crate) fn universe_compatible_with(&self, other: Self) -> bool { - self.min_universe().can_name(other.min_universe()) - || self.min_universe().can_name(other.max_placeholder_universe_reached) + self.max_nameable_universe().can_name(other.max_nameable_universe()) + || self.max_nameable_universe().can_name(other.max_placeholder_universe_reached) } } impl scc::Annotation for RegionTracker { - fn merge_scc(mut self, mut other: Self) -> Self { - // Prefer any placeholder over any existential - if other.representative_is_placeholder && self.representative_is_existential { - other.merge_min_max_seen(&self); - return other; - } - - if self.representative_is_placeholder && other.representative_is_existential - || (self.representative <= other.representative) - { - self.merge_min_max_seen(&other); - return self; - } - other.merge_min_max_seen(&self); - other + fn merge_scc(mut self, other: Self) -> Self { + self.representative = self.representative.merge_scc(other.representative); + self.merge_min_max_seen(&other); + self } fn merge_reached(mut self, other: Self) -> Self { @@ -164,7 +142,7 @@ impl scc::Annotation for RegionTracker { } /// Determines if the region variable definitions contain -/// placeholers, and compute them for later use. +/// placeholders, and compute them for later use. fn region_definitions<'tcx>( universal_regions: &UniversalRegions<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, @@ -177,12 +155,19 @@ fn region_definitions<'tcx>( let mut has_placeholders = false; for info in var_infos.iter() { - let definition = RegionDefinition::new(info); - has_placeholders |= matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)); + let origin = match info.origin { + RegionVariableOrigin::Nll(origin) => origin, + _ => NllRegionVariableOrigin::Existential { from_forall: false }, + }; + + let definition = RegionDefinition { origin, universe: info.universe, external_name: None }; + + has_placeholders |= matches!(origin, NllRegionVariableOrigin::Placeholder(_)); definitions.push(definition); } // Add external names from universal regions in fun function definitions. + // FIXME: this two-step method is annoying, but I don't know how to avoid it. for (external_name, variable) in universal_regions.named_universal_regions_iter() { debug!("region {:?} has external name {:?}", variable, external_name); definitions[variable].external_name = Some(external_name); @@ -190,29 +175,19 @@ fn region_definitions<'tcx>( (Frozen::freeze(definitions), has_placeholders) } -/// This method handles Universe errors by rewriting the constraint +/// This method handles placeholders by rewriting the constraint /// graph. For each strongly connected component in the constraint /// graph such that there is a series of constraints /// A: B: C: ... : X where -/// A's universe is smaller than X's and A is a placeholder, +/// A contains a placeholder whose universe cannot be named by X, /// add a constraint that A: 'static. This is a safe upper bound /// in the face of borrow checker/trait solver limitations that will /// eventually go away. /// /// For a more precise definition, see the documentation for -/// [`RegionTracker`] and its methods!. -/// -/// Since universes can also be involved in errors (if one placeholder -/// transitively outlives another), this function also flags those. -/// -/// Additionally, it similarly rewrites type-tests. -/// -/// This edge case used to be handled during constraint propagation -/// by iterating over the strongly connected components in the constraint -/// graph while maintaining a set of bookkeeping mappings similar -/// to what is stored in `RegionTracker` and manually adding 'sttaic as -/// needed. +/// [`RegionTracker`] and its methods! /// +/// This edge case used to be handled during constraint propagation. /// It was rewritten as part of the Polonius project with the goal of moving /// higher-kindedness concerns out of the path of the borrow checker, /// for two reasons: @@ -228,7 +203,7 @@ fn region_definitions<'tcx>( /// This code is a stop-gap measure in preparation for the future trait solver. /// /// Every constraint added by this method is an internal `IllegalUniverse` constraint. -pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( +pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( constraints: MirTypeckRegionConstraints<'tcx>, universal_region_relations: &Frozen>, infcx: &BorrowckInferCtxt<'tcx>, @@ -267,13 +242,14 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( ) }; + let mut scc_annotations = SccAnnotations::init(&definitions); + let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); + // This code structure is a bit convoluted because it allows for a planned // future change where the early return here has a different type of annotation // that does much less work. if !has_placeholders { debug!("No placeholder regions found; skipping rewriting logic!"); - let mut scc_annotations = SccAnnotations::init(&definitions); - let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); return LoweredConstraints { type_tests, @@ -281,7 +257,7 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( constraint_sccs, scc_annotations: scc_annotations.scc_to_annotation, definitions, - outlives_constraints, + outlives_constraints: Frozen::freeze(outlives_constraints), liveness_constraints, universe_causes, placeholder_indices, @@ -289,14 +265,14 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( } debug!("Placeholders present; activating placeholder handling logic!"); - let mut annotations = SccAnnotations::init(&definitions); - let sccs = compute_sccs(&outlives_constraints, &mut annotations); + let added_constraints = rewrite_placeholder_outlives( + &constraint_sccs, + &scc_annotations, + fr_static, + &mut outlives_constraints, + ); - let outlives_static = - rewrite_outlives(&sccs, &annotations, fr_static, &mut outlives_constraints); - - let (sccs, scc_annotations) = if !outlives_static.is_empty() { - debug!("The following SCCs had :'static constraints added: {:?}", outlives_static); + let (constraint_sccs, scc_annotations) = if added_constraints { let mut annotations = SccAnnotations::init(&definitions); // We changed the constraint set and so must recompute SCCs. @@ -307,15 +283,15 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( } else { // If we didn't add any back-edges; no more work needs doing debug!("No constraints rewritten!"); - (sccs, annotations.scc_to_annotation) + (constraint_sccs, scc_annotations.scc_to_annotation) }; LoweredConstraints { - constraint_sccs: sccs, + constraint_sccs, definitions, scc_annotations, member_constraints, - outlives_constraints, + outlives_constraints: Frozen::freeze(outlives_constraints), type_tests, liveness_constraints, universe_causes, @@ -323,15 +299,15 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>( } } -fn rewrite_outlives<'tcx>( +fn rewrite_placeholder_outlives<'tcx>( sccs: &Sccs, annotations: &SccAnnotations<'_, '_, RegionTracker>, fr_static: RegionVid, outlives_constraints: &mut OutlivesConstraintSet<'tcx>, -) -> FxHashSet { - // Changed to `true` if we added any constraints to `self` and need to +) -> bool { + // Changed to `true` if we added any constraints and need to // recompute SCCs. - let mut outlives_static = FxHashSet::default(); + let mut added_constraints = false; let annotations = &annotations.scc_to_annotation; @@ -354,9 +330,8 @@ fn rewrite_outlives<'tcx>( // needed for correctness, since an SCC upstream of another with // a universe violation will "infect" its downstream SCCs to also // outlive static. - outlives_static.insert(scc); let scc_representative_outlives_static = OutlivesConstraint { - sup: annotation.representative, + sup: annotation.representative.rvid(), sub: fr_static, category: ConstraintCategory::IllegalUniverse, locations: Locations::All(rustc_span::DUMMY_SP), @@ -365,7 +340,9 @@ fn rewrite_outlives<'tcx>( from_closure: false, }; outlives_constraints.push(scc_representative_outlives_static); + added_constraints = true; + debug!("Added {:?}: 'static!", annotation.representative.rvid()); } } - outlives_static + added_constraints } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index edf5494e8867..8c4d5c0debee 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -75,7 +75,7 @@ mod constraints; mod dataflow; mod def_use; mod diagnostics; -mod eliminate_placeholders; +mod handle_placeholders; mod member_constraints; mod nll; mod path_utils; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 9b1a5df8d06b..66ab39476b11 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -22,7 +22,7 @@ use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; use crate::diagnostics::RegionErrors; -use crate::eliminate_placeholders::rewrite_higher_kinded_outlives_as_constraints; +use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints; use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, @@ -118,7 +118,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( Rc::clone(&location_map), ); - let lowered_constraints = rewrite_higher_kinded_outlives_as_constraints( + let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( constraints, &universal_region_relations, infcx, diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 67da43ca5788..05fd6e39476b 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -13,7 +13,7 @@ use tracing::debug; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; -use crate::eliminate_placeholders::LoweredConstraints; +use crate::handle_placeholders::LoweredConstraints; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index ef3d6309c19c..a9ab30fd8fa3 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { "| {r:rw$?} | {ui:4?} | {v}", r = region, rw = REGION_WIDTH, - ui = self.region_universe(region), + ui = self.max_nameable_universe(self.constraint_sccs.scc(region)), v = self.region_value_str(region), )?; } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index cfad071460b4..dcb09d5be140 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -4,15 +4,13 @@ use std::rc::Rc; use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::scc::{self, Sccs}; use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::IndexVec; use rustc_infer::infer::outlives::test_type_match; -use rustc_infer::infer::region_constraints::{ - GenericKind, RegionVariableInfo, VerifyBound, VerifyIfEq, -}; -use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; +use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::bug; use rustc_middle::mir::{ AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint, @@ -29,7 +27,7 @@ use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::eliminate_placeholders::{LoweredConstraints, RegionTracker}; +use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; @@ -50,6 +48,47 @@ mod reverse_sccs; pub(crate) mod values; +/// The representative region variable for an SCC, tagged by its origin. +/// We prefer placeholders over existentially quantified variables, otherwise +/// it's the one with the smallest Region Variable ID. In other words, +/// the order of this enumeration really matters! +#[derive(Copy, Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] +pub(crate) enum Representative { + FreeRegion(RegionVid), + Placeholder(RegionVid), + Existential(RegionVid), +} + +impl Representative { + pub(crate) fn rvid(self) -> RegionVid { + match self { + Representative::FreeRegion(region_vid) + | Representative::Placeholder(region_vid) + | Representative::Existential(region_vid) => region_vid, + } + } + + pub(crate) fn new(r: RegionVid, definition: &RegionDefinition<'_>) -> Self { + match definition.origin { + NllRegionVariableOrigin::FreeRegion => Representative::FreeRegion(r), + NllRegionVariableOrigin::Placeholder(_) => Representative::Placeholder(r), + NllRegionVariableOrigin::Existential { .. } => Representative::Existential(r), + } + } +} + +impl scc::Annotation for Representative { + fn merge_scc(self, other: Self) -> Self { + // Just pick the smallest one. Note that we order by tag first! + std::cmp::min(self, other) + } + + // For reachability, we do nothing since the representative doesn't change. + fn merge_reached(self, _other: Self) -> Self { + self + } +} + pub(crate) type ConstraintSccs = Sccs; pub struct RegionInferenceContext<'tcx> { @@ -351,7 +390,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut result = Self { definitions, liveness_constraints, - constraints: Frozen::freeze(outlives_constraints), + constraints: outlives_constraints, constraint_graph, constraint_sccs, scc_annotations, @@ -510,11 +549,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_values.placeholders_contained_in(scc) } - /// Returns access to the value of `r` for debugging purposes. - pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { - self.scc_universe(self.constraint_sccs.scc(r)) - } - /// Once region solving has completed, this function will return the member constraints that /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`. pub(crate) fn applied_member_constraints( @@ -681,7 +715,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if !self.scc_universe(scc).is_root() { + if !self.max_nameable_universe(scc).is_root() { return; } @@ -861,7 +895,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { "lower_bound = {:?} r_scc={:?} universe={:?}", lower_bound, r_scc, - self.scc_universe(r_scc) + self.max_nameable_universe(r_scc) ); // If the type test requires that `T: 'a` where `'a` is a // placeholder from another universe, that effectively requires @@ -1339,10 +1373,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// The minimum universe of any variable reachable from this - /// SCC, inside or outside of it. - fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { - self.scc_annotations[scc].min_universe() + /// The largest universe of any region nameable from this SCC. + fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { + self.scc_annotations[scc].max_nameable_universe() } /// Checks the final value for the free region `fr` to see if it @@ -1364,7 +1397,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Because this free region must be in the ROOT universe, we // know it cannot contain any bound universes. - assert!(self.scc_universe(longer_fr_scc).is_root()); + assert!(self.max_nameable_universe(longer_fr_scc).is_root()); // Only check all of the relations for the main representative of each // SCC, otherwise just check that we outlive said representative. This @@ -1755,7 +1788,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(skip(self), level = "trace", ret)] pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); - trace!(universe = ?self.region_universe(fr1)); + trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1))); self.find_constraint_paths_between_regions(fr1, |r| { // First look for some `r` such that `fr1: r` and `r` is live at `location` trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); @@ -2086,7 +2119,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// they *must* be equal (though not having the same repr does not /// mean they are unequal). fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { - self.scc_annotations[scc].representative + self.scc_annotations[scc].representative.rvid() } pub(crate) fn liveness_constraints(&self) -> &LivenessValues { @@ -2108,21 +2141,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } -impl<'tcx> RegionDefinition<'tcx> { - pub(crate) fn new(rv_info: &RegionVariableInfo) -> Self { - // Create a new region definition. Note that, for free - // regions, the `external_name` field gets updated later in - // [[crate::eliminate_placeholders]]. - - let origin = match rv_info.origin { - RegionVariableOrigin::Nll(origin) => origin, - _ => NllRegionVariableOrigin::Existential { from_forall: false }, - }; - - Self { origin, universe: rv_info.universe, external_name: None } - } -} - #[derive(Clone, Debug)] pub(crate) struct BlameConstraint<'tcx> { pub category: ConstraintCategory<'tcx>, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 550c57338d30..3c05fa2ae0fa 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -191,7 +191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc = self.constraint_sccs.scc(vid); // Special handling of higher-ranked regions. - if !self.scc_universe(scc).is_root() { + if !self.max_nameable_universe(scc).is_root() { match self.scc_values.placeholders_contained_in(scc).enumerate().last() { // If the region contains a single placeholder then they're equal. Some((0, placeholder)) => { From ff005610d5956cee82d4b2d162c3ee8da71e8164 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Tue, 3 Jun 2025 17:47:38 +0500 Subject: [PATCH 44/59] cleaned up some tests --- tests/ui/break-diverging-value.rs | 37 ------------ .../cancel-clean-via-immediate-rvalue-ref.rs | 9 --- .../ui/cannot-mutate-captured-non-mut-var.rs | 15 ----- .../closures/closure-immut-capture-error.rs | 23 ++++++++ .../closure-immut-capture-error.stderr} | 12 ++-- tests/ui/codegen/rvalue-mut-ref-box-drop.rs | 13 +++++ .../loops/loop-break-never-type-mismatch.rs | 56 +++++++++++++++++++ .../loop-break-never-type-mismatch.stderr} | 4 +- .../clone-unwind-rc-cleanup.rs} | 33 +++-------- .../copy-trait-implicit-copy.rs} | 6 +- 10 files changed, 112 insertions(+), 96 deletions(-) delete mode 100644 tests/ui/break-diverging-value.rs delete mode 100644 tests/ui/cancel-clean-via-immediate-rvalue-ref.rs delete mode 100644 tests/ui/cannot-mutate-captured-non-mut-var.rs create mode 100644 tests/ui/closures/closure-immut-capture-error.rs rename tests/ui/{cannot-mutate-captured-non-mut-var.stderr => closures/closure-immut-capture-error.stderr} (62%) create mode 100644 tests/ui/codegen/rvalue-mut-ref-box-drop.rs create mode 100644 tests/ui/loops/loop-break-never-type-mismatch.rs rename tests/ui/{break-diverging-value.stderr => loops/loop-break-never-type-mismatch.stderr} (84%) rename tests/ui/{builtin-clone-unwind.rs => traits/clone-unwind-rc-cleanup.rs} (54%) rename tests/ui/{can-copy-pod.rs => traits/copy-trait-implicit-copy.rs} (65%) diff --git a/tests/ui/break-diverging-value.rs b/tests/ui/break-diverging-value.rs deleted file mode 100644 index d070fddaffc1..000000000000 --- a/tests/ui/break-diverging-value.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(never_type)] - -fn loop_break_return() -> i32 { - let loop_value = loop { break return 0 }; // ok -} - -fn loop_break_loop() -> i32 { - let loop_value = loop { break loop {} }; // ok -} - -fn loop_break_break() -> i32 { //~ ERROR mismatched types - let loop_value = loop { break break }; -} - -fn loop_break_return_2() -> i32 { - let loop_value = loop { break { return 0; () } }; // ok -} - -enum Void {} - -fn get_void() -> Void { - panic!() -} - -fn loop_break_void() -> i32 { //~ ERROR mismatched types - let loop_value = loop { break get_void() }; -} - -fn get_never() -> ! { - panic!() -} - -fn loop_break_never() -> i32 { - let loop_value = loop { break get_never() }; // ok -} - -fn main() {} diff --git a/tests/ui/cancel-clean-via-immediate-rvalue-ref.rs b/tests/ui/cancel-clean-via-immediate-rvalue-ref.rs deleted file mode 100644 index 12d143bd9895..000000000000 --- a/tests/ui/cancel-clean-via-immediate-rvalue-ref.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass - -fn foo(x: &mut Box) { - *x = Box::new(5); -} - -pub fn main() { - foo(&mut Box::new(4)); -} diff --git a/tests/ui/cannot-mutate-captured-non-mut-var.rs b/tests/ui/cannot-mutate-captured-non-mut-var.rs deleted file mode 100644 index 952dab25bf9d..000000000000 --- a/tests/ui/cannot-mutate-captured-non-mut-var.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(unboxed_closures, tuple_trait)] - -use std::io::Read; - -fn to_fn_once>(f: F) -> F { f } - -fn main() { - let x = 1; - to_fn_once(move|| { x = 2; }); - //~^ ERROR: cannot assign to `x`, as it is not declared as mutable - - let s = std::io::stdin(); - to_fn_once(move|| { s.read_to_end(&mut Vec::new()); }); - //~^ ERROR: cannot borrow `s` as mutable, as it is not declared as mutable -} diff --git a/tests/ui/closures/closure-immut-capture-error.rs b/tests/ui/closures/closure-immut-capture-error.rs new file mode 100644 index 000000000000..19fe599f2411 --- /dev/null +++ b/tests/ui/closures/closure-immut-capture-error.rs @@ -0,0 +1,23 @@ +//! Tests that mutation of captured immutable variables in closures are not permitted. + +#![feature(unboxed_closures, tuple_trait)] + +use std::io::Read; + +fn to_fn_once>(f: F) -> F { + f +} + +fn main() { + let x = 1; + to_fn_once(move || { + x = 2; + //~^ ERROR: cannot assign to `x`, as it is not declared as mutable + }); + + let s = std::io::stdin(); + to_fn_once(move || { + s.read_to_end(&mut Vec::new()); + //~^ ERROR: cannot borrow `s` as mutable, as it is not declared as mutable + }); +} diff --git a/tests/ui/cannot-mutate-captured-non-mut-var.stderr b/tests/ui/closures/closure-immut-capture-error.stderr similarity index 62% rename from tests/ui/cannot-mutate-captured-non-mut-var.stderr rename to tests/ui/closures/closure-immut-capture-error.stderr index 8d794f8251f1..516cf7c074ee 100644 --- a/tests/ui/cannot-mutate-captured-non-mut-var.stderr +++ b/tests/ui/closures/closure-immut-capture-error.stderr @@ -1,8 +1,8 @@ error[E0594]: cannot assign to `x`, as it is not declared as mutable - --> $DIR/cannot-mutate-captured-non-mut-var.rs:9:25 + --> $DIR/closure-immut-capture-error.rs:14:9 | -LL | to_fn_once(move|| { x = 2; }); - | ^^^^^ cannot assign +LL | x = 2; + | ^^^^^ cannot assign | help: consider changing this to be mutable | @@ -10,10 +10,10 @@ LL | let mut x = 1; | +++ error[E0596]: cannot borrow `s` as mutable, as it is not declared as mutable - --> $DIR/cannot-mutate-captured-non-mut-var.rs:13:25 + --> $DIR/closure-immut-capture-error.rs:20:9 | -LL | to_fn_once(move|| { s.read_to_end(&mut Vec::new()); }); - | ^ cannot borrow as mutable +LL | s.read_to_end(&mut Vec::new()); + | ^ cannot borrow as mutable | help: consider changing this to be mutable | diff --git a/tests/ui/codegen/rvalue-mut-ref-box-drop.rs b/tests/ui/codegen/rvalue-mut-ref-box-drop.rs new file mode 100644 index 000000000000..441820ad64e4 --- /dev/null +++ b/tests/ui/codegen/rvalue-mut-ref-box-drop.rs @@ -0,0 +1,13 @@ +//! Tests cleanup of a temporary `Box` rvalue passed as a mutable reference. +//! +//! - Issue: . + +//@ run-pass + +fn foo(x: &mut Box) { + *x = Box::new(5); +} + +pub fn main() { + foo(&mut Box::new(4)); +} diff --git a/tests/ui/loops/loop-break-never-type-mismatch.rs b/tests/ui/loops/loop-break-never-type-mismatch.rs new file mode 100644 index 000000000000..2be4e4f4ac5a --- /dev/null +++ b/tests/ui/loops/loop-break-never-type-mismatch.rs @@ -0,0 +1,56 @@ +//! Tests type mismatches with `break` and diverging types in loops + +#![feature(never_type)] + +fn loop_break_return() -> i32 { + let loop_value = loop { + break return 0; + }; // ok +} + +fn loop_break_loop() -> i32 { + let loop_value = loop { + break loop {}; + }; // ok +} + +fn loop_break_break() -> i32 { + //~^ ERROR mismatched types + let loop_value = loop { + break break; + }; +} + +fn loop_break_return_2() -> i32 { + let loop_value = loop { + break { + return 0; + () + }; + }; // ok +} + +enum Void {} + +fn get_void() -> Void { + panic!() +} + +fn loop_break_void() -> i32 { + //~^ ERROR mismatched types + let loop_value = loop { + break get_void(); + }; +} + +fn get_never() -> ! { + panic!() +} + +fn loop_break_never() -> i32 { + let loop_value = loop { + break get_never(); + }; // ok +} + +fn main() {} diff --git a/tests/ui/break-diverging-value.stderr b/tests/ui/loops/loop-break-never-type-mismatch.stderr similarity index 84% rename from tests/ui/break-diverging-value.stderr rename to tests/ui/loops/loop-break-never-type-mismatch.stderr index 69edcd240800..e6868f375e28 100644 --- a/tests/ui/break-diverging-value.stderr +++ b/tests/ui/loops/loop-break-never-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/break-diverging-value.rs:11:26 + --> $DIR/loop-break-never-type-mismatch.rs:17:26 | LL | fn loop_break_break() -> i32 { | ---------------- ^^^ expected `i32`, found `()` @@ -7,7 +7,7 @@ LL | fn loop_break_break() -> i32 { | implicitly returns `()` as its body has no tail or `return` expression error[E0308]: mismatched types - --> $DIR/break-diverging-value.rs:25:25 + --> $DIR/loop-break-never-type-mismatch.rs:39:25 | LL | fn loop_break_void() -> i32 { | --------------- ^^^ expected `i32`, found `()` diff --git a/tests/ui/builtin-clone-unwind.rs b/tests/ui/traits/clone-unwind-rc-cleanup.rs similarity index 54% rename from tests/ui/builtin-clone-unwind.rs rename to tests/ui/traits/clone-unwind-rc-cleanup.rs index 507ea045b4f8..cd02050ea274 100644 --- a/tests/ui/builtin-clone-unwind.rs +++ b/tests/ui/traits/clone-unwind-rc-cleanup.rs @@ -1,14 +1,13 @@ +//! Tests cleanup behavior of the built-in `Clone` impl for tuples during unwinding. + //@ run-pass //@ needs-unwind #![allow(unused_variables)] #![allow(unused_imports)] -// Test that builtin implementations of `Clone` cleanup everything -// in case of unwinding. - -use std::thread; use std::rc::Rc; +use std::thread; struct S(Rc<()>); @@ -28,34 +27,20 @@ fn main() { // Unwinding with tuples... let ccounter = counter.clone(); let result = std::panic::catch_unwind(move || { - let _ = ( - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter) - ).clone(); + let _ = + (S(ccounter.clone()), S(ccounter.clone()), S(ccounter.clone()), S(ccounter)).clone(); }); assert!(result.is_err()); - assert_eq!( - 1, - Rc::strong_count(&counter) - ); + assert_eq!(1, Rc::strong_count(&counter)); // ... and with arrays. let ccounter = counter.clone(); let child = std::panic::catch_unwind(move || { - let _ = [ - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter) - ].clone(); + let _ = + [S(ccounter.clone()), S(ccounter.clone()), S(ccounter.clone()), S(ccounter)].clone(); }); assert!(child.is_err()); - assert_eq!( - 1, - Rc::strong_count(&counter) - ); + assert_eq!(1, Rc::strong_count(&counter)); } diff --git a/tests/ui/can-copy-pod.rs b/tests/ui/traits/copy-trait-implicit-copy.rs similarity index 65% rename from tests/ui/can-copy-pod.rs rename to tests/ui/traits/copy-trait-implicit-copy.rs index ffb8a08fa980..e06385587b4b 100644 --- a/tests/ui/can-copy-pod.rs +++ b/tests/ui/traits/copy-trait-implicit-copy.rs @@ -1,13 +1,13 @@ +//! Tests that type parameters with the `Copy` are implicitly copyable. + //@ run-pass /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -// Tests that type parameters with the `Copy` are implicitly copyable. - #![allow(dead_code)] -fn can_copy_copy(v: T) { +fn can_copy_copy(v: T) { let _a = v; let _b = v; } From 9091a94ff042d501b627f6244444f472a32fc972 Mon Sep 17 00:00:00 2001 From: smpdt <206221751+smpdt@users.noreply.github.com> Date: Wed, 4 Jun 2025 10:00:20 -0400 Subject: [PATCH 45/59] bootstrap: Fix file permissions when dereferencing symlinks --- src/bootstrap/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index cfb968f79ba1..07772b8932d9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1795,11 +1795,12 @@ Executed at: {executed_at}"#, let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)); let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos())); } - let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display())); + let mut metadata = t!(src.symlink_metadata(), format!("src = {}", src.display())); let mut src = src.to_path_buf(); if metadata.file_type().is_symlink() { if dereference_symlinks { src = t!(fs::canonicalize(src)); + metadata = t!(fs::metadata(&src), format!("target = {}", src.display())); } else { let link = t!(fs::read_link(src)); t!(self.symlink_file(link, dst)); From 06ab34e51604ae09d56b42cb4576a87dd44db5be Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Tue, 3 Jun 2025 20:59:42 +0500 Subject: [PATCH 46/59] cleaned up some tests --- tests/ui/char.rs | 13 ---- tests/ui/class-cast-to-trait.rs | 54 ----------------- tests/ui/class-cast-to-trait.stderr | 9 --- tests/ui/class-method-missing.rs | 21 ------- tests/ui/class-method-missing.stderr | 12 ---- tests/ui/cleanup-rvalue-for-scope.rs | 60 ------------------- .../ui/drop/for-expr-temporary-drop-scope.rs | 33 ++++++++++ .../enum-drop-cast-error.rs} | 4 ++ .../enum-drop-cast-error.stderr} | 2 +- tests/ui/privacy/trait-object-method-error.rs | 20 +++++++ .../privacy/trait-object-method-error.stderr | 15 +++++ tests/ui/traits/trait-impl-missing-method.rs | 13 ++++ .../traits/trait-impl-missing-method.stderr | 12 ++++ 13 files changed, 98 insertions(+), 170 deletions(-) delete mode 100644 tests/ui/char.rs delete mode 100644 tests/ui/class-cast-to-trait.rs delete mode 100644 tests/ui/class-cast-to-trait.stderr delete mode 100644 tests/ui/class-method-missing.rs delete mode 100644 tests/ui/class-method-missing.stderr delete mode 100644 tests/ui/cleanup-rvalue-for-scope.rs create mode 100644 tests/ui/drop/for-expr-temporary-drop-scope.rs rename tests/ui/{cenum_impl_drop_cast.rs => enum/enum-drop-cast-error.rs} (61%) rename tests/ui/{cenum_impl_drop_cast.stderr => enum/enum-drop-cast-error.stderr} (81%) create mode 100644 tests/ui/privacy/trait-object-method-error.rs create mode 100644 tests/ui/privacy/trait-object-method-error.stderr create mode 100644 tests/ui/traits/trait-impl-missing-method.rs create mode 100644 tests/ui/traits/trait-impl-missing-method.stderr diff --git a/tests/ui/char.rs b/tests/ui/char.rs deleted file mode 100644 index a7842f16fa7a..000000000000 --- a/tests/ui/char.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -pub fn main() { - let c: char = 'x'; - let d: char = 'x'; - assert_eq!(c, 'x'); - assert_eq!('x', c); - assert_eq!(c, c); - assert_eq!(c, d); - assert_eq!(d, c); - assert_eq!(d, 'x'); - assert_eq!('x', d); -} diff --git a/tests/ui/class-cast-to-trait.rs b/tests/ui/class-cast-to-trait.rs deleted file mode 100644 index ca98e4c90031..000000000000 --- a/tests/ui/class-cast-to-trait.rs +++ /dev/null @@ -1,54 +0,0 @@ -trait Noisy { - fn speak(&mut self); -} - -struct Cat { - meows : usize, - - how_hungry : isize, - name : String, -} - -impl Cat { - pub fn eat(&mut self) -> bool { - if self.how_hungry > 0 { - println!("OM NOM NOM"); - self.how_hungry -= 2; - return true; - } - else { - println!("Not hungry!"); - return false; - } - } -} - -impl Noisy for Cat { - fn speak(&mut self) { self.meow(); } - -} - -impl Cat { - fn meow(&mut self) { - println!("Meow"); - self.meows += 1; - if self.meows % 5 == 0 { - self.how_hungry += 1; - } - } -} - -fn cat(in_x : usize, in_y : isize, in_name: String) -> Cat { - Cat { - meows: in_x, - how_hungry: in_y, - name: in_name - } -} - - - -fn main() { - let nyan: Box = Box::new(cat(0, 2, "nyan".to_string())) as Box; - nyan.eat(); //~ ERROR no method named `eat` found -} diff --git a/tests/ui/class-cast-to-trait.stderr b/tests/ui/class-cast-to-trait.stderr deleted file mode 100644 index 4ea0f41c3ed9..000000000000 --- a/tests/ui/class-cast-to-trait.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0599]: no method named `eat` found for struct `Box` in the current scope - --> $DIR/class-cast-to-trait.rs:53:8 - | -LL | nyan.eat(); - | ^^^ method not found in `Box` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/class-method-missing.rs b/tests/ui/class-method-missing.rs deleted file mode 100644 index 5dc18328f31e..000000000000 --- a/tests/ui/class-method-missing.rs +++ /dev/null @@ -1,21 +0,0 @@ -trait Animal { - fn eat(&self); -} - -struct Cat { - meows: usize, -} - -impl Animal for Cat { - //~^ ERROR not all trait items implemented, missing: `eat` -} - -fn cat(in_x : usize) -> Cat { - Cat { - meows: in_x - } -} - -fn main() { - let nyan = cat(0); -} diff --git a/tests/ui/class-method-missing.stderr b/tests/ui/class-method-missing.stderr deleted file mode 100644 index 42bd22e18a19..000000000000 --- a/tests/ui/class-method-missing.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0046]: not all trait items implemented, missing: `eat` - --> $DIR/class-method-missing.rs:9:1 - | -LL | fn eat(&self); - | -------------- `eat` from trait -... -LL | impl Animal for Cat { - | ^^^^^^^^^^^^^^^^^^^ missing `eat` in implementation - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/cleanup-rvalue-for-scope.rs b/tests/ui/cleanup-rvalue-for-scope.rs deleted file mode 100644 index 8f5ee8723fd6..000000000000 --- a/tests/ui/cleanup-rvalue-for-scope.rs +++ /dev/null @@ -1,60 +0,0 @@ -//@ run-pass - -#![allow(non_snake_case)] -#![allow(dead_code)] -#![allow(unused_variables)] -// Test that the lifetime of rvalues in for loops is extended -// to the for loop itself. -static mut FLAGS: u64 = 0; - -struct Box { f: T } -struct AddFlags { bits: u64 } - -fn AddFlags(bits: u64) -> AddFlags { - AddFlags { bits: bits } -} - -fn arg(exp: u64, _x: &AddFlags) { - check_flags(exp); -} - -fn pass(v: T) -> T { - v -} - -fn check_flags(exp: u64) { - unsafe { - let x = FLAGS; - FLAGS = 0; - println!("flags {}, expected {}", x, exp); - assert_eq!(x, exp); - } -} - -impl AddFlags { - fn check_flags(&self, exp: u64) -> &AddFlags { - check_flags(exp); - self - } - - fn bits(&self) -> u64 { - self.bits - } -} - -impl Drop for AddFlags { - fn drop(&mut self) { - unsafe { - FLAGS = FLAGS + self.bits; - } - } -} - -pub fn main() { - // The array containing [AddFlags] should not be dropped until - // after the for loop: - for x in &[AddFlags(1)] { - check_flags(0); - } - check_flags(1); -} diff --git a/tests/ui/drop/for-expr-temporary-drop-scope.rs b/tests/ui/drop/for-expr-temporary-drop-scope.rs new file mode 100644 index 000000000000..c6f80842ee63 --- /dev/null +++ b/tests/ui/drop/for-expr-temporary-drop-scope.rs @@ -0,0 +1,33 @@ +//! Check that temporaries in the for into-iterable expr are not dropped +//! until the end of the for expr. + +//@ run-pass + +static mut FLAGS: u64 = 0; + +struct AddFlags { + bits: u64, +} + +impl Drop for AddFlags { + fn drop(&mut self) { + unsafe { + FLAGS += self.bits; + } + } +} + +fn check_flags(expected: u64) { + unsafe { + let actual = FLAGS; + FLAGS = 0; + assert_eq!(actual, expected, "flags {}, expected {}", actual, expected); + } +} + +fn main() { + for _ in &[AddFlags { bits: 1 }] { + check_flags(0); + } + check_flags(1); +} diff --git a/tests/ui/cenum_impl_drop_cast.rs b/tests/ui/enum/enum-drop-cast-error.rs similarity index 61% rename from tests/ui/cenum_impl_drop_cast.rs rename to tests/ui/enum/enum-drop-cast-error.rs index f681434dd86a..36101573624d 100644 --- a/tests/ui/cenum_impl_drop_cast.rs +++ b/tests/ui/enum/enum-drop-cast-error.rs @@ -1,3 +1,7 @@ +//! Check that trying to cast an enum with a Drop impl to an integer is rejected. +//! +//! Issue: + enum E { A = 0, } diff --git a/tests/ui/cenum_impl_drop_cast.stderr b/tests/ui/enum/enum-drop-cast-error.stderr similarity index 81% rename from tests/ui/cenum_impl_drop_cast.stderr rename to tests/ui/enum/enum-drop-cast-error.stderr index 35c69f4b4b78..b58abbd39d38 100644 --- a/tests/ui/cenum_impl_drop_cast.stderr +++ b/tests/ui/enum/enum-drop-cast-error.stderr @@ -1,5 +1,5 @@ error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:13:13 + --> $DIR/enum-drop-cast-error.rs:17:13 | LL | let i = e as u32; | ^^^^^^^^ diff --git a/tests/ui/privacy/trait-object-method-error.rs b/tests/ui/privacy/trait-object-method-error.rs new file mode 100644 index 000000000000..f0214dc63613 --- /dev/null +++ b/tests/ui/privacy/trait-object-method-error.rs @@ -0,0 +1,20 @@ +//! Trait objects only allow access to methods defined in the trait. + +trait MyTrait { + fn trait_method(&mut self); +} + +struct ImplType; + +impl MyTrait for ImplType { + fn trait_method(&mut self) {} +} + +impl ImplType { + fn struct_impl_method(&mut self) {} +} + +fn main() { + let obj: Box = Box::new(ImplType); + obj.struct_impl_method(); //~ ERROR no method named `struct_impl_method` found +} diff --git a/tests/ui/privacy/trait-object-method-error.stderr b/tests/ui/privacy/trait-object-method-error.stderr new file mode 100644 index 000000000000..40dde8fc47e1 --- /dev/null +++ b/tests/ui/privacy/trait-object-method-error.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `struct_impl_method` found for struct `Box` in the current scope + --> $DIR/trait-object-method-error.rs:19:9 + | +LL | obj.struct_impl_method(); + | ^^^^^^^^^^^^^^^^^^ + | +help: there is a method `trait_method` with a similar name + | +LL - obj.struct_impl_method(); +LL + obj.trait_method(); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/traits/trait-impl-missing-method.rs b/tests/ui/traits/trait-impl-missing-method.rs new file mode 100644 index 000000000000..1f03a332c4a2 --- /dev/null +++ b/tests/ui/traits/trait-impl-missing-method.rs @@ -0,0 +1,13 @@ +//! Trait impls must define all required methods. + +trait MyTrait { + fn trait_method(&self); +} + +struct ImplType; + +impl MyTrait for ImplType {} //~ ERROR not all trait items implemented, missing: `trait_method` + +fn main() { + let _ = ImplType; +} diff --git a/tests/ui/traits/trait-impl-missing-method.stderr b/tests/ui/traits/trait-impl-missing-method.stderr new file mode 100644 index 000000000000..ae11c3665eed --- /dev/null +++ b/tests/ui/traits/trait-impl-missing-method.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `trait_method` + --> $DIR/trait-impl-missing-method.rs:9:1 + | +LL | fn trait_method(&self); + | ----------------------- `trait_method` from trait +... +LL | impl MyTrait for ImplType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `trait_method` in implementation + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. From 9a50cb4a0caa07b7b751afb70464066e4d6985d3 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Fri, 28 Mar 2025 16:12:07 -0400 Subject: [PATCH 47/59] Introduce the `mismatched_lifetime_syntaxes` lint --- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_lint/messages.ftl | 22 + compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lifetime_syntax.rs | 503 ++++++++++++++++++ compiler/rustc_lint/src/lints.rs | 125 +++++ src/tools/miri/tests/pass/fat_ptr.rs | 4 +- .../lifetimes/mismatched-lifetime-syntaxes.rs | 318 +++++++++++ .../mismatched-lifetime-syntaxes.stderr | 473 ++++++++++++++++ 8 files changed, 1447 insertions(+), 3 deletions(-) create mode 100644 compiler/rustc_lint/src/lifetime_syntax.rs create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 974c24ef0f87..2b197c716cd5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -206,7 +206,7 @@ impl ParamName { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub enum LifetimeKind { /// User-given names or fresh (synthetic) names. Param(LocalDefId), diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 17485a838f31..ac9c772c4278 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -518,6 +518,28 @@ lint_metavariable_still_repeating = variable `{$name}` is still repeating at thi lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator +lint_mismatched_lifetime_syntaxes = + lifetime flowing from input to output with different syntax can be confusing + .label_mismatched_lifetime_syntaxes_inputs = + {$n_inputs -> + [one] this lifetime flows + *[other] these lifetimes flow + } to the output + .label_mismatched_lifetime_syntaxes_outputs = + the {$n_outputs -> + [one] lifetime gets + *[other] lifetimes get + } resolved as `{$lifetime_name}` + +lint_mismatched_lifetime_syntaxes_suggestion_explicit = + one option is to consistently use `{$lifetime_name}` + +lint_mismatched_lifetime_syntaxes_suggestion_implicit = + one option is to consistently remove the lifetime + +lint_mismatched_lifetime_syntaxes_suggestion_mixed = + one option is to remove the lifetime for references and use the anonymous lifetime for paths + lint_missing_fragment_specifier = missing fragment specifier lint_missing_unsafe_on_extern = extern blocks should be unsafe diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0a52e42e4428..0439befc6ba9 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -55,6 +55,7 @@ mod invalid_from_utf8; mod late; mod let_underscore; mod levels; +mod lifetime_syntax; mod lints; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; @@ -96,6 +97,7 @@ use impl_trait_overcaptures::ImplTraitOvercaptures; use internal::*; use invalid_from_utf8::*; use let_underscore::*; +use lifetime_syntax::*; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -246,6 +248,7 @@ late_lint_methods!( StaticMutRefs: StaticMutRefs, UnqualifiedLocalImports: UnqualifiedLocalImports, CheckTransmutes: CheckTransmutes, + LifetimeSyntax: LifetimeSyntax, ] ] ); diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs new file mode 100644 index 000000000000..f895cdb2a544 --- /dev/null +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -0,0 +1,503 @@ +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, LifetimeSource}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; +use tracing::instrument; + +use crate::{LateContext, LateLintPass, LintContext, lints}; + +declare_lint! { + /// The `mismatched_lifetime_syntaxes` lint detects when the same + /// lifetime is referred to by different syntaxes between function + /// arguments and return values. + /// + /// The three kinds of syntaxes are: + /// + /// 1. Named lifetimes. These are references (`&'a str`) or paths + /// (`Person<'a>`) that use a lifetime with a name, such as + /// `'static` or `'a`. + /// + /// 2. Elided lifetimes. These are references with no explicit + /// lifetime (`&str`), references using the anonymous lifetime + /// (`&'_ str`), and paths using the anonymous lifetime + /// (`Person<'_>`). + /// + /// 3. Hidden lifetimes. These are paths that do not contain any + /// visual indication that it contains a lifetime (`Person`). + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(mismatched_lifetime_syntaxes)] + /// + /// pub fn mixing_named_with_elided(v: &'static u8) -> &u8 { + /// v + /// } + /// + /// struct Person<'a> { + /// name: &'a str, + /// } + /// + /// pub fn mixing_hidden_with_elided(v: Person) -> Person<'_> { + /// v + /// } + /// + /// struct Foo; + /// + /// impl Foo { + /// // Lifetime elision results in the output lifetime becoming + /// // `'static`, which is not what was intended. + /// pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { + /// unsafe { &mut *(x as *mut _) } + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Lifetime elision is useful because it frees you from having to + /// give each lifetime its own name and show the relation of input + /// and output lifetimes for common cases. However, a lifetime + /// that uses inconsistent syntax between related arguments and + /// return values is more confusing. + /// + /// In certain `unsafe` code, lifetime elision combined with + /// inconsistent lifetime syntax may result in unsound code. + pub MISMATCHED_LIFETIME_SYNTAXES, + Allow, + "detects when a lifetime uses different syntax between arguments and return values" +} + +declare_lint_pass!(LifetimeSyntax => [MISMATCHED_LIFETIME_SYNTAXES]); + +impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax { + #[instrument(skip_all)] + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: hir::intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + _: &'tcx hir::Body<'tcx>, + _: rustc_span::Span, + _: rustc_span::def_id::LocalDefId, + ) { + let mut input_map = Default::default(); + let mut output_map = Default::default(); + + for input in fd.inputs { + LifetimeInfoCollector::collect(input, &mut input_map); + } + + if let hir::FnRetTy::Return(output) = fd.output { + LifetimeInfoCollector::collect(output, &mut output_map); + } + + report_mismatches(cx, &input_map, &output_map); + } +} + +#[instrument(skip_all)] +fn report_mismatches<'tcx>( + cx: &LateContext<'tcx>, + inputs: &LifetimeInfoMap<'tcx>, + outputs: &LifetimeInfoMap<'tcx>, +) { + for (resolved_lifetime, output_info) in outputs { + if let Some(input_info) = inputs.get(resolved_lifetime) { + if !lifetimes_use_matched_syntax(input_info, output_info) { + emit_mismatch_diagnostic(cx, input_info, output_info); + } + } + } +} + +fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool { + // Categorize lifetimes into source/syntax buckets. + let mut n_hidden = 0; + let mut n_elided = 0; + let mut n_named = 0; + + for info in input_info.iter().chain(output_info) { + use LifetimeSource::*; + use hir::LifetimeSyntax::*; + + let syntax_source = (info.lifetime.syntax, info.lifetime.source); + + match syntax_source { + // Ignore any other kind of lifetime. + (_, Other) => continue, + + // E.g. `&T`. + (Implicit, Reference | OutlivesBound | PreciseCapturing) | + // E.g. `&'_ T`. + (ExplicitAnonymous, Reference | OutlivesBound | PreciseCapturing) | + // E.g. `ContainsLifetime<'_>`. + (ExplicitAnonymous, Path { .. }) => n_elided += 1, + + // E.g. `ContainsLifetime`. + (Implicit, Path { .. }) => n_hidden += 1, + + // E.g. `&'a T`. + (ExplicitBound, Reference | OutlivesBound | PreciseCapturing) | + // E.g. `ContainsLifetime<'a>`. + (ExplicitBound, Path { .. }) => n_named += 1, + }; + } + + let syntax_counts = (n_hidden, n_elided, n_named); + tracing::debug!(?syntax_counts); + + matches!(syntax_counts, (_, 0, 0) | (0, _, 0) | (0, 0, _)) +} + +fn emit_mismatch_diagnostic<'tcx>( + cx: &LateContext<'tcx>, + input_info: &[Info<'_>], + output_info: &[Info<'_>], +) { + // There can only ever be zero or one bound lifetime + // for a given lifetime resolution. + let mut bound_lifetime = None; + + // We offer the following kinds of suggestions (when appropriate + // such that the suggestion wouldn't violate the lint): + // + // 1. Every lifetime becomes named, when there is already a + // user-provided name. + // + // 2. A "mixed" signature, where references become implicit + // and paths become explicitly anonymous. + // + // 3. Every lifetime becomes implicit. + // + // 4. Every lifetime becomes explicitly anonymous. + // + // Number 2 is arguably the most common pattern and the one we + // should push strongest. Number 3 is likely the next most common, + // followed by number 1. Coming in at a distant last would be + // number 4. + // + // Beyond these, there are variants of acceptable signatures that + // we won't suggest because they are very low-value. For example, + // we will never suggest `fn(&T1, &'_ T2) -> &T3` even though that + // would pass the lint. + // + // The following collections are the lifetime instances that we + // suggest changing to a given alternate style. + + // 1. Convert all to named. + let mut suggest_change_to_explicit_bound = Vec::new(); + + // 2. Convert to mixed. We track each kind of change separately. + let mut suggest_change_to_mixed_implicit = Vec::new(); + let mut suggest_change_to_mixed_explicit_anonymous = Vec::new(); + + // 3. Convert all to implicit. + let mut suggest_change_to_implicit = Vec::new(); + + // 4. Convert all to explicit anonymous. + let mut suggest_change_to_explicit_anonymous = Vec::new(); + + // Some styles prevent using implicit syntax at all. + let mut allow_suggesting_implicit = true; + + // It only makes sense to suggest mixed if we have both sources. + let mut saw_a_reference = false; + let mut saw_a_path = false; + + for info in input_info.iter().chain(output_info) { + use LifetimeSource::*; + use hir::LifetimeSyntax::*; + + let syntax_source = (info.lifetime.syntax, info.lifetime.source); + + if let (_, Other) = syntax_source { + // Ignore any other kind of lifetime. + continue; + } + + if let (ExplicitBound, _) = syntax_source { + bound_lifetime = Some(info); + } + + match syntax_source { + // E.g. `&T`. + (Implicit, Reference) => { + suggest_change_to_explicit_anonymous.push(info); + suggest_change_to_explicit_bound.push(info); + } + + // E.g. `&'_ T`. + (ExplicitAnonymous, Reference) => { + suggest_change_to_implicit.push(info); + suggest_change_to_mixed_implicit.push(info); + suggest_change_to_explicit_bound.push(info); + } + + // E.g. `ContainsLifetime`. + (Implicit, Path { .. }) => { + suggest_change_to_mixed_explicit_anonymous.push(info); + suggest_change_to_explicit_anonymous.push(info); + suggest_change_to_explicit_bound.push(info); + } + + // E.g. `ContainsLifetime<'_>`. + (ExplicitAnonymous, Path { .. }) => { + suggest_change_to_explicit_bound.push(info); + } + + // E.g. `&'a T`. + (ExplicitBound, Reference) => { + suggest_change_to_implicit.push(info); + suggest_change_to_mixed_implicit.push(info); + suggest_change_to_explicit_anonymous.push(info); + } + + // E.g. `ContainsLifetime<'a>`. + (ExplicitBound, Path { .. }) => { + suggest_change_to_mixed_explicit_anonymous.push(info); + suggest_change_to_explicit_anonymous.push(info); + } + + (Implicit, OutlivesBound | PreciseCapturing) => { + panic!("This syntax / source combination is not possible"); + } + + // E.g. `+ '_`, `+ use<'_>`. + (ExplicitAnonymous, OutlivesBound | PreciseCapturing) => { + suggest_change_to_explicit_bound.push(info); + } + + // E.g. `+ 'a`, `+ use<'a>`. + (ExplicitBound, OutlivesBound | PreciseCapturing) => { + suggest_change_to_mixed_explicit_anonymous.push(info); + suggest_change_to_explicit_anonymous.push(info); + } + + (_, Other) => { + panic!("This syntax / source combination has already been skipped"); + } + } + + if matches!(syntax_source, (_, Path { .. } | OutlivesBound | PreciseCapturing)) { + allow_suggesting_implicit = false; + } + + match syntax_source { + (_, Reference) => saw_a_reference = true, + (_, Path { .. }) => saw_a_path = true, + _ => {} + } + } + + let make_implicit_suggestions = + |infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::>(); + + let inputs = input_info.iter().map(|info| info.reporting_span()).collect(); + let outputs = output_info.iter().map(|info| info.reporting_span()).collect(); + + let explicit_bound_suggestion = bound_lifetime.map(|info| { + build_mismatch_suggestion(info.lifetime_name(), &suggest_change_to_explicit_bound) + }); + + let is_bound_static = bound_lifetime.is_some_and(|info| info.is_static()); + + tracing::debug!(?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static); + + let should_suggest_mixed = + // Do we have a mixed case? + (saw_a_reference && saw_a_path) && + // Is there anything to change? + (!suggest_change_to_mixed_implicit.is_empty() || + !suggest_change_to_mixed_explicit_anonymous.is_empty()) && + // If we have `'static`, we don't want to remove it. + !is_bound_static; + + let mixed_suggestion = should_suggest_mixed.then(|| { + let implicit_suggestions = make_implicit_suggestions(&suggest_change_to_mixed_implicit); + + let explicit_anonymous_suggestions = suggest_change_to_mixed_explicit_anonymous + .iter() + .map(|info| info.suggestion("'_")) + .collect(); + + lints::MismatchedLifetimeSyntaxesSuggestion::Mixed { + implicit_suggestions, + explicit_anonymous_suggestions, + tool_only: false, + } + }); + + tracing::debug!( + ?suggest_change_to_mixed_implicit, + ?suggest_change_to_mixed_explicit_anonymous, + ?mixed_suggestion, + ); + + let should_suggest_implicit = + // Is there anything to change? + !suggest_change_to_implicit.is_empty() && + // We never want to hide the lifetime in a path (or similar). + allow_suggesting_implicit && + // If we have `'static`, we don't want to remove it. + !is_bound_static; + + let implicit_suggestion = should_suggest_implicit.then(|| { + let suggestions = make_implicit_suggestions(&suggest_change_to_implicit); + + lints::MismatchedLifetimeSyntaxesSuggestion::Implicit { suggestions, tool_only: false } + }); + + tracing::debug!( + ?should_suggest_implicit, + ?suggest_change_to_implicit, + allow_suggesting_implicit, + ?implicit_suggestion, + ); + + let should_suggest_explicit_anonymous = + // Is there anything to change? + !suggest_change_to_explicit_anonymous.is_empty() && + // If we have `'static`, we don't want to remove it. + !is_bound_static; + + let explicit_anonymous_suggestion = should_suggest_explicit_anonymous + .then(|| build_mismatch_suggestion("'_", &suggest_change_to_explicit_anonymous)); + + tracing::debug!( + ?should_suggest_explicit_anonymous, + ?suggest_change_to_explicit_anonymous, + ?explicit_anonymous_suggestion, + ); + + let lifetime_name = bound_lifetime.map(|info| info.lifetime_name()).unwrap_or("'_").to_owned(); + + // We can produce a number of suggestions which may overwhelm + // the user. Instead, we order the suggestions based on Rust + // idioms. The "best" choice is shown to the user and the + // remaining choices are shown to tools only. + let mut suggestions = Vec::new(); + suggestions.extend(explicit_bound_suggestion); + suggestions.extend(mixed_suggestion); + suggestions.extend(implicit_suggestion); + suggestions.extend(explicit_anonymous_suggestion); + + cx.emit_span_lint( + MISMATCHED_LIFETIME_SYNTAXES, + Vec::clone(&inputs), + lints::MismatchedLifetimeSyntaxes { lifetime_name, inputs, outputs, suggestions }, + ); +} + +fn build_mismatch_suggestion( + lifetime_name: &str, + infos: &[&Info<'_>], +) -> lints::MismatchedLifetimeSyntaxesSuggestion { + let lifetime_name = lifetime_name.to_owned(); + + let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect(); + + lints::MismatchedLifetimeSyntaxesSuggestion::Explicit { + lifetime_name, + suggestions, + tool_only: false, + } +} + +#[derive(Debug)] +struct Info<'tcx> { + type_span: Span, + referenced_type_span: Option, + lifetime: &'tcx hir::Lifetime, +} + +impl<'tcx> Info<'tcx> { + fn lifetime_name(&self) -> &str { + self.lifetime.ident.as_str() + } + + fn is_static(&self) -> bool { + self.lifetime.is_static() + } + + /// When reporting a lifetime that is implicit, we expand the span + /// to include the type. Otherwise we end up pointing at nothing, + /// which is a bit confusing. + fn reporting_span(&self) -> Span { + if self.lifetime.is_implicit() { self.type_span } else { self.lifetime.ident.span } + } + + /// When removing an explicit lifetime from a reference, + /// we want to remove the whitespace after the lifetime. + /// + /// ```rust + /// fn x(a: &'_ u8) {} + /// ``` + /// + /// Should become: + /// + /// ```rust + /// fn x(a: &u8) {} + /// ``` + // FIXME: Ideally, we'd also remove the lifetime declaration. + fn removing_span(&self) -> Span { + let mut span = self.suggestion("'dummy").0; + + if let Some(referenced_type_span) = self.referenced_type_span { + span = span.until(referenced_type_span); + } + + span + } + + fn suggestion(&self, lifetime_name: &str) -> (Span, String) { + self.lifetime.suggestion(lifetime_name) + } +} + +type LifetimeInfoMap<'tcx> = FxIndexMap<&'tcx hir::LifetimeKind, Vec>>; + +struct LifetimeInfoCollector<'a, 'tcx> { + type_span: Span, + referenced_type_span: Option, + map: &'a mut LifetimeInfoMap<'tcx>, +} + +impl<'a, 'tcx> LifetimeInfoCollector<'a, 'tcx> { + fn collect(ty: &'tcx hir::Ty<'tcx>, map: &'a mut LifetimeInfoMap<'tcx>) { + let mut this = Self { type_span: ty.span, referenced_type_span: None, map }; + + intravisit::walk_unambig_ty(&mut this, ty); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> { + #[instrument(skip(self))] + fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) { + let type_span = self.type_span; + let referenced_type_span = self.referenced_type_span; + + let info = Info { type_span, referenced_type_span, lifetime }; + + self.map.entry(&lifetime.kind).or_default().push(info); + } + + #[instrument(skip(self))] + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) -> Self::Result { + let old_type_span = self.type_span; + let old_referenced_type_span = self.referenced_type_span; + + self.type_span = ty.span; + if let hir::TyKind::Ref(_, ty) = ty.kind { + self.referenced_type_span = Some(ty.ty.span); + } + + intravisit::walk_ty(self, ty); + + self.type_span = old_type_span; + self.referenced_type_span = old_referenced_type_span; + } +} diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 10d0e2c93a83..53fbe158885e 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3241,3 +3241,128 @@ pub(crate) struct ReservedMultihash { #[suggestion(code = " ", applicability = "machine-applicable")] pub suggestion: Span, } + +#[derive(Debug)] +pub(crate) struct MismatchedLifetimeSyntaxes { + pub lifetime_name: String, + pub inputs: Vec, + pub outputs: Vec, + + pub suggestions: Vec, +} + +impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSyntaxes { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { + diag.primary_message(fluent::lint_mismatched_lifetime_syntaxes); + + diag.arg("lifetime_name", self.lifetime_name); + + diag.arg("n_inputs", self.inputs.len()); + for input in self.inputs { + let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_inputs); + diag.span_label(input, a); + } + + diag.arg("n_outputs", self.outputs.len()); + for output in self.outputs { + let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_outputs); + diag.span_label(output, a); + } + + let mut suggestions = self.suggestions.into_iter(); + if let Some(s) = suggestions.next() { + diag.subdiagnostic(s); + + for mut s in suggestions { + s.make_tool_only(); + diag.subdiagnostic(s); + } + } + } +} + +#[derive(Debug)] +pub(crate) enum MismatchedLifetimeSyntaxesSuggestion { + Implicit { + suggestions: Vec, + tool_only: bool, + }, + + Mixed { + implicit_suggestions: Vec, + explicit_anonymous_suggestions: Vec<(Span, String)>, + tool_only: bool, + }, + + Explicit { + lifetime_name: String, + suggestions: Vec<(Span, String)>, + tool_only: bool, + }, +} + +impl MismatchedLifetimeSyntaxesSuggestion { + fn make_tool_only(&mut self) { + use MismatchedLifetimeSyntaxesSuggestion::*; + + let tool_only = match self { + Implicit { tool_only, .. } | Mixed { tool_only, .. } | Explicit { tool_only, .. } => { + tool_only + } + }; + + *tool_only = true; + } +} + +impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + use MismatchedLifetimeSyntaxesSuggestion::*; + + let style = |tool_only| { + if tool_only { SuggestionStyle::CompletelyHidden } else { SuggestionStyle::ShowAlways } + }; + + match self { + Implicit { suggestions, tool_only } => { + let suggestions = suggestions.into_iter().map(|s| (s, String::new())).collect(); + diag.multipart_suggestion_with_style( + fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit, + suggestions, + Applicability::MachineApplicable, + style(tool_only), + ); + } + + Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => { + let implicit_suggestions = + implicit_suggestions.into_iter().map(|s| (s, String::new())); + + let suggestions = + implicit_suggestions.chain(explicit_anonymous_suggestions).collect(); + + diag.multipart_suggestion_with_style( + fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed, + suggestions, + Applicability::MachineApplicable, + style(tool_only), + ); + } + + Explicit { lifetime_name, suggestions, tool_only } => { + diag.arg("lifetime_name", lifetime_name); + + let msg = diag.eagerly_translate( + fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit, + ); + + diag.multipart_suggestion_with_style( + msg, + suggestions, + Applicability::MachineApplicable, + style(tool_only), + ); + } + } + } +} diff --git a/src/tools/miri/tests/pass/fat_ptr.rs b/src/tools/miri/tests/pass/fat_ptr.rs index c5603d2cf804..13608b8f898e 100644 --- a/src/tools/miri/tests/pass/fat_ptr.rs +++ b/src/tools/miri/tests/pass/fat_ptr.rs @@ -19,11 +19,11 @@ fn fat_ptr_via_local(a: &[u8]) -> &[u8] { x } -fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] { +fn fat_ptr_from_struct(s: FatPtrContainer<'_>) -> &[u8] { s.ptr } -fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer { +fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer<'_> { FatPtrContainer { ptr: a } } diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs new file mode 100644 index 000000000000..ffeaab36bf39 --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs @@ -0,0 +1,318 @@ +#![deny(mismatched_lifetime_syntaxes)] + +// `elided_named_lifetimes` overlaps with `lifetime_style_mismatch`, ignore it for now +#![allow(elided_named_lifetimes)] + +#[derive(Copy, Clone)] +struct ContainsLifetime<'a>(&'a u8); + +struct S(u8); + +fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v +} + +fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v +} + +// --- + +fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime) -> ContainsLifetime<'_> { + //~^ ERROR lifetime flowing from input to output with different syntax + v +} + +fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + v +} + +fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + v +} + +fn explicit_bound_path_to_explicit_anonymous_path<'a>( + v: ContainsLifetime<'a>, + //~^ ERROR lifetime flowing from input to output with different syntax +) -> ContainsLifetime<'_> { + v +} + +// --- + +fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(v) +} + +fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(v) +} + +fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(v) +} + +fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(v) +} + +// --- + +fn implicit_path_to_implicit_ref(v: ContainsLifetime) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v.0 +} + +fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v.0 +} + +fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v.0 +} + +fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v.0 +} + +impl S { + fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + &self.0 + } + + fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + &self.0 + } + + // --- + + fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(&self.0) + } + + fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(&self.0) + } + + fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(&self.0) + } +} + +// If a function uses the `'static` lifetime, we should not suggest +// replacing it with an explicitly anonymous or implicit +// lifetime. Only suggest using `'static` everywhere. +mod static_suggestions { + #[derive(Copy, Clone)] + struct ContainsLifetime<'a>(&'a u8); + + struct S(u8); + + fn static_ref_to_implicit_ref(v: &'static u8) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v + } + + fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + v + } + + fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(v) + } + + fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(v) + } + + impl S { + fn static_ref_to_implicit_ref(&'static self) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + &self.0 + } + + fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + &self.0 + } + + fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(&self.0) + } + + fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> { + //~^ ERROR lifetime flowing from input to output with different syntax + ContainsLifetime(&self.0) + } + } +} + +/// `impl Trait` uses lifetimes in some additional ways. +mod impl_trait { + #[derive(Copy, Clone)] + struct ContainsLifetime<'a>(&'a u8); + + fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ { + //~^ ERROR lifetime flowing from input to output with different syntax + move || _ = v + } + + fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> { + //~^ ERROR lifetime flowing from input to output with different syntax + move || _ = v + } + + fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ { + //~^ ERROR lifetime flowing from input to output with different syntax + move || _ = v + } + + fn explicit_bound_path_to_impl_trait_precise_capture<'a>( + v: ContainsLifetime<'a>, + //~^ ERROR lifetime flowing from input to output with different syntax + ) -> impl FnOnce() + use<'_> { + move || _ = v + } +} + +/// `dyn Trait` uses lifetimes in some additional ways. +mod dyn_trait { + use std::iter; + + #[derive(Copy, Clone)] + struct ContainsLifetime<'a>(&'a u8); + + fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box + '_> { + //~^ ERROR lifetime flowing from input to output with different syntax + Box::new(iter::once(v)) + } + + fn explicit_bound_path_to_dyn_trait_bound<'a>( + v: ContainsLifetime<'a>, + //~^ ERROR lifetime flowing from input to output with different syntax + ) -> Box + '_> { + Box::new(iter::once(v)) + } +} + +/// These tests serve to exercise edge cases of the lint formatting +mod diagnostic_output { + fn multiple_outputs<'a>(v: &'a u8) -> (&u8, &u8) { + //~^ ERROR lifetime flowing from input to output with different syntax + (v, v) + } +} + +/// These usages are expected to **not** trigger the lint +mod acceptable_uses { + #[derive(Copy, Clone)] + struct ContainsLifetime<'a>(&'a u8); + + struct S(u8); + + fn implicit_ref_to_implicit_ref(v: &u8) -> &u8 { + v + } + + fn explicit_anonymous_ref_to_explicit_anonymous_ref(v: &'_ u8) -> &'_ u8 { + v + } + + fn explicit_bound_ref_to_explicit_bound_ref<'a>(v: &'a u8) -> &'a u8 { + v + } + + fn implicit_path_to_implicit_path(v: ContainsLifetime) -> ContainsLifetime { + v + } + + fn explicit_anonymous_path_to_explicit_anonymous_path( + v: ContainsLifetime<'_>, + ) -> ContainsLifetime<'_> { + v + } + + fn explicit_bound_path_to_explicit_bound_path<'a>( + v: ContainsLifetime<'a>, + ) -> ContainsLifetime<'a> { + v + } + + fn explicit_anonymous_ref_to_explicit_anonymous_path(v: &'_ u8) -> ContainsLifetime<'_> { + ContainsLifetime(v) + } + + fn explicit_bound_ref_to_explicit_bound_path<'a>(v: &'a u8) -> ContainsLifetime<'a> { + ContainsLifetime(v) + } + + fn explicit_anonymous_path_to_explicit_anonymous_ref(v: ContainsLifetime<'_>) -> &'_ u8 { + v.0 + } + + fn explicit_bound_path_to_explicit_bound_ref<'a>(v: ContainsLifetime<'a>) -> &'a u8 { + v.0 + } + + // These may be surprising, but ampersands count as enough of a + // visual indicator that a reference exists that we treat + // references with implicit lifetimes the same as if they were + // explicitly anonymous. + fn implicit_ref_to_explicit_anonymous_ref(v: &u8) -> &'_ u8 { + v + } + + fn explicit_anonymous_ref_to_implicit_ref(v: &'_ u8) -> &u8 { + v + } + + fn implicit_ref_to_explicit_anonymous_path(v: &u8) -> ContainsLifetime<'_> { + ContainsLifetime(v) + } + + fn explicit_anonymous_path_to_implicit_ref(v: ContainsLifetime<'_>) -> &u8 { + v.0 + } + + impl S { + fn method_implicit_ref_to_explicit_anonymous_ref(&self) -> &'_ u8 { + &self.0 + } + + fn method_explicit_anonymous_ref_to_implicit_ref(&'_ self) -> &u8 { + &self.0 + } + + fn method_implicit_ref_to_explicit_anonymous_path(&self) -> ContainsLifetime<'_> { + ContainsLifetime(&self.0) + } + } + + // `dyn Trait` has an "embedded" lifetime that we should **not** + // lint about. + fn dyn_trait_does_not_have_a_lifetime_generic(v: &u8) -> &dyn core::fmt::Debug { + v + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr new file mode 100644 index 000000000000..57af0ac93cfa --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr @@ -0,0 +1,473 @@ +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:11:47 + | +LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &u8 { + | ^^ --- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +note: the lint level is defined here + --> $DIR/mismatched-lifetime-syntaxes.rs:1:9 + | +LL | #![deny(mismatched_lifetime_syntaxes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: one option is to consistently use `'a` + | +LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &'a u8 { + | ++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:16:57 + | +LL | fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 { +LL + fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'a u8 { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:23:48 + | +LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime) -> ContainsLifetime<'_> { + | ^^^^^^^^^^^^^^^^ -- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'_` + | +LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime<'_>) -> ContainsLifetime<'_> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:28:65 + | +LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime { + | ^^ ---------------- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'_` + | +LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime<'_> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:33:65 + | +LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime { + | ^^ ---------------- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime<'a> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:39:25 + | +LL | v: ContainsLifetime<'a>, + | ^^ this lifetime flows to the output +LL | +LL | ) -> ContainsLifetime<'_> { + | -- the lifetime gets resolved as `'a` + | +help: one option is to consistently use `'a` + | +LL - ) -> ContainsLifetime<'_> { +LL + ) -> ContainsLifetime<'a> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:47:37 + | +LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime { + | ^^^ ---------------- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to remove the lifetime for references and use the anonymous lifetime for paths + | +LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:52:48 + | +LL | fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime { + | ^^ ---------------- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to remove the lifetime for references and use the anonymous lifetime for paths + | +LL - fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime { +LL + fn explicit_anonymous_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:57:48 + | +LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime { + | ^^ ---------------- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime<'a> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:62:58 + | +LL | fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> { +LL + fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'a> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:69:37 + | +LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime) -> &u8 { + | ^^^^^^^^^^^^^^^^ --- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to remove the lifetime for references and use the anonymous lifetime for paths + | +LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime<'_>) -> &u8 { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:74:47 + | +LL | fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 { + | ^^^^^^^^^^^^^^^^ -- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to remove the lifetime for references and use the anonymous lifetime for paths + | +LL - fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 { +LL + fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime<'_>) -> &u8 { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:79:64 + | +LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &u8 { + | ^^ --- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &'a u8 { + | ++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:84:74 + | +LL | fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 { +LL + fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'a u8 { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:90:55 + | +LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &u8 { + | ^^ --- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &'a u8 { + | ++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:95:65 + | +LL | fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 { +LL + fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'a u8 { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:102:56 + | +LL | fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime { + | ^^ ---------------- the lifetime gets resolved as `'_` + | | + | this lifetime flows to the output + | +help: one option is to remove the lifetime for references and use the anonymous lifetime for paths + | +LL - fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime { +LL + fn method_explicit_anonymous_ref_to_implicit_path(&self) -> ContainsLifetime<'_> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:107:56 + | +LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime { + | ^^ ---------------- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime<'a> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:112:66 + | +LL | fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> { +LL + fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'a> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:127:39 + | +LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &u8 { + | ^^^^^^^ --- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &'static u8 { + | +++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:132:49 + | +LL | fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 { + | ^^^^^^^ -- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL - fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 { +LL + fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'static u8 { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:137:40 + | +LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime { + | ^^^^^^^ ---------------- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime<'static> { + | +++++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:142:50 + | +LL | fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> { + | ^^^^^^^ -- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL - fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> { +LL + fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'static> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:148:40 + | +LL | fn static_ref_to_implicit_ref(&'static self) -> &u8 { + | ^^^^^^^ --- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL | fn static_ref_to_implicit_ref(&'static self) -> &'static u8 { + | +++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:153:50 + | +LL | fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 { + | ^^^^^^^ -- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL - fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 { +LL + fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'static u8 { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:158:41 + | +LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime { + | ^^^^^^^ ---------------- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime<'static> { + | +++++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:163:51 + | +LL | fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> { + | ^^^^^^^ -- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL - fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> { +LL + fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'static> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:175:55 + | +LL | fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ { +LL + fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + 'a { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:180:65 + | +LL | fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> { +LL + fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'a> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:185:72 + | +LL | fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ { +LL + fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + 'a { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:191:29 + | +LL | v: ContainsLifetime<'a>, + | ^^ this lifetime flows to the output +LL | +LL | ) -> impl FnOnce() + use<'_> { + | -- the lifetime gets resolved as `'a` + | +help: one option is to consistently use `'a` + | +LL - ) -> impl FnOnce() + use<'_> { +LL + ) -> impl FnOnce() + use<'a> { + | + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:205:54 + | +LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box + '_> { + | ^^ --- -- the lifetimes get resolved as `'a` + | | | + | | the lifetimes get resolved as `'a` + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box + '_> { + | ++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:211:29 + | +LL | v: ContainsLifetime<'a>, + | ^^ this lifetime flows to the output +LL | +LL | ) -> Box + '_> { + | ---------------- -- the lifetimes get resolved as `'a` + | | + | the lifetimes get resolved as `'a` + | +help: one option is to consistently use `'a` + | +LL | ) -> Box> + '_> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/mismatched-lifetime-syntaxes.rs:220:33 + | +LL | fn multiple_outputs<'a>(v: &'a u8) -> (&u8, &u8) { + | ^^ --- --- the lifetimes get resolved as `'a` + | | | + | | the lifetimes get resolved as `'a` + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn multiple_outputs<'a>(v: &'a u8) -> (&'a u8, &'a u8) { + | ++ ++ + +error: aborting due to 34 previous errors + From d35ad948492146f0811d43606db66c65c55980a9 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 17 Mar 2025 12:43:55 -0400 Subject: [PATCH 48/59] Replace `elided_named_lifetimes` with `mismatched_lifetime_syntaxes` --- compiler/rustc_hir/src/def.rs | 7 +- compiler/rustc_lint/messages.ftl | 5 -- compiler/rustc_lint/src/early/diagnostics.rs | 17 +---- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint/src/lifetime_syntax.rs | 2 +- compiler/rustc_lint/src/lints.rs | 56 +-------------- compiler/rustc_lint_defs/src/builtin.rs | 33 --------- compiler/rustc_lint_defs/src/lib.rs | 12 +--- compiler/rustc_resolve/src/late.rs | 61 ++--------------- .../rustc_resolve/src/late/diagnostics.rs | 4 +- .../clippy/tests/ui/explicit_iter_loop.fixed | 4 +- .../clippy/tests/ui/explicit_iter_loop.rs | 4 +- src/tools/clippy/tests/ui/iter_next_loop.rs | 2 +- .../tests/ui/iter_not_returning_iterator.rs | 2 +- src/tools/clippy/tests/ui/methods.rs | 2 +- .../clippy/tests/ui/needless_lifetimes.fixed | 2 +- .../clippy/tests/ui/needless_lifetimes.rs | 2 +- src/tools/clippy/tests/ui/ptr_arg.rs | 2 +- src/tools/clippy/tests/ui/ptr_arg.stderr | 25 ++++--- .../tests/ui/significant_drop_in_scrutinee.rs | 12 ++-- .../tests/ui/trivially_copy_pass_by_ref.fixed | 1 + .../tests/ui/trivially_copy_pass_by_ref.rs | 1 + .../ui/trivially_copy_pass_by_ref.stderr | 36 +++++----- src/tools/clippy/tests/ui/use_self.fixed | 2 +- src/tools/clippy/tests/ui/use_self.rs | 2 +- .../associated-types-impl-redirect.rs | 2 +- ...sociated-types-normalize-in-bounds-ufcs.rs | 4 +- .../associated-types-normalize-in-bounds.rs | 22 ++++-- ...iated-types-where-clause-impl-ambiguity.rs | 2 +- tests/ui/associated-types/cache/elision.rs | 2 +- .../project-defer-unification.rs | 2 +- tests/ui/async-await/issues/issue-63388-1.rs | 2 +- .../async-await/issues/issue-63388-1.stderr | 13 +--- .../borrowck/copy-suggestion-region-vid.fixed | 2 +- .../ui/borrowck/copy-suggestion-region-vid.rs | 2 +- .../type-dependent/issue-71348.full.stderr | 15 ++-- .../type-dependent/issue-71348.min.stderr | 10 +-- .../type-dependent/issue-71348.rs | 2 +- .../drop/drop-order-comparisons.e2021.fixed | 2 +- tests/ui/drop/drop-order-comparisons.rs | 2 +- tests/ui/drop/drop_order.rs | 4 +- tests/ui/drop/drop_order_if_let_rescope.rs | 2 +- tests/ui/drop/issue-2735-2.rs | 2 +- tests/ui/drop/issue-2735-3.rs | 2 +- tests/ui/drop/issue-979.rs | 2 +- tests/ui/drop/tail-expr-drop-order.rs | 4 +- .../ui/generics/export-name-on-generics.fixed | 2 +- tests/ui/generics/export-name-on-generics.rs | 2 +- tests/ui/generics/generic-no-mangle.fixed | 2 +- tests/ui/generics/generic-no-mangle.rs | 2 +- .../normalize-under-binder/issue-62529-1.rs | 2 +- tests/ui/impl-trait/impl-fn-hrtb-bounds.rs | 1 - .../ui/impl-trait/impl-fn-hrtb-bounds.stderr | 12 +--- .../impl-fn-predefined-lifetimes.rs | 1 - .../impl-fn-predefined-lifetimes.stderr | 13 +--- .../rpit-assoc-pair-with-lifetime.rs | 2 +- .../rpit-assoc-pair-with-lifetime.stderr | 12 ++-- tests/ui/issues/issue-2502.rs | 2 +- tests/ui/issues/issue-42552.rs | 2 +- tests/ui/issues/issue-5708.rs | 2 +- ...und-on-trait-object-using-type-alias.fixed | 1 + ...-bound-on-trait-object-using-type-alias.rs | 1 + ...nd-on-trait-object-using-type-alias.stderr | 2 +- ...-return-type-requires-explicit-lifetime.rs | 1 - ...urn-type-requires-explicit-lifetime.stderr | 12 +--- ...-one-existing-name-if-else-using-impl-3.rs | 4 -- ...-existing-name-if-else-using-impl-3.stderr | 15 +--- .../example-from-issue48686.rs | 4 +- .../example-from-issue48686.stderr | 20 ++++++ .../missing-lifetime-kind.rs | 27 ++++++++ .../missing-lifetime-kind.stderr | 60 ++++++++++++++++ .../not-tied-to-crate.rs | 20 ++++++ .../not-tied-to-crate.stderr | 38 +++++++++++ .../static.rs | 10 +-- .../static.stderr | 60 ++++++++++++++++ .../lifetimes/mismatched-lifetime-syntaxes.rs | 3 - .../mismatched-lifetime-syntaxes.stderr | 68 +++++++++---------- .../example-from-issue48686.stderr | 18 ----- .../missing-lifetime-kind.rs | 27 -------- .../missing-lifetime-kind.stderr | 40 ----------- .../not-tied-to-crate.rs | 17 ----- .../not-tied-to-crate.stderr | 34 ---------- .../lint/elided-named-lifetimes/static.stderr | 52 -------------- tests/ui/macros/format-args-temporaries.rs | 2 +- tests/ui/mir/mir_fat_ptr.rs | 4 +- tests/ui/nll/issue-53570.rs | 2 +- tests/ui/nll/issue-54556-niconii.rs | 2 +- .../object-lifetime-default-elision.rs | 2 - .../object-lifetime-default-elision.stderr | 14 +--- tests/ui/regions/init-res-into-things.rs | 2 +- tests/ui/regions/regions-nullary-variant.rs | 2 +- tests/ui/resource-assign-is-not-copy.rs | 2 +- tests/ui/resource-destruct.rs | 2 +- .../temporary-early-drop.rs | 2 +- .../edition-gate-macro.rs | 2 +- .../temporary-early-drop.rs | 2 +- .../elision/ignore-non-reference-lifetimes.rs | 4 +- .../ignore-non-reference-lifetimes.stderr | 27 ++++++-- tests/ui/self/elision/lt-ref-self-async.fixed | 2 +- tests/ui/self/elision/lt-ref-self-async.rs | 2 +- tests/ui/self/self_lifetime-async.rs | 4 +- tests/ui/self/self_lifetime-async.stderr | 29 +++++--- tests/ui/self/self_lifetime.rs | 4 +- tests/ui/self/self_lifetime.stderr | 29 +++++--- .../impl-trait-missing-lifetime-gated.rs | 1 - .../impl-trait-missing-lifetime-gated.stderr | 10 +-- .../missing-lifetimes-in-signature.rs | 1 - .../missing-lifetimes-in-signature.stderr | 10 +-- .../missing_lifetime_bound.rs | 2 +- .../missing_lifetime_bound.stderr | 12 +--- tests/ui/type-id-higher-rank-2.rs | 2 +- tests/ui/underscore-lifetimes.rs | 2 +- 112 files changed, 500 insertions(+), 669 deletions(-) rename tests/ui/{lint/elided-named-lifetimes => lifetimes/mismatched-lifetime-syntaxes-details}/example-from-issue48686.rs (55%) create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.rs create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.rs create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr rename tests/ui/{lint/elided-named-lifetimes => lifetimes/mismatched-lifetime-syntaxes-details}/static.rs (63%) create mode 100644 tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr delete mode 100644 tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr delete mode 100644 tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs delete mode 100644 tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr delete mode 100644 tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs delete mode 100644 tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr delete mode 100644 tests/ui/lint/elided-named-lifetimes/static.stderr diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 98ec1ccd6bad..459fe5935e06 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -852,12 +852,7 @@ pub enum LifetimeRes { /// late resolution. Those lifetimes will be inferred by typechecking. Infer, /// `'static` lifetime. - Static { - /// We do not want to emit `elided_named_lifetimes` - /// when we are inside of a const item or a static, - /// because it would get too annoying. - suppress_elision_warning: bool, - }, + Static, /// Resolution failure. Error, /// HACK: This is used to recover the NodeId of an elided lifetime. diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index ac9c772c4278..3b1cd475e076 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -253,11 +253,6 @@ lint_duplicate_macro_attribute = lint_duplicate_matcher_binding = duplicate matcher binding -lint_elided_named_lifetime = elided lifetime has a name - .label_elided = this elided lifetime gets resolved as `{$name}` - .label_named = lifetime `{$name}` declared here - .suggestion = consider specifying it explicitly - lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 71b621e8d20e..60c477dd6c74 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -10,11 +10,11 @@ use rustc_errors::{ use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution}; -use rustc_span::{BytePos, kw}; +use rustc_session::lint::BuiltinLintDiag; +use rustc_span::BytePos; use tracing::debug; -use crate::lints::{self, ElidedNamedLifetime}; +use crate::lints; mod check_cfg; @@ -471,16 +471,5 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) } - BuiltinLintDiag::ElidedNamedLifetimes { elided: (span, kind), resolution } => { - match resolution { - ElidedLifetimeResolution::Static => { - ElidedNamedLifetime { span, kind, name: kw::StaticLifetime, declaration: None } - } - ElidedLifetimeResolution::Param(name, declaration) => { - ElidedNamedLifetime { span, kind, name, declaration: Some(declaration) } - } - } - .decorate_lint(diag) - } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0439befc6ba9..c86f66cc9b09 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -356,6 +356,7 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("unused_tuple_struct_fields", "dead_code"); store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); + store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index f895cdb2a544..31b038e6a467 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -67,7 +67,7 @@ declare_lint! { /// In certain `unsafe` code, lifetime elision combined with /// inconsistent lifetime syntax may result in unsound code. pub MISMATCHED_LIFETIME_SYNTAXES, - Allow, + Warn, "detects when a lifetime uses different syntax between arguments and return values" } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 53fbe158885e..9d3c74a9a2b8 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -8,17 +8,17 @@ use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle, }; +use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{self as hir, MissingLifetimeKind}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::inhabitedness::InhabitedPredicate; use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::lint::AmbiguityErrorDiag; use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, kw, sym}; +use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; @@ -2752,58 +2752,6 @@ pub(crate) struct ElidedLifetimesInPaths { pub subdiag: ElidedLifetimeInPathSubdiag, } -pub(crate) struct ElidedNamedLifetime { - pub span: Span, - pub kind: MissingLifetimeKind, - pub name: Symbol, - pub declaration: Option, -} - -impl LintDiagnostic<'_, G> for ElidedNamedLifetime { - fn decorate_lint(self, diag: &mut rustc_errors::Diag<'_, G>) { - let Self { span, kind, name, declaration } = self; - diag.primary_message(fluent::lint_elided_named_lifetime); - diag.arg("name", name); - diag.span_label(span, fluent::lint_label_elided); - if let Some(declaration) = declaration { - diag.span_label(declaration, fluent::lint_label_named); - } - // FIXME(GrigorenkoPV): this `if` and `return` should be removed, - // but currently this lint's suggestions can conflict with those of `clippy::needless_lifetimes`: - // https://github.com/rust-lang/rust/pull/129840#issuecomment-2323349119 - // HACK: `'static` suggestions will never sonflict, emit only those for now. - if name != kw::StaticLifetime { - return; - } - match kind { - MissingLifetimeKind::Underscore => diag.span_suggestion_verbose( - span, - fluent::lint_suggestion, - format!("{name}"), - Applicability::MachineApplicable, - ), - MissingLifetimeKind::Ampersand => diag.span_suggestion_verbose( - span.shrink_to_hi(), - fluent::lint_suggestion, - format!("{name} "), - Applicability::MachineApplicable, - ), - MissingLifetimeKind::Comma => diag.span_suggestion_verbose( - span.shrink_to_hi(), - fluent::lint_suggestion, - format!("{name}, "), - Applicability::MachineApplicable, - ), - MissingLifetimeKind::Brackets => diag.span_suggestion_verbose( - span.shrink_to_hi(), - fluent::lint_suggestion, - format!("<{name}>"), - Applicability::MachineApplicable, - ), - }; - } -} - #[derive(LintDiagnostic)] #[diag(lint_invalid_crate_type_value)] pub(crate) struct UnknownCrateTypes { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 7c7ba85d4848..fbe311b41952 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -40,7 +40,6 @@ declare_lint_pass! { DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, - ELIDED_NAMED_LIFETIMES, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, @@ -1827,38 +1826,6 @@ declare_lint! { "hidden lifetime parameters in types are deprecated" } -declare_lint! { - /// The `elided_named_lifetimes` lint detects when an elided - /// lifetime ends up being a named lifetime, such as `'static` - /// or some lifetime parameter `'a`. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(elided_named_lifetimes)] - /// struct Foo; - /// impl Foo { - /// pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { - /// unsafe { &mut *(x as *mut _) } - /// } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Lifetime elision is quite useful, because it frees you from having - /// to give each lifetime its own name, but sometimes it can produce - /// somewhat surprising resolutions. In safe code, it is mostly okay, - /// because the borrow checker prevents any unsoundness, so the worst - /// case scenario is you get a confusing error message in some other place. - /// But with `unsafe` code, such unexpected resolutions may lead to unsound code. - pub ELIDED_NAMED_LIFETIMES, - Warn, - "detects when an elided lifetime gets resolved to be `'static` or some named parameter" -} - declare_lint! { /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait /// objects. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6cbdc245d212..16fd092f2513 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -9,7 +9,7 @@ use rustc_data_structures::stable_hasher::{ use rustc_error_messages::{DiagMessage, MultiSpan}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefPathHash; -use rustc_hir::{HashStableContext, HirId, ItemLocalId, MissingLifetimeKind}; +use rustc_hir::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::edition::Edition; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; @@ -635,12 +635,6 @@ pub enum DeprecatedSinceKind { InVersion(String), } -#[derive(Debug)] -pub enum ElidedLifetimeResolution { - Static, - Param(Symbol, Span), -} - // This could be a closure, but then implementing derive trait // becomes hacky (and it gets allocated). #[derive(Debug)] @@ -653,10 +647,6 @@ pub enum BuiltinLintDiag { }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), - ElidedNamedLifetimes { - elided: (Span, MissingLifetimeKind), - resolution: ElidedLifetimeResolution, - }, UnknownCrateTypes { span: Span, candidate: Option, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 23ede3cdbad8..26742f5401f7 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1729,7 +1729,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { if ident.name == kw::StaticLifetime { self.record_lifetime_res( lifetime.id, - LifetimeRes::Static { suppress_elision_warning: false }, + LifetimeRes::Static, LifetimeElisionCandidate::Named, ); return; @@ -1877,8 +1877,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { if lifetimes_in_scope.is_empty() { self.record_lifetime_res( lifetime.id, - // We are inside a const item, so do not warn. - LifetimeRes::Static { suppress_elision_warning: true }, + LifetimeRes::Static, elision_candidate, ); return; @@ -2225,47 +2224,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { panic!("lifetime {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)") } - match candidate { - LifetimeElisionCandidate::Missing(missing @ MissingLifetime { .. }) => { - debug_assert_eq!(id, missing.id); - match res { - LifetimeRes::Static { suppress_elision_warning } => { - if !suppress_elision_warning { - self.r.lint_buffer.buffer_lint( - lint::builtin::ELIDED_NAMED_LIFETIMES, - missing.id_for_lint, - missing.span, - BuiltinLintDiag::ElidedNamedLifetimes { - elided: (missing.span, missing.kind), - resolution: lint::ElidedLifetimeResolution::Static, - }, - ); - } - } - LifetimeRes::Param { param, binder: _ } => { - let tcx = self.r.tcx(); - self.r.lint_buffer.buffer_lint( - lint::builtin::ELIDED_NAMED_LIFETIMES, - missing.id_for_lint, - missing.span, - BuiltinLintDiag::ElidedNamedLifetimes { - elided: (missing.span, missing.kind), - resolution: lint::ElidedLifetimeResolution::Param( - tcx.item_name(param.into()), - tcx.source_span(param), - ), - }, - ); - } - LifetimeRes::Fresh { .. } - | LifetimeRes::Infer - | LifetimeRes::Error - | LifetimeRes::ElidedAnchor { .. } => {} - } - } - LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {} - } - match res { LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static { .. } => { if let Some(ref mut candidates) = self.lifetime_elision_candidates { @@ -2788,14 +2746,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .. }) => { self.with_static_rib(def_kind, |this| { - this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Static { - suppress_elision_warning: true, - }), - |this| { - this.visit_ty(ty); - }, - ); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.visit_ty(ty); + }); if let Some(expr) = expr { // We already forbid generic params because of the above item rib, // so it doesn't matter whether this is a trivial constant. @@ -2832,9 +2785,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { this.visit_generics(generics); this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Static { - suppress_elision_warning: true, - }), + LifetimeRibKind::Elided(LifetimeRes::Static), |this| this.visit_ty(ty), ); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 97a45fcf2330..2f6aed35f252 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3440,7 +3440,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { maybe_static = true; in_scope_lifetimes = vec![( Ident::with_dummy_span(kw::StaticLifetime), - (DUMMY_NODE_ID, LifetimeRes::Static { suppress_elision_warning: false }), + (DUMMY_NODE_ID, LifetimeRes::Static), )]; } } else if elided_len == 0 { @@ -3452,7 +3452,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { maybe_static = true; in_scope_lifetimes = vec![( Ident::with_dummy_span(kw::StaticLifetime), - (DUMMY_NODE_ID, LifetimeRes::Static { suppress_elision_warning: false }), + (DUMMY_NODE_ID, LifetimeRes::Static), )]; } } else if num_params == 1 { diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed index f246ec61800e..bffa1c4cf408 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed @@ -77,11 +77,11 @@ fn main() { struct NoIntoIter(); impl NoIntoIter { - fn iter(&self) -> slice::Iter { + fn iter(&self) -> slice::Iter<'_, u8> { unimplemented!() } - fn iter_mut(&mut self) -> slice::IterMut { + fn iter_mut(&mut self) -> slice::IterMut<'_, u8> { unimplemented!() } } diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs index 35f4fb7097d8..6a5a3dd00ba2 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs @@ -77,11 +77,11 @@ fn main() { struct NoIntoIter(); impl NoIntoIter { - fn iter(&self) -> slice::Iter { + fn iter(&self) -> slice::Iter<'_, u8> { unimplemented!() } - fn iter_mut(&mut self) -> slice::IterMut { + fn iter_mut(&mut self) -> slice::IterMut<'_, u8> { unimplemented!() } } diff --git a/src/tools/clippy/tests/ui/iter_next_loop.rs b/src/tools/clippy/tests/ui/iter_next_loop.rs index 8e62ed963b90..969c51006af6 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.rs +++ b/src/tools/clippy/tests/ui/iter_next_loop.rs @@ -8,7 +8,7 @@ fn main() { struct Unrelated(&'static [u8]); impl Unrelated { - fn next(&self) -> std::slice::Iter { + fn next(&self) -> std::slice::Iter<'_, u8> { self.0.iter() } } diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs index 5c8c8eb4a43d..d2497ed4330c 100644 --- a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs +++ b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs @@ -71,7 +71,7 @@ impl S { struct S2([u8]); impl S2 { - fn iter(&self) -> core::slice::Iter { + fn iter(&self) -> core::slice::Iter<'_, u8> { self.0.iter() } } diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index 2f4004181f6a..f73fe288b0f8 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -49,7 +49,7 @@ struct Lt2<'a> { impl<'a> Lt2<'a> { // The lifetime is different, but that’s irrelevant; see issue #734. - pub fn new(s: &str) -> Lt2 { + pub fn new(s: &str) -> Lt2<'_> { unimplemented!() } } diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index e9d811986aa4..ceea4480d0dd 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -10,7 +10,7 @@ clippy::unnecessary_wraps, dyn_drop, clippy::get_first, - elided_named_lifetimes + mismatched_lifetime_syntaxes, )] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 0b6eb9755b93..8432f9e6d2f1 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -10,7 +10,7 @@ clippy::unnecessary_wraps, dyn_drop, clippy::get_first, - elided_named_lifetimes + mismatched_lifetime_syntaxes, )] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index 2d77bf06ff94..65f3f05d6cb0 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -312,7 +312,7 @@ mod issue_9218 { // Inferred to be `&'a str`, afaik. fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str { - //~^ ERROR: elided lifetime has a name + //~^ ERROR: lifetime flowing from input to output with different syntax todo!() } } diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 741e60cbd749..600343754e18 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -1,12 +1,3 @@ -error: elided lifetime has a name - --> tests/ui/ptr_arg.rs:314:56 - | -LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str { - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` - | - = note: `-D elided-named-lifetimes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` - error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do --> tests/ui/ptr_arg.rs:13:14 | @@ -240,5 +231,21 @@ error: writing `&String` instead of `&str` involves a new object where a slice w LL | fn good(v1: &String, v2: &String) { | ^^^^^^^ help: change this to: `&str` +error: lifetime flowing from input to output with different syntax can be confusing + --> tests/ui/ptr_arg.rs:314:36 + | +LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str { + | ^^ ^^ ---- the lifetime gets resolved as `'a` + | | | + | | these lifetimes flow to the output + | these lifetimes flow to the output + | + = note: `-D mismatched-lifetime-syntaxes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(mismatched_lifetime_syntaxes)]` +help: one option is to consistently use `'a` + | +LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &'a str { + | ++ + error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs index 4f65a06680d6..78fc365bd5bb 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs @@ -191,7 +191,7 @@ struct CounterWrapper<'a> { } impl<'a> CounterWrapper<'a> { - fn new(counter: &Counter) -> CounterWrapper { + fn new(counter: &Counter) -> CounterWrapper<'_> { counter.i.fetch_add(1, Ordering::Relaxed); CounterWrapper { counter } } @@ -204,7 +204,7 @@ impl<'a> Drop for CounterWrapper<'a> { } impl Counter { - fn temp_increment(&self) -> Vec { + fn temp_increment(&self) -> Vec> { vec![CounterWrapper::new(self), CounterWrapper::new(self)] } } @@ -480,7 +480,7 @@ impl StateWithBoxedMutexGuard { fn new() -> StateWithBoxedMutexGuard { StateWithBoxedMutexGuard { u: Mutex::new(42) } } - fn lock(&self) -> Box> { + fn lock(&self) -> Box> { Box::new(self.u.lock().unwrap()) } } @@ -507,7 +507,7 @@ impl StateStringWithBoxedMutexGuard { s: Mutex::new("A String".to_owned()), } } - fn lock(&self) -> Box> { + fn lock(&self) -> Box> { Box::new(self.s.lock().unwrap()) } } @@ -686,11 +686,11 @@ struct Guard<'a, T>(MutexGuard<'a, T>); struct Ref<'a, T>(&'a T); impl<'a, T> Guard<'a, T> { - fn guard(&self) -> &MutexGuard { + fn guard(&self) -> &MutexGuard<'_, T> { &self.0 } - fn guard_ref(&self) -> Ref> { + fn guard_ref(&self) -> Ref<'_, MutexGuard<'_, T>> { Ref(&self.0) } diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed index af7d82130f01..1a07f119398a 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed @@ -41,6 +41,7 @@ fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 { &foo.0 } +#[allow(mismatched_lifetime_syntaxes)] fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef { FooRef { foo } } diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs index 00e11a1ea280..07b1842bbf85 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs @@ -41,6 +41,7 @@ fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 { &foo.0 } +#[allow(mismatched_lifetime_syntaxes)] fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef { FooRef { foo } } diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr index f101ac5ccd68..36247d3fe0b1 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,5 +1,5 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:53:11 + --> tests/ui/trivially_copy_pass_by_ref.rs:54:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` @@ -11,103 +11,103 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:53:20 + --> tests/ui/trivially_copy_pass_by_ref.rs:54:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:53:29 + --> tests/ui/trivially_copy_pass_by_ref.rs:54:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:63:12 + --> tests/ui/trivially_copy_pass_by_ref.rs:64:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:63:22 + --> tests/ui/trivially_copy_pass_by_ref.rs:64:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:63:31 + --> tests/ui/trivially_copy_pass_by_ref.rs:64:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:63:40 + --> tests/ui/trivially_copy_pass_by_ref.rs:64:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:69:16 + --> tests/ui/trivially_copy_pass_by_ref.rs:70:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:69:25 + --> tests/ui/trivially_copy_pass_by_ref.rs:70:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:69:34 + --> tests/ui/trivially_copy_pass_by_ref.rs:70:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:74:35 + --> tests/ui/trivially_copy_pass_by_ref.rs:75:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:87:16 + --> tests/ui/trivially_copy_pass_by_ref.rs:88:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:87:25 + --> tests/ui/trivially_copy_pass_by_ref.rs:88:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:87:34 + --> tests/ui/trivially_copy_pass_by_ref.rs:88:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:94:33 + --> tests/ui/trivially_copy_pass_by_ref.rs:95:33 | LL | fn trait_method(&self, foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:132:21 + --> tests/ui/trivially_copy_pass_by_ref.rs:133:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:138:15 + --> tests/ui/trivially_copy_pass_by_ref.rs:139:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:164:36 + --> tests/ui/trivially_copy_pass_by_ref.rs:165:36 | LL | fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index f15e5e0a5bb4..cccb6bffabb7 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -69,7 +69,7 @@ mod lifetimes { impl<'a> Foo<'a> { // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) -> // Foo<'b>` - fn foo(s: &str) -> Foo { + fn foo(s: &str) -> Foo<'_> { Foo { foo_str: s } } // cannot replace with `Self`, because that's `Foo<'a>` diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index b6376938611e..09288677aa71 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -69,7 +69,7 @@ mod lifetimes { impl<'a> Foo<'a> { // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) -> // Foo<'b>` - fn foo(s: &str) -> Foo { + fn foo(s: &str) -> Foo<'_> { Foo { foo_str: s } } // cannot replace with `Self`, because that's `Foo<'a>` diff --git a/tests/ui/associated-types/associated-types-impl-redirect.rs b/tests/ui/associated-types/associated-types-impl-redirect.rs index 65e6a094b77d..2cbe0d725402 100644 --- a/tests/ui/associated-types/associated-types-impl-redirect.rs +++ b/tests/ui/associated-types/associated-types-impl-redirect.rs @@ -21,7 +21,7 @@ trait Iterator { } trait IteratorExt: Iterator + Sized { - fn by_ref(&mut self) -> ByRef { + fn by_ref(&mut self) -> ByRef<'_, Self> { ByRef(self) } } diff --git a/tests/ui/associated-types/associated-types-normalize-in-bounds-ufcs.rs b/tests/ui/associated-types/associated-types-normalize-in-bounds-ufcs.rs index f15de0d9a289..b32323181b5f 100644 --- a/tests/ui/associated-types/associated-types-normalize-in-bounds-ufcs.rs +++ b/tests/ui/associated-types/associated-types-normalize-in-bounds-ufcs.rs @@ -21,11 +21,11 @@ trait SliceExt2 { impl SliceExt2 for [T] { type Item = T; - fn split2

(&self, pred: P) -> Splits where P: FnMut(&T) -> bool { + fn split2

(&self, pred: P) -> Splits<'_, T, P> where P: FnMut(&T) -> bool { loop {} } - fn splitn2

(&self, n: u32, pred: P) -> SplitsN> where P: FnMut(&T) -> bool { + fn splitn2

(&self, n: u32, pred: P) -> SplitsN> where P: FnMut(&T) -> bool { SliceExt2::split2(self, pred); loop {} } diff --git a/tests/ui/associated-types/associated-types-normalize-in-bounds.rs b/tests/ui/associated-types/associated-types-normalize-in-bounds.rs index 7e94d3a011f5..6844c5f9adb8 100644 --- a/tests/ui/associated-types/associated-types-normalize-in-bounds.rs +++ b/tests/ui/associated-types/associated-types-normalize-in-bounds.rs @@ -3,32 +3,40 @@ // Test that we normalize associated types that appear in bounds; if // we didn't, the call to `self.split2()` fails to type check. - use std::marker::PhantomData; -struct Splits<'a, T, P>(PhantomData<(&'a(),T,P)>); +struct Splits<'a, T, P>(PhantomData<(&'a (), T, P)>); struct SplitsN(PhantomData); trait SliceExt2 { type Item; fn split2<'a, P>(&'a self, pred: P) -> Splits<'a, Self::Item, P> - where P: FnMut(&Self::Item) -> bool; + where + P: FnMut(&Self::Item) -> bool; + fn splitn2<'a, P>(&'a self, n: usize, pred: P) -> SplitsN> - where P: FnMut(&Self::Item) -> bool; + where + P: FnMut(&Self::Item) -> bool; } impl SliceExt2 for [T] { type Item = T; - fn split2

(&self, pred: P) -> Splits where P: FnMut(&T) -> bool { + fn split2

(&self, pred: P) -> Splits<'_, T, P> + where + P: FnMut(&T) -> bool, + { loop {} } - fn splitn2

(&self, n: usize, pred: P) -> SplitsN> where P: FnMut(&T) -> bool { + fn splitn2

(&self, n: usize, pred: P) -> SplitsN> + where + P: FnMut(&T) -> bool, + { self.split2(pred); loop {} } } -fn main() { } +fn main() {} diff --git a/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs b/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs index dcfa3532cdfb..34c269d4d903 100644 --- a/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs +++ b/tests/ui/associated-types/associated-types-where-clause-impl-ambiguity.rs @@ -20,7 +20,7 @@ trait Iterator { } trait IteratorExt: Iterator + Sized { - fn by_ref(&mut self) -> ByRef { + fn by_ref(&mut self) -> ByRef<'_, Self> { ByRef(self) } } diff --git a/tests/ui/associated-types/cache/elision.rs b/tests/ui/associated-types/cache/elision.rs index 12765fc58110..7ddb32ea8741 100644 --- a/tests/ui/associated-types/cache/elision.rs +++ b/tests/ui/associated-types/cache/elision.rs @@ -14,7 +14,7 @@ pub trait UnicodeStr { impl UnicodeStr for str { #[inline] - fn split_whitespace(&self) -> SplitWhitespace { + fn split_whitespace(&self) -> SplitWhitespace<'_> { unimplemented!() } } diff --git a/tests/ui/associated-types/project-defer-unification.rs b/tests/ui/associated-types/project-defer-unification.rs index b51228ef4117..a949171db122 100644 --- a/tests/ui/associated-types/project-defer-unification.rs +++ b/tests/ui/associated-types/project-defer-unification.rs @@ -50,7 +50,7 @@ where P: Pixel + 'static, loop { } } - pub fn pixels_mut(&mut self) -> PixelsMut

{ + pub fn pixels_mut(&mut self) -> PixelsMut<'_, P> { loop { } } } diff --git a/tests/ui/async-await/issues/issue-63388-1.rs b/tests/ui/async-await/issues/issue-63388-1.rs index acfc64baff97..3a89f3ebfd2b 100644 --- a/tests/ui/async-await/issues/issue-63388-1.rs +++ b/tests/ui/async-await/issues/issue-63388-1.rs @@ -9,7 +9,7 @@ trait Foo {} impl Xyz { async fn do_sth<'a>( &'a self, foo: &dyn Foo - ) -> &dyn Foo //~ WARNING elided lifetime has a name + ) -> &dyn Foo { foo //~^ ERROR explicit lifetime required in the type of `foo` [E0621] diff --git a/tests/ui/async-await/issues/issue-63388-1.stderr b/tests/ui/async-await/issues/issue-63388-1.stderr index 579caa45bc94..277f7fa6f63e 100644 --- a/tests/ui/async-await/issues/issue-63388-1.stderr +++ b/tests/ui/async-await/issues/issue-63388-1.stderr @@ -1,14 +1,3 @@ -warning: elided lifetime has a name - --> $DIR/issue-63388-1.rs:12:10 - | -LL | async fn do_sth<'a>( - | -- lifetime `'a` declared here -LL | &'a self, foo: &dyn Foo -LL | ) -> &dyn Foo - | ^ this elided lifetime gets resolved as `'a` - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0621]: explicit lifetime required in the type of `foo` --> $DIR/issue-63388-1.rs:14:9 | @@ -18,6 +7,6 @@ LL | &'a self, foo: &dyn Foo LL | foo | ^^^ lifetime `'a` required -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/borrowck/copy-suggestion-region-vid.fixed b/tests/ui/borrowck/copy-suggestion-region-vid.fixed index 7fe18615408b..2bc8a74086e3 100644 --- a/tests/ui/borrowck/copy-suggestion-region-vid.fixed +++ b/tests/ui/borrowck/copy-suggestion-region-vid.fixed @@ -7,7 +7,7 @@ pub struct HelperStruct<'n> { } impl DataStruct { - pub fn f(&self) -> HelperStruct { + pub fn f(&self) -> HelperStruct<'_> { let helpers = [vec![], vec![]]; HelperStruct { helpers: helpers.clone(), is_empty: helpers[0].is_empty() } diff --git a/tests/ui/borrowck/copy-suggestion-region-vid.rs b/tests/ui/borrowck/copy-suggestion-region-vid.rs index daafba71ece7..248ce80d22bf 100644 --- a/tests/ui/borrowck/copy-suggestion-region-vid.rs +++ b/tests/ui/borrowck/copy-suggestion-region-vid.rs @@ -7,7 +7,7 @@ pub struct HelperStruct<'n> { } impl DataStruct { - pub fn f(&self) -> HelperStruct { + pub fn f(&self) -> HelperStruct<'_> { let helpers = [vec![], vec![]]; HelperStruct { helpers, is_empty: helpers[0].is_empty() } diff --git a/tests/ui/const-generics/type-dependent/issue-71348.full.stderr b/tests/ui/const-generics/type-dependent/issue-71348.full.stderr index 177ff20fbf9e..f68fdb3b6515 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.full.stderr +++ b/tests/ui/const-generics/type-dependent/issue-71348.full.stderr @@ -1,10 +1,17 @@ -warning: elided lifetime has a name - --> $DIR/issue-71348.rs:18:68 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/issue-71348.rs:18:40 | LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | ^^ -- ------------------------ the lifetimes get resolved as `'a` + | | | + | | the lifetimes get resolved as `'a` + | this lifetime flows to the output | - = note: `#[warn(elided_named_lifetimes)]` on by default + = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default +help: one option is to consistently use `'a` + | +LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target + | +++ warning: 1 warning emitted diff --git a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr index 8995c4158635..c491469bcbd2 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr +++ b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr @@ -1,11 +1,3 @@ -warning: elided lifetime has a name - --> $DIR/issue-71348.rs:18:68 - | -LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error: `&'static str` is forbidden as the type of a const generic parameter --> $DIR/issue-71348.rs:10:24 | @@ -38,5 +30,5 @@ help: add `#![feature(unsized_const_params)]` to the crate attributes to enable LL + #![feature(unsized_const_params)] | -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/type-dependent/issue-71348.rs b/tests/ui/const-generics/type-dependent/issue-71348.rs index 97e786405fe3..c6563c803059 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.rs +++ b/tests/ui/const-generics/type-dependent/issue-71348.rs @@ -17,7 +17,7 @@ trait Get<'a, const N: &'static str> { impl Foo { fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target //[min]~^ ERROR `&'static str` is forbidden as the type of a const generic parameter - //~^^ WARNING elided lifetime has a name + //[full]~^^ WARNING lifetime flowing from input to output with different syntax where Self: Get<'a, N>, { diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed index 6c8d2d3fa9c1..42f805923ec2 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.fixed +++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed @@ -589,7 +589,7 @@ impl Events { Ok(LogDrop(self, m)) } /// Return an `Err` value that logs its drop. - fn err(&self, m: u64) -> Result { + fn err(&self, m: u64) -> Result, LogDrop<'_>> { Err(LogDrop(self, m)) } /// Log an event. diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs index 9a10a08a3ff7..e7425159aa23 100644 --- a/tests/ui/drop/drop-order-comparisons.rs +++ b/tests/ui/drop/drop-order-comparisons.rs @@ -589,7 +589,7 @@ impl Events { Ok(LogDrop(self, m)) } /// Return an `Err` value that logs its drop. - fn err(&self, m: u64) -> Result { + fn err(&self, m: u64) -> Result, LogDrop<'_>> { Err(LogDrop(self, m)) } /// Log an event. diff --git a/tests/ui/drop/drop_order.rs b/tests/ui/drop/drop_order.rs index b96e55a2535a..34b1a0e8f754 100644 --- a/tests/ui/drop/drop_order.rs +++ b/tests/ui/drop/drop_order.rs @@ -23,11 +23,11 @@ impl Drop for LoudDrop<'_> { } impl DropOrderCollector { - fn option_loud_drop(&self, n: u32) -> Option { + fn option_loud_drop(&self, n: u32) -> Option> { Some(LoudDrop(self, n)) } - fn loud_drop(&self, n: u32) -> LoudDrop { + fn loud_drop(&self, n: u32) -> LoudDrop<'_> { LoudDrop(self, n) } diff --git a/tests/ui/drop/drop_order_if_let_rescope.rs b/tests/ui/drop/drop_order_if_let_rescope.rs index 27bced5fa62f..e96ceedd5cb1 100644 --- a/tests/ui/drop/drop_order_if_let_rescope.rs +++ b/tests/ui/drop/drop_order_if_let_rescope.rs @@ -18,7 +18,7 @@ impl Drop for LoudDrop<'_> { } impl DropOrderCollector { - fn option_loud_drop(&self, n: u32) -> Option { + fn option_loud_drop(&self, n: u32) -> Option> { Some(LoudDrop(self, n)) } diff --git a/tests/ui/drop/issue-2735-2.rs b/tests/ui/drop/issue-2735-2.rs index 7a6ed6ea2f8d..66025956e088 100644 --- a/tests/ui/drop/issue-2735-2.rs +++ b/tests/ui/drop/issue-2735-2.rs @@ -14,7 +14,7 @@ impl<'a> Drop for defer<'a> { } } -fn defer(b: &Cell) -> defer { +fn defer(b: &Cell) -> defer<'_> { defer { b: b } diff --git a/tests/ui/drop/issue-2735-3.rs b/tests/ui/drop/issue-2735-3.rs index 3bb4536537cb..c9535168653c 100644 --- a/tests/ui/drop/issue-2735-3.rs +++ b/tests/ui/drop/issue-2735-3.rs @@ -14,7 +14,7 @@ impl<'a> Drop for defer<'a> { } } -fn defer(b: &Cell) -> defer { +fn defer(b: &Cell) -> defer<'_> { defer { b: b } diff --git a/tests/ui/drop/issue-979.rs b/tests/ui/drop/issue-979.rs index 8d98ac4df233..70052708be6c 100644 --- a/tests/ui/drop/issue-979.rs +++ b/tests/ui/drop/issue-979.rs @@ -13,7 +13,7 @@ impl<'a> Drop for r<'a> { } } -fn r(b: &Cell) -> r { +fn r(b: &Cell) -> r<'_> { r { b: b } diff --git a/tests/ui/drop/tail-expr-drop-order.rs b/tests/ui/drop/tail-expr-drop-order.rs index f74530fce1e2..a6807b16b506 100644 --- a/tests/ui/drop/tail-expr-drop-order.rs +++ b/tests/ui/drop/tail-expr-drop-order.rs @@ -28,11 +28,11 @@ impl Drop for LoudDrop<'_> { } impl DropOrderCollector { - fn option_loud_drop(&self, n: u32) -> Option { + fn option_loud_drop(&self, n: u32) -> Option> { Some(LoudDrop(self, n)) } - fn loud_drop(&self, n: u32) -> LoudDrop { + fn loud_drop(&self, n: u32) -> LoudDrop<'_> { LoudDrop(self, n) } diff --git a/tests/ui/generics/export-name-on-generics.fixed b/tests/ui/generics/export-name-on-generics.fixed index 4430cd9a2998..c8a3fd5798f8 100644 --- a/tests/ui/generics/export-name-on-generics.fixed +++ b/tests/ui/generics/export-name-on-generics.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -#![allow(dead_code, elided_named_lifetimes)] +#![allow(dead_code, mismatched_lifetime_syntaxes)] #![deny(no_mangle_generic_items)] pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled diff --git a/tests/ui/generics/export-name-on-generics.rs b/tests/ui/generics/export-name-on-generics.rs index cbf110219605..8b38037fe12f 100644 --- a/tests/ui/generics/export-name-on-generics.rs +++ b/tests/ui/generics/export-name-on-generics.rs @@ -1,5 +1,5 @@ //@ run-rustfix -#![allow(dead_code, elided_named_lifetimes)] +#![allow(dead_code, mismatched_lifetime_syntaxes)] #![deny(no_mangle_generic_items)] #[export_name = "foo"] diff --git a/tests/ui/generics/generic-no-mangle.fixed b/tests/ui/generics/generic-no-mangle.fixed index 2776848c45ff..e3e41eb9d0db 100644 --- a/tests/ui/generics/generic-no-mangle.fixed +++ b/tests/ui/generics/generic-no-mangle.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -#![allow(dead_code, elided_named_lifetimes)] +#![allow(dead_code, mismatched_lifetime_syntaxes)] #![deny(no_mangle_generic_items)] pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled diff --git a/tests/ui/generics/generic-no-mangle.rs b/tests/ui/generics/generic-no-mangle.rs index 5314005d31fa..085f8610a548 100644 --- a/tests/ui/generics/generic-no-mangle.rs +++ b/tests/ui/generics/generic-no-mangle.rs @@ -1,5 +1,5 @@ //@ run-rustfix -#![allow(dead_code, elided_named_lifetimes)] +#![allow(dead_code, mismatched_lifetime_syntaxes)] #![deny(no_mangle_generic_items)] #[no_mangle] diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs index c368f2650629..e00a31e26aa2 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-1.rs @@ -25,7 +25,7 @@ where impl Inject for RefMutFamily { type I = Self; - fn inject(_: &()) -> ::Out { + fn inject(_: &()) -> >::Out { unimplemented!() } } diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs index a7f38b5c16af..da7530b4e7a8 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs @@ -13,7 +13,6 @@ fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` - //~| WARNING elided lifetime has a name |x| x } diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr index 31f39eb90041..40cb6b647d1e 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/impl-fn-hrtb-bounds.rs:20:38 + --> $DIR/impl-fn-hrtb-bounds.rs:19:38 | LL | fn d() -> impl Fn() -> (impl Debug + '_) { | ^^ expected named lifetime parameter @@ -11,14 +11,6 @@ LL - fn d() -> impl Fn() -> (impl Debug + '_) { LL + fn d() -> impl Fn() -> (impl Debug + 'static) { | -warning: elided lifetime has a name - --> $DIR/impl-fn-hrtb-bounds.rs:14:52 - | -LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { - | -- lifetime `'a` declared here ^^ this elided lifetime gets resolved as `'a` - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` --> $DIR/impl-fn-hrtb-bounds.rs:4:41 | @@ -55,7 +47,7 @@ note: lifetime declared here LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { | ^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 4 previous errors Some errors have detailed explanations: E0106, E0657. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs index 776bb7278ce1..199cbbf4fcc9 100644 --- a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs +++ b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs @@ -2,7 +2,6 @@ use std::fmt::Debug; fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { - //~^ WARNING elided lifetime has a name |x| x //~^ ERROR expected generic lifetime parameter, found `'_` } diff --git a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr index 209186db4cc1..6064b09ef092 100644 --- a/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr +++ b/tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr @@ -1,20 +1,11 @@ -warning: elided lifetime has a name - --> $DIR/impl-fn-predefined-lifetimes.rs:4:48 - | -LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { - | -- lifetime `'a` declared here ^^ this elided lifetime gets resolved as `'a` - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/impl-fn-predefined-lifetimes.rs:6:9 + --> $DIR/impl-fn-predefined-lifetimes.rs:5:9 | LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) { | -- this generic parameter must be used with a generic lifetime parameter -LL | LL | |x| x | ^ -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs index e48441f533d7..1ac3c593dbe5 100644 --- a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs +++ b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs @@ -1,7 +1,7 @@ //@ check-pass pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax v.into_iter() } diff --git a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr index bff3ffd934ac..b9d8674992ca 100644 --- a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr +++ b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr @@ -1,10 +1,14 @@ -warning: elided lifetime has a name - --> $DIR/rpit-assoc-pair-with-lifetime.rs:3:82 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/rpit-assoc-pair-with-lifetime.rs:3:31 | LL | pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | ^^ this lifetime flows to the output ---- the lifetime gets resolved as `'a` | - = note: `#[warn(elided_named_lifetimes)]` on by default + = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default +help: one option is to consistently use `'a` + | +LL | pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { + | ++ warning: 1 warning emitted diff --git a/tests/ui/issues/issue-2502.rs b/tests/ui/issues/issue-2502.rs index dfc0995104ea..98a52a3b5a7d 100644 --- a/tests/ui/issues/issue-2502.rs +++ b/tests/ui/issues/issue-2502.rs @@ -14,7 +14,7 @@ impl<'a> font<'a> { } } -fn font(fontbuf: &Vec ) -> font { +fn font(fontbuf: &Vec ) -> font<'_> { font { fontbuf: fontbuf } diff --git a/tests/ui/issues/issue-42552.rs b/tests/ui/issues/issue-42552.rs index 998cde44be01..734921d9b012 100644 --- a/tests/ui/issues/issue-42552.rs +++ b/tests/ui/issues/issue-42552.rs @@ -1,7 +1,7 @@ //@ run-pass // Regression test for an obscure issue with the projection cache. -fn into_iter(a: &I) -> Groups { +fn into_iter(a: &I) -> Groups<'_, I> { Groups { _a: a } } diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs index ce9ef78ffcd9..6fa3cfa47245 100644 --- a/tests/ui/issues/issue-5708.rs +++ b/tests/ui/issues/issue-5708.rs @@ -25,7 +25,7 @@ struct Outer<'a> { } impl<'a> Outer<'a> { - fn new(inner: &dyn Inner) -> Outer { + fn new(inner: &dyn Inner) -> Outer<'_> { Outer { inner: inner } diff --git a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed index 2ceaaf0339d9..bcc4abc47e15 100644 --- a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed +++ b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed @@ -28,6 +28,7 @@ impl Greeter1 for FixedGreeter<'_> { struct Greetings(pub Vec); impl Greetings { + #[expect(mismatched_lifetime_syntaxes)] pub fn get(&self, i: usize) -> BoxedGreeter { (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i]))) //~^ ERROR lifetime may not live long enough diff --git a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs index e7d427517b5f..9ca6077f47f3 100644 --- a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs +++ b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs @@ -28,6 +28,7 @@ impl Greeter1 for FixedGreeter<'_> { struct Greetings(pub Vec); impl Greetings { + #[expect(mismatched_lifetime_syntaxes)] pub fn get(&self, i: usize) -> BoxedGreeter { (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i]))) //~^ ERROR lifetime may not live long enough diff --git a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr index 24bb9e2ef7d6..2eba3ff418ba 100644 --- a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr +++ b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs:32:9 + --> $DIR/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs:33:9 | LL | pub fn get(&self, i: usize) -> BoxedGreeter { | - let's call the lifetime of this reference `'1` diff --git a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs index 63a2c9be9eba..d0a8fe795efd 100644 --- a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -47,6 +47,5 @@ fn l<'a>(_: &'a str, _: &'a str) -> &str { "" } // This is ok because both `'a` are for the same parameter. fn m<'a>(_: &'a Foo<'a>) -> &str { "" } -//~^ WARNING elided lifetime has a name fn main() {} diff --git a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr index f835d2655bb0..23ef36888f07 100644 --- a/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr +++ b/tests/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr @@ -105,16 +105,6 @@ help: consider using the `'a` lifetime LL | fn l<'a>(_: &'a str, _: &'a str) -> &'a str { "" } | ++ -warning: elided lifetime has a name - --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:49:29 - | -LL | fn m<'a>(_: &'a Foo<'a>) -> &str { "" } - | -- ^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - | - = note: `#[warn(elided_named_lifetimes)]` on by default - -error: aborting due to 7 previous errors; 1 warning emitted +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs index 598633d75768..5dbc0c556fb5 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs @@ -4,12 +4,8 @@ struct Foo { impl Foo { fn foo<'a>(&'a self, x: &i32) -> &i32 { - //~^ WARNING elided lifetime has a name - if true { &self.field } else { x } //~ ERROR explicit lifetime - } - } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr index 2d5d4fb0e72e..071bda24ef8d 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.stderr @@ -1,22 +1,11 @@ -warning: elided lifetime has a name - --> $DIR/ex1-return-one-existing-name-if-else-using-impl-3.rs:6:36 - | -LL | fn foo<'a>(&'a self, x: &i32) -> &i32 { - | -- ^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/ex1-return-one-existing-name-if-else-using-impl-3.rs:9:36 + --> $DIR/ex1-return-one-existing-name-if-else-using-impl-3.rs:7:36 | LL | fn foo<'a>(&'a self, x: &i32) -> &i32 { | ---- help: add explicit lifetime `'a` to the type of `x`: `&'a i32` -... LL | if true { &self.field } else { x } | ^ lifetime `'a` required -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.rs similarity index 55% rename from tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs rename to tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.rs index eac7c32a9aac..1804003d3672 100644 --- a/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.rs +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.rs @@ -1,10 +1,10 @@ -#![deny(elided_named_lifetimes)] +#![deny(mismatched_lifetime_syntaxes)] struct Foo; impl Foo { pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { - //~^ ERROR elided lifetime has a name + //~^ ERROR lifetime flowing from input to output with different syntax unsafe { &mut *(x as *mut _) } } } diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr new file mode 100644 index 000000000000..7c7411651d03 --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr @@ -0,0 +1,20 @@ +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/example-from-issue48686.rs:6:21 + | +LL | pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { + | ^^^^^^^ ------- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +note: the lint level is defined here + --> $DIR/example-from-issue48686.rs:1:9 + | +LL | #![deny(mismatched_lifetime_syntaxes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: one option is to consistently use `'static` + | +LL | pub fn get_mut(&'static self, x: &mut u8) -> &'static mut u8 { + | +++++++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.rs new file mode 100644 index 000000000000..3d5aab5c8295 --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.rs @@ -0,0 +1,27 @@ +#![deny(mismatched_lifetime_syntaxes)] + +fn ampersand<'a>(x: &'a u8) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + x +} + +struct Brackets<'a>(&'a u8); + +fn brackets<'a>(x: &'a u8) -> Brackets { + //~^ ERROR lifetime flowing from input to output with different syntax + Brackets(x) +} + +struct Comma<'a, T>(&'a T); + +fn comma<'a>(x: &'a u8) -> Comma { + //~^ ERROR lifetime flowing from input to output with different syntax + Comma(x) +} + +fn underscore<'a>(x: &'a u8) -> &'_ u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + x +} + +fn main() {} diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr new file mode 100644 index 000000000000..681b3c970526 --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr @@ -0,0 +1,60 @@ +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/missing-lifetime-kind.rs:3:22 + | +LL | fn ampersand<'a>(x: &'a u8) -> &u8 { + | ^^ --- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +note: the lint level is defined here + --> $DIR/missing-lifetime-kind.rs:1:9 + | +LL | #![deny(mismatched_lifetime_syntaxes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: one option is to consistently use `'a` + | +LL | fn ampersand<'a>(x: &'a u8) -> &'a u8 { + | ++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/missing-lifetime-kind.rs:10:21 + | +LL | fn brackets<'a>(x: &'a u8) -> Brackets { + | ^^ -------- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn brackets<'a>(x: &'a u8) -> Brackets<'a> { + | ++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/missing-lifetime-kind.rs:17:18 + | +LL | fn comma<'a>(x: &'a u8) -> Comma { + | ^^ --------- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn comma<'a>(x: &'a u8) -> Comma<'a, u8> { + | +++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/missing-lifetime-kind.rs:22:23 + | +LL | fn underscore<'a>(x: &'a u8) -> &'_ u8 { + | ^^ -- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL - fn underscore<'a>(x: &'a u8) -> &'_ u8 { +LL + fn underscore<'a>(x: &'a u8) -> &'a u8 { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.rs new file mode 100644 index 000000000000..cc398ab78883 --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.rs @@ -0,0 +1,20 @@ +#![allow(mismatched_lifetime_syntaxes)] + +//! Ensure that the lint level of `mismatched_lifetime_syntaxes` can +//! be adjusted by attributes not applied at the crate-level. + +#[warn(mismatched_lifetime_syntaxes)] +mod foo { + fn bar(x: &'static u8) -> &u8 { + //~^ WARNING lifetime flowing from input to output with different syntax + x + } + + #[deny(mismatched_lifetime_syntaxes)] + fn baz(x: &'static u8) -> &u8 { + //~^ ERROR lifetime flowing from input to output with different syntax + x + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr new file mode 100644 index 000000000000..da691225c176 --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr @@ -0,0 +1,38 @@ +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/not-tied-to-crate.rs:8:16 + | +LL | fn bar(x: &'static u8) -> &u8 { + | ^^^^^^^ --- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +note: the lint level is defined here + --> $DIR/not-tied-to-crate.rs:6:8 + | +LL | #[warn(mismatched_lifetime_syntaxes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: one option is to consistently use `'static` + | +LL | fn bar(x: &'static u8) -> &'static u8 { + | +++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/not-tied-to-crate.rs:14:16 + | +LL | fn baz(x: &'static u8) -> &u8 { + | ^^^^^^^ --- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +note: the lint level is defined here + --> $DIR/not-tied-to-crate.rs:13:12 + | +LL | #[deny(mismatched_lifetime_syntaxes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: one option is to consistently use `'static` + | +LL | fn baz(x: &'static u8) -> &'static u8 { + | +++++++ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/lint/elided-named-lifetimes/static.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.rs similarity index 63% rename from tests/ui/lint/elided-named-lifetimes/static.rs rename to tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.rs index dc8222c6e6e4..47ae258f138f 100644 --- a/tests/ui/lint/elided-named-lifetimes/static.rs +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.rs @@ -1,4 +1,4 @@ -#![deny(elided_named_lifetimes)] +#![deny(mismatched_lifetime_syntaxes)] use std::borrow::Cow; @@ -14,26 +14,26 @@ impl Trait for () { } fn ampersand(x: &'static u8) -> &u8 { - //~^ ERROR elided lifetime has a name + //~^ ERROR lifetime flowing from input to output with different syntax x } struct Brackets<'a>(&'a u8); fn brackets(x: &'static u8) -> Brackets { - //~^ ERROR elided lifetime has a name + //~^ ERROR lifetime flowing from input to output with different syntax Brackets(x) } struct Comma<'a, T>(&'a T); fn comma(x: &'static u8) -> Comma { - //~^ ERROR elided lifetime has a name + //~^ ERROR lifetime flowing from input to output with different syntax Comma(x) } fn underscore(x: &'static u8) -> &'_ u8 { - //~^ ERROR elided lifetime has a name + //~^ ERROR lifetime flowing from input to output with different syntax x } diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr new file mode 100644 index 000000000000..5b9a986bcbea --- /dev/null +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr @@ -0,0 +1,60 @@ +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/static.rs:16:18 + | +LL | fn ampersand(x: &'static u8) -> &u8 { + | ^^^^^^^ --- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +note: the lint level is defined here + --> $DIR/static.rs:1:9 + | +LL | #![deny(mismatched_lifetime_syntaxes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: one option is to consistently use `'static` + | +LL | fn ampersand(x: &'static u8) -> &'static u8 { + | +++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/static.rs:23:17 + | +LL | fn brackets(x: &'static u8) -> Brackets { + | ^^^^^^^ -------- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL | fn brackets(x: &'static u8) -> Brackets<'static> { + | +++++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/static.rs:30:14 + | +LL | fn comma(x: &'static u8) -> Comma { + | ^^^^^^^ --------- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL | fn comma(x: &'static u8) -> Comma<'static, u8> { + | ++++++++ + +error: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/static.rs:35:19 + | +LL | fn underscore(x: &'static u8) -> &'_ u8 { + | ^^^^^^^ -- the lifetime gets resolved as `'static` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'static` + | +LL - fn underscore(x: &'static u8) -> &'_ u8 { +LL + fn underscore(x: &'static u8) -> &'static u8 { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs index ffeaab36bf39..6d8487b99c6f 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs @@ -1,8 +1,5 @@ #![deny(mismatched_lifetime_syntaxes)] -// `elided_named_lifetimes` overlaps with `lifetime_style_mismatch`, ignore it for now -#![allow(elided_named_lifetimes)] - #[derive(Copy, Clone)] struct ContainsLifetime<'a>(&'a u8); diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr index 57af0ac93cfa..0ec16a266b60 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr @@ -1,5 +1,5 @@ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:11:47 + --> $DIR/mismatched-lifetime-syntaxes.rs:8:47 | LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &u8 { | ^^ --- the lifetime gets resolved as `'a` @@ -17,7 +17,7 @@ LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &'a u8 { | ++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:16:57 + --> $DIR/mismatched-lifetime-syntaxes.rs:13:57 | LL | fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 { | ^^ -- the lifetime gets resolved as `'a` @@ -31,7 +31,7 @@ LL + fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'a u8 { | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:23:48 + --> $DIR/mismatched-lifetime-syntaxes.rs:20:48 | LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime) -> ContainsLifetime<'_> { | ^^^^^^^^^^^^^^^^ -- the lifetime gets resolved as `'_` @@ -44,7 +44,7 @@ LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime<'_>) -> Con | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:28:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:25:65 | LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime { | ^^ ---------------- the lifetime gets resolved as `'_` @@ -57,7 +57,7 @@ LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> Con | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:33:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:30:65 | LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime { | ^^ ---------------- the lifetime gets resolved as `'a` @@ -70,7 +70,7 @@ LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> Con | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:39:25 + --> $DIR/mismatched-lifetime-syntaxes.rs:36:25 | LL | v: ContainsLifetime<'a>, | ^^ this lifetime flows to the output @@ -85,7 +85,7 @@ LL + ) -> ContainsLifetime<'a> { | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:47:37 + --> $DIR/mismatched-lifetime-syntaxes.rs:44:37 | LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime { | ^^^ ---------------- the lifetime gets resolved as `'_` @@ -98,7 +98,7 @@ LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> { | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:52:48 + --> $DIR/mismatched-lifetime-syntaxes.rs:49:48 | LL | fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime { | ^^ ---------------- the lifetime gets resolved as `'_` @@ -112,7 +112,7 @@ LL + fn explicit_anonymous_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:57:48 + --> $DIR/mismatched-lifetime-syntaxes.rs:54:48 | LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime { | ^^ ---------------- the lifetime gets resolved as `'a` @@ -125,7 +125,7 @@ LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime<' | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:62:58 + --> $DIR/mismatched-lifetime-syntaxes.rs:59:58 | LL | fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> { | ^^ -- the lifetime gets resolved as `'a` @@ -139,7 +139,7 @@ LL + fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> Contains | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:69:37 + --> $DIR/mismatched-lifetime-syntaxes.rs:66:37 | LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime) -> &u8 { | ^^^^^^^^^^^^^^^^ --- the lifetime gets resolved as `'_` @@ -152,7 +152,7 @@ LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime<'_>) -> &u8 { | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:74:47 + --> $DIR/mismatched-lifetime-syntaxes.rs:71:47 | LL | fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 { | ^^^^^^^^^^^^^^^^ -- the lifetime gets resolved as `'_` @@ -166,7 +166,7 @@ LL + fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime<'_>) -> &u8 | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:79:64 + --> $DIR/mismatched-lifetime-syntaxes.rs:76:64 | LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &u8 { | ^^ --- the lifetime gets resolved as `'a` @@ -179,7 +179,7 @@ LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &'a | ++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:84:74 + --> $DIR/mismatched-lifetime-syntaxes.rs:81:74 | LL | fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 { | ^^ -- the lifetime gets resolved as `'a` @@ -193,7 +193,7 @@ LL + fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:90:55 + --> $DIR/mismatched-lifetime-syntaxes.rs:87:55 | LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &u8 { | ^^ --- the lifetime gets resolved as `'a` @@ -206,7 +206,7 @@ LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &'a u8 { | ++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:95:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:92:65 | LL | fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 { | ^^ -- the lifetime gets resolved as `'a` @@ -220,7 +220,7 @@ LL + fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:102:56 + --> $DIR/mismatched-lifetime-syntaxes.rs:99:56 | LL | fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime { | ^^ ---------------- the lifetime gets resolved as `'_` @@ -234,7 +234,7 @@ LL + fn method_explicit_anonymous_ref_to_implicit_path(&self) -> ContainsLif | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:107:56 + --> $DIR/mismatched-lifetime-syntaxes.rs:104:56 | LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime { | ^^ ---------------- the lifetime gets resolved as `'a` @@ -247,7 +247,7 @@ LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> Contains | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:112:66 + --> $DIR/mismatched-lifetime-syntaxes.rs:109:66 | LL | fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> { | ^^ -- the lifetime gets resolved as `'a` @@ -261,7 +261,7 @@ LL + fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) - | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:127:39 + --> $DIR/mismatched-lifetime-syntaxes.rs:124:39 | LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &u8 { | ^^^^^^^ --- the lifetime gets resolved as `'static` @@ -274,7 +274,7 @@ LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &'static u8 { | +++++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:132:49 + --> $DIR/mismatched-lifetime-syntaxes.rs:129:49 | LL | fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 { | ^^^^^^^ -- the lifetime gets resolved as `'static` @@ -288,7 +288,7 @@ LL + fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'static u8 | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:137:40 + --> $DIR/mismatched-lifetime-syntaxes.rs:134:40 | LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime { | ^^^^^^^ ---------------- the lifetime gets resolved as `'static` @@ -301,7 +301,7 @@ LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime<'sta | +++++++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:142:50 + --> $DIR/mismatched-lifetime-syntaxes.rs:139:50 | LL | fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> { | ^^^^^^^ -- the lifetime gets resolved as `'static` @@ -315,7 +315,7 @@ LL + fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLif | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:148:40 + --> $DIR/mismatched-lifetime-syntaxes.rs:145:40 | LL | fn static_ref_to_implicit_ref(&'static self) -> &u8 { | ^^^^^^^ --- the lifetime gets resolved as `'static` @@ -328,7 +328,7 @@ LL | fn static_ref_to_implicit_ref(&'static self) -> &'static u8 { | +++++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:153:50 + --> $DIR/mismatched-lifetime-syntaxes.rs:150:50 | LL | fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 { | ^^^^^^^ -- the lifetime gets resolved as `'static` @@ -342,7 +342,7 @@ LL + fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'static | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:158:41 + --> $DIR/mismatched-lifetime-syntaxes.rs:155:41 | LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime { | ^^^^^^^ ---------------- the lifetime gets resolved as `'static` @@ -355,7 +355,7 @@ LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime<' | +++++++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:163:51 + --> $DIR/mismatched-lifetime-syntaxes.rs:160:51 | LL | fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> { | ^^^^^^^ -- the lifetime gets resolved as `'static` @@ -369,7 +369,7 @@ LL + fn static_ref_to_explicit_anonymous_path(&'static self) -> Contains | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:175:55 + --> $DIR/mismatched-lifetime-syntaxes.rs:172:55 | LL | fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ { | ^^ -- the lifetime gets resolved as `'a` @@ -383,7 +383,7 @@ LL + fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:180:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:177:65 | LL | fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> { | ^^ -- the lifetime gets resolved as `'a` @@ -397,7 +397,7 @@ LL + fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> i | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:185:72 + --> $DIR/mismatched-lifetime-syntaxes.rs:182:72 | LL | fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ { | ^^ -- the lifetime gets resolved as `'a` @@ -411,7 +411,7 @@ LL + fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:191:29 + --> $DIR/mismatched-lifetime-syntaxes.rs:188:29 | LL | v: ContainsLifetime<'a>, | ^^ this lifetime flows to the output @@ -426,7 +426,7 @@ LL + ) -> impl FnOnce() + use<'a> { | error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:205:54 + --> $DIR/mismatched-lifetime-syntaxes.rs:202:54 | LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box + '_> { | ^^ --- -- the lifetimes get resolved as `'a` @@ -440,7 +440,7 @@ LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box $DIR/mismatched-lifetime-syntaxes.rs:211:29 + --> $DIR/mismatched-lifetime-syntaxes.rs:208:29 | LL | v: ContainsLifetime<'a>, | ^^ this lifetime flows to the output @@ -456,7 +456,7 @@ LL | ) -> Box> + '_> { | ++++ error: lifetime flowing from input to output with different syntax can be confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:220:33 + --> $DIR/mismatched-lifetime-syntaxes.rs:217:33 | LL | fn multiple_outputs<'a>(v: &'a u8) -> (&u8, &u8) { | ^^ --- --- the lifetimes get resolved as `'a` diff --git a/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr b/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr deleted file mode 100644 index 2d8c6e996439..000000000000 --- a/tests/ui/lint/elided-named-lifetimes/example-from-issue48686.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: elided lifetime has a name - --> $DIR/example-from-issue48686.rs:6:50 - | -LL | pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { - | ^ this elided lifetime gets resolved as `'static` - | -note: the lint level is defined here - --> $DIR/example-from-issue48686.rs:1:9 - | -LL | #![deny(elided_named_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying it explicitly - | -LL | pub fn get_mut(&'static self, x: &mut u8) -> &'static mut u8 { - | +++++++ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs b/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs deleted file mode 100644 index 2f9083ed65f6..000000000000 --- a/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![deny(elided_named_lifetimes)] - -fn ampersand<'a>(x: &'a u8) -> &u8 { - //~^ ERROR elided lifetime has a name - x -} - -struct Brackets<'a>(&'a u8); - -fn brackets<'a>(x: &'a u8) -> Brackets { - //~^ ERROR elided lifetime has a name - Brackets(x) -} - -struct Comma<'a, T>(&'a T); - -fn comma<'a>(x: &'a u8) -> Comma { - //~^ ERROR elided lifetime has a name - Comma(x) -} - -fn underscore<'a>(x: &'a u8) -> &'_ u8 { - //~^ ERROR elided lifetime has a name - x -} - -fn main() {} diff --git a/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr b/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr deleted file mode 100644 index 249ae146b167..000000000000 --- a/tests/ui/lint/elided-named-lifetimes/missing-lifetime-kind.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: elided lifetime has a name - --> $DIR/missing-lifetime-kind.rs:3:32 - | -LL | fn ampersand<'a>(x: &'a u8) -> &u8 { - | -- ^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - | -note: the lint level is defined here - --> $DIR/missing-lifetime-kind.rs:1:9 - | -LL | #![deny(elided_named_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: elided lifetime has a name - --> $DIR/missing-lifetime-kind.rs:10:31 - | -LL | fn brackets<'a>(x: &'a u8) -> Brackets { - | -- ^^^^^^^^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - -error: elided lifetime has a name - --> $DIR/missing-lifetime-kind.rs:17:33 - | -LL | fn comma<'a>(x: &'a u8) -> Comma { - | -- ^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - -error: elided lifetime has a name - --> $DIR/missing-lifetime-kind.rs:22:34 - | -LL | fn underscore<'a>(x: &'a u8) -> &'_ u8 { - | -- ^^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - -error: aborting due to 4 previous errors - diff --git a/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs b/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs deleted file mode 100644 index 4f9218130fbc..000000000000 --- a/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![allow(elided_named_lifetimes)] - -#[warn(elided_named_lifetimes)] -mod foo { - fn bar(x: &'static u8) -> &u8 { - //~^ WARNING elided lifetime has a name - x - } - - #[deny(elided_named_lifetimes)] - fn baz(x: &'static u8) -> &u8 { - //~^ ERROR elided lifetime has a name - x - } -} - -fn main() {} diff --git a/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr b/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr deleted file mode 100644 index 3c01375d5019..000000000000 --- a/tests/ui/lint/elided-named-lifetimes/not-tied-to-crate.stderr +++ /dev/null @@ -1,34 +0,0 @@ -warning: elided lifetime has a name - --> $DIR/not-tied-to-crate.rs:5:31 - | -LL | fn bar(x: &'static u8) -> &u8 { - | ^ this elided lifetime gets resolved as `'static` - | -note: the lint level is defined here - --> $DIR/not-tied-to-crate.rs:3:8 - | -LL | #[warn(elided_named_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying it explicitly - | -LL | fn bar(x: &'static u8) -> &'static u8 { - | +++++++ - -error: elided lifetime has a name - --> $DIR/not-tied-to-crate.rs:11:31 - | -LL | fn baz(x: &'static u8) -> &u8 { - | ^ this elided lifetime gets resolved as `'static` - | -note: the lint level is defined here - --> $DIR/not-tied-to-crate.rs:10:12 - | -LL | #[deny(elided_named_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying it explicitly - | -LL | fn baz(x: &'static u8) -> &'static u8 { - | +++++++ - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/lint/elided-named-lifetimes/static.stderr b/tests/ui/lint/elided-named-lifetimes/static.stderr deleted file mode 100644 index 7ad08dbf04ba..000000000000 --- a/tests/ui/lint/elided-named-lifetimes/static.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: elided lifetime has a name - --> $DIR/static.rs:16:33 - | -LL | fn ampersand(x: &'static u8) -> &u8 { - | ^ this elided lifetime gets resolved as `'static` - | -note: the lint level is defined here - --> $DIR/static.rs:1:9 - | -LL | #![deny(elided_named_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying it explicitly - | -LL | fn ampersand(x: &'static u8) -> &'static u8 { - | +++++++ - -error: elided lifetime has a name - --> $DIR/static.rs:23:32 - | -LL | fn brackets(x: &'static u8) -> Brackets { - | ^^^^^^^^ this elided lifetime gets resolved as `'static` - | -help: consider specifying it explicitly - | -LL | fn brackets(x: &'static u8) -> Brackets<'static> { - | +++++++++ - -error: elided lifetime has a name - --> $DIR/static.rs:30:34 - | -LL | fn comma(x: &'static u8) -> Comma { - | ^ this elided lifetime gets resolved as `'static` - | -help: consider specifying it explicitly - | -LL | fn comma(x: &'static u8) -> Comma<'static, u8> { - | ++++++++ - -error: elided lifetime has a name - --> $DIR/static.rs:35:35 - | -LL | fn underscore(x: &'static u8) -> &'_ u8 { - | ^^ this elided lifetime gets resolved as `'static` - | -help: consider specifying it explicitly - | -LL - fn underscore(x: &'static u8) -> &'_ u8 { -LL + fn underscore(x: &'static u8) -> &'static u8 { - | - -error: aborting due to 4 previous errors - diff --git a/tests/ui/macros/format-args-temporaries.rs b/tests/ui/macros/format-args-temporaries.rs index ad9792bc796c..74714be76534 100644 --- a/tests/ui/macros/format-args-temporaries.rs +++ b/tests/ui/macros/format-args-temporaries.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; struct Mutex; impl Mutex { - fn lock(&self) -> MutexGuard { + fn lock(&self) -> MutexGuard<'_> { MutexGuard(self) } } diff --git a/tests/ui/mir/mir_fat_ptr.rs b/tests/ui/mir/mir_fat_ptr.rs index f2dd9e6fe406..af9831570d10 100644 --- a/tests/ui/mir/mir_fat_ptr.rs +++ b/tests/ui/mir/mir_fat_ptr.rs @@ -20,11 +20,11 @@ fn fat_ptr_via_local(a: &[u8]) -> &[u8] { x } -fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] { +fn fat_ptr_from_struct(s: FatPtrContainer<'_>) -> &[u8] { s.ptr } -fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer { +fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer<'_> { FatPtrContainer { ptr: a } } diff --git a/tests/ui/nll/issue-53570.rs b/tests/ui/nll/issue-53570.rs index 882ce9e1c313..cfc1edd217d0 100644 --- a/tests/ui/nll/issue-53570.rs +++ b/tests/ui/nll/issue-53570.rs @@ -22,7 +22,7 @@ struct Scratchpad<'a> { } impl<'a> Scratchpad<'a> { - fn get>(&self) -> Ref<[T]> + fn get>(&self) -> Ref<'_, [T]> where T: 'a { Ref::map(self.buffers.borrow(), |x| T::unwrap(x.as_ref())) diff --git a/tests/ui/nll/issue-54556-niconii.rs b/tests/ui/nll/issue-54556-niconii.rs index 9d37adede6ad..dc641524b155 100644 --- a/tests/ui/nll/issue-54556-niconii.rs +++ b/tests/ui/nll/issue-54556-niconii.rs @@ -18,7 +18,7 @@ impl Drop for Mutex { fn drop(&mut self) { println!("Mutex::drop"); } } impl<'a> Drop for MutexGuard<'a> { fn drop(&mut self) { println!("MutexGuard::drop"); } } impl Mutex { - fn lock(&self) -> Result { Ok(MutexGuard(self)) } + fn lock(&self) -> Result, ()> { Ok(MutexGuard(self)) } } fn main() { diff --git a/tests/ui/object-lifetime/object-lifetime-default-elision.rs b/tests/ui/object-lifetime/object-lifetime-default-elision.rs index ede6af51174b..f7c0261cfbb9 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-elision.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-elision.rs @@ -46,8 +46,6 @@ fn load1(ss: &dyn SomeTrait) -> &dyn SomeTrait { } fn load2<'a>(ss: &'a dyn SomeTrait) -> &dyn SomeTrait { - //~^ WARNING elided lifetime has a name - // Same as `load1` but with an explicit name thrown in for fun. ss diff --git a/tests/ui/object-lifetime/object-lifetime-default-elision.stderr b/tests/ui/object-lifetime/object-lifetime-default-elision.stderr index b44a184c6848..b59956879275 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-elision.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-elision.stderr @@ -1,15 +1,5 @@ -warning: elided lifetime has a name - --> $DIR/object-lifetime-default-elision.rs:48:40 - | -LL | fn load2<'a>(ss: &'a dyn SomeTrait) -> &dyn SomeTrait { - | -- ^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error: lifetime may not live long enough - --> $DIR/object-lifetime-default-elision.rs:73:5 + --> $DIR/object-lifetime-default-elision.rs:71:5 | LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait { | -- -- lifetime `'b` defined here @@ -21,5 +11,5 @@ LL | ss | = help: consider adding the following bound: `'a: 'b` -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/regions/init-res-into-things.rs b/tests/ui/regions/init-res-into-things.rs index 64909b329199..7b100d325335 100644 --- a/tests/ui/regions/init-res-into-things.rs +++ b/tests/ui/regions/init-res-into-things.rs @@ -20,7 +20,7 @@ impl<'a> Drop for r<'a> { } } -fn r(i: &Cell) -> r { +fn r(i: &Cell) -> r<'_> { r { i: i } diff --git a/tests/ui/regions/regions-nullary-variant.rs b/tests/ui/regions/regions-nullary-variant.rs index 8624f9961f66..24c5c6765a86 100644 --- a/tests/ui/regions/regions-nullary-variant.rs +++ b/tests/ui/regions/regions-nullary-variant.rs @@ -7,7 +7,7 @@ enum roption<'a> { a, b(&'a usize) } -fn mk(cond: bool, ptr: &usize) -> roption { +fn mk(cond: bool, ptr: &usize) -> roption<'_> { if cond {roption::a} else {roption::b(ptr)} } diff --git a/tests/ui/resource-assign-is-not-copy.rs b/tests/ui/resource-assign-is-not-copy.rs index 078824cea1ba..ab4260169013 100644 --- a/tests/ui/resource-assign-is-not-copy.rs +++ b/tests/ui/resource-assign-is-not-copy.rs @@ -14,7 +14,7 @@ impl<'a> Drop for r<'a> { } } -fn r(i: &Cell) -> r { +fn r(i: &Cell) -> r<'_> { r { i: i } diff --git a/tests/ui/resource-destruct.rs b/tests/ui/resource-destruct.rs index cbb17bb6ba61..480cbe091a7a 100644 --- a/tests/ui/resource-destruct.rs +++ b/tests/ui/resource-destruct.rs @@ -17,7 +17,7 @@ impl<'a> shrinky_pointer<'a> { pub fn look_at(&self) -> isize { return self.i.get(); } } -fn shrinky_pointer(i: &Cell) -> shrinky_pointer { +fn shrinky_pointer(i: &Cell) -> shrinky_pointer<'_> { shrinky_pointer { i: i } diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs index 9edbc3243c71..9bf6cfcccba0 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs @@ -10,7 +10,7 @@ struct Pd; impl Pd { - fn it(&self) -> It { + fn it(&self) -> It<'_> { todo!() } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs index 5dd928dbce5b..a61be38a23af 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs @@ -33,7 +33,7 @@ impl DropOrderCollector { println!("{n}"); self.0.borrow_mut().push(n) } - fn some_loud(&self, n: u32) -> Option { + fn some_loud(&self, n: u32) -> Option> { Some(LoudDrop(self, n)) } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs index 7d8dfe37135c..3e4fae2e84e7 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs @@ -8,7 +8,7 @@ struct Pd; impl Pd { - fn it(&self) -> It { + fn it(&self) -> It<'_> { todo!() } } diff --git a/tests/ui/self/elision/ignore-non-reference-lifetimes.rs b/tests/ui/self/elision/ignore-non-reference-lifetimes.rs index cedc6f0f9bc6..ecd669059ed3 100644 --- a/tests/ui/self/elision/ignore-non-reference-lifetimes.rs +++ b/tests/ui/self/elision/ignore-non-reference-lifetimes.rs @@ -4,11 +4,11 @@ struct Foo<'a>(&'a str); impl<'b> Foo<'b> { fn a<'a>(self: Self, a: &'a str) -> &str { - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax a } fn b<'a>(self: Foo<'b>, a: &'a str) -> &str { - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax a } } diff --git a/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr b/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr index 4465dbae5298..5351bf3c94cf 100644 --- a/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr +++ b/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr @@ -1,16 +1,29 @@ -warning: elided lifetime has a name - --> $DIR/ignore-non-reference-lifetimes.rs:6:41 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/ignore-non-reference-lifetimes.rs:6:30 | LL | fn a<'a>(self: Self, a: &'a str) -> &str { - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | ^^ ---- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output | - = note: `#[warn(elided_named_lifetimes)]` on by default + = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default +help: one option is to consistently use `'a` + | +LL | fn a<'a>(self: Self, a: &'a str) -> &'a str { + | ++ -warning: elided lifetime has a name - --> $DIR/ignore-non-reference-lifetimes.rs:10:44 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/ignore-non-reference-lifetimes.rs:10:33 | LL | fn b<'a>(self: Foo<'b>, a: &'a str) -> &str { - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | ^^ ---- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn b<'a>(self: Foo<'b>, a: &'a str) -> &'a str { + | ++ warning: 2 warnings emitted diff --git a/tests/ui/self/elision/lt-ref-self-async.fixed b/tests/ui/self/elision/lt-ref-self-async.fixed index aae94f7a6ccc..191610336de3 100644 --- a/tests/ui/self/elision/lt-ref-self-async.fixed +++ b/tests/ui/self/elision/lt-ref-self-async.fixed @@ -1,6 +1,6 @@ //@ edition:2018 //@ run-rustfix -#![allow(non_snake_case, dead_code, elided_named_lifetimes)] +#![allow(non_snake_case, dead_code, mismatched_lifetime_syntaxes)] use std::pin::Pin; diff --git a/tests/ui/self/elision/lt-ref-self-async.rs b/tests/ui/self/elision/lt-ref-self-async.rs index 0c11b271c35a..c910a5d6d7e8 100644 --- a/tests/ui/self/elision/lt-ref-self-async.rs +++ b/tests/ui/self/elision/lt-ref-self-async.rs @@ -1,6 +1,6 @@ //@ edition:2018 //@ run-rustfix -#![allow(non_snake_case, dead_code, elided_named_lifetimes)] +#![allow(non_snake_case, dead_code, mismatched_lifetime_syntaxes)] use std::pin::Pin; diff --git a/tests/ui/self/self_lifetime-async.rs b/tests/ui/self/self_lifetime-async.rs index fd6902071188..f839ab03a607 100644 --- a/tests/ui/self/self_lifetime-async.rs +++ b/tests/ui/self/self_lifetime-async.rs @@ -4,13 +4,13 @@ struct Foo<'a>(&'a ()); impl<'a> Foo<'a> { async fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax } type Alias = Foo<'static>; impl Alias { async fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax } fn main() {} diff --git a/tests/ui/self/self_lifetime-async.stderr b/tests/ui/self/self_lifetime-async.stderr index 32de3fd18c97..a9c1be2e808b 100644 --- a/tests/ui/self/self_lifetime-async.stderr +++ b/tests/ui/self/self_lifetime-async.stderr @@ -1,18 +1,29 @@ -warning: elided lifetime has a name - --> $DIR/self_lifetime-async.rs:6:44 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/self_lifetime-async.rs:6:29 | LL | async fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } - | -- ^ this elided lifetime gets resolved as `'b` - | | - | lifetime `'b` declared here + | ^^ --- the lifetime gets resolved as `'b` + | | + | this lifetime flows to the output | - = note: `#[warn(elided_named_lifetimes)]` on by default + = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default +help: one option is to consistently use `'b` + | +LL | async fn foo<'b>(self: &'b Foo<'a>) -> &'b () { self.0 } + | ++ -warning: elided lifetime has a name - --> $DIR/self_lifetime-async.rs:12:52 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/self_lifetime-async.rs:12:42 | LL | async fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | ^^ --- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | async fn bar<'a>(self: &Alias, arg: &'a ()) -> &'a () { arg } + | ++ warning: 2 warnings emitted diff --git a/tests/ui/self/self_lifetime.rs b/tests/ui/self/self_lifetime.rs index 0607c3b9317f..aaa31f85ad5b 100644 --- a/tests/ui/self/self_lifetime.rs +++ b/tests/ui/self/self_lifetime.rs @@ -5,13 +5,13 @@ struct Foo<'a>(&'a ()); impl<'a> Foo<'a> { fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax } type Alias = Foo<'static>; impl Alias { fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } - //~^ WARNING elided lifetime has a name + //~^ WARNING lifetime flowing from input to output with different syntax } fn main() {} diff --git a/tests/ui/self/self_lifetime.stderr b/tests/ui/self/self_lifetime.stderr index cd8f4d8adf8b..ec676e69cf63 100644 --- a/tests/ui/self/self_lifetime.stderr +++ b/tests/ui/self/self_lifetime.stderr @@ -1,18 +1,29 @@ -warning: elided lifetime has a name - --> $DIR/self_lifetime.rs:7:38 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/self_lifetime.rs:7:23 | LL | fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } - | -- ^ this elided lifetime gets resolved as `'b` - | | - | lifetime `'b` declared here + | ^^ --- the lifetime gets resolved as `'b` + | | + | this lifetime flows to the output | - = note: `#[warn(elided_named_lifetimes)]` on by default + = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default +help: one option is to consistently use `'b` + | +LL | fn foo<'b>(self: &'b Foo<'a>) -> &'b () { self.0 } + | ++ -warning: elided lifetime has a name - --> $DIR/self_lifetime.rs:13:46 +warning: lifetime flowing from input to output with different syntax can be confusing + --> $DIR/self_lifetime.rs:13:36 | LL | fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` + | ^^ --- the lifetime gets resolved as `'a` + | | + | this lifetime flows to the output + | +help: one option is to consistently use `'a` + | +LL | fn bar<'a>(self: &Alias, arg: &'a ()) -> &'a () { arg } + | ++ warning: 2 warnings emitted diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs index f5c3da847c72..f176d52c087e 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs @@ -73,7 +73,6 @@ mod in_path { // This must not err, as the `&` actually resolves to `'a`. fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { - //~^ WARNING elided lifetime has a name f("f"); } diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 92996ca84678..f29107b297a5 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -149,14 +149,6 @@ LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } LL + fn g(mut x: impl Foo<()>) -> Option<()> { x.next() } | -warning: elided lifetime has a name - --> $DIR/impl-trait-missing-lifetime-gated.rs:75:57 - | -LL | fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { - | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0658]: anonymous lifetimes in `impl Trait` are unstable --> $DIR/impl-trait-missing-lifetime-gated.rs:6:35 | @@ -289,7 +281,7 @@ help: consider introducing a named lifetime parameter LL | fn g<'a>(mut x: impl Foo<'a, ()>) -> Option<&()> { x.next() } | ++++ +++ -error: aborting due to 17 previous errors; 1 warning emitted +error: aborting due to 17 previous errors Some errors have detailed explanations: E0106, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs index b61bea16e3bc..b641f5941dce 100644 --- a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs +++ b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs @@ -100,7 +100,6 @@ where // This also works. The `'_` isn't necessary but it's where we arrive to following the suggestions: fn ok2<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() + '_ + 'a -//~^ WARNING elided lifetime has a name where G: Get, { diff --git a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index b92719e8033a..41cb2ee46bb5 100644 --- a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -6,14 +6,6 @@ LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ | | | help: consider introducing lifetime `'a` here: `'a,` -warning: elided lifetime has a name - --> $DIR/missing-lifetimes-in-signature.rs:102:64 - | -LL | fn ok2<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() + '_ + 'a - | -- lifetime `'a` declared here ^^ this elided lifetime gets resolved as `'a` - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0700]: hidden type for `impl FnOnce()` captures lifetime that does not appear in bounds --> $DIR/missing-lifetimes-in-signature.rs:19:5 | @@ -136,7 +128,7 @@ help: consider adding an explicit lifetime bound LL | G: Get + 'a, | ++++ -error: aborting due to 8 previous errors; 1 warning emitted +error: aborting due to 8 previous errors Some errors have detailed explanations: E0261, E0309, E0311, E0621, E0700. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs index d77efa39aeb6..60d9521621fa 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs @@ -3,7 +3,7 @@ type Opaque2 = impl Sized; type Opaque<'a, T> = Opaque2; #[define_opaque(Opaque)] -fn defining<'a, T>(x: &'a i32) -> Opaque { x } //~ WARNING elided lifetime has a name +fn defining<'a, T>(x: &'a i32) -> Opaque { x } //~^ ERROR: hidden type for `Opaque2` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr index 61eb76ffc5ae..b73e6b8c1015 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr @@ -1,13 +1,3 @@ -warning: elided lifetime has a name - --> $DIR/missing_lifetime_bound.rs:6:41 - | -LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } - | -- ^ this elided lifetime gets resolved as `'a` - | | - | lifetime `'a` declared here - | - = note: `#[warn(elided_named_lifetimes)]` on by default - error[E0700]: hidden type for `Opaque2` captures lifetime that does not appear in bounds --> $DIR/missing_lifetime_bound.rs:6:47 | @@ -19,6 +9,6 @@ LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } | | | hidden type `&'a i32` captures the lifetime `'a` as defined here -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-id-higher-rank-2.rs b/tests/ui/type-id-higher-rank-2.rs index 4a76b737e8c4..7b0c7b539408 100644 --- a/tests/ui/type-id-higher-rank-2.rs +++ b/tests/ui/type-id-higher-rank-2.rs @@ -5,7 +5,7 @@ use std::any::Any; struct Foo<'a>(&'a str); -fn good(s: &String) -> Foo { Foo(s) } +fn good(s: &String) -> Foo<'_> { Foo(s) } fn bad1(s: String) -> Option<&'static str> { let a: Box = Box::new(good as fn(&String) -> Foo); diff --git a/tests/ui/underscore-lifetimes.rs b/tests/ui/underscore-lifetimes.rs index 6174f8ce036f..a372851f9cff 100644 --- a/tests/ui/underscore-lifetimes.rs +++ b/tests/ui/underscore-lifetimes.rs @@ -1,6 +1,6 @@ //@ run-pass -#![allow(dead_code)] +#![allow(dead_code, mismatched_lifetime_syntaxes)] struct Foo<'a>(&'a u8); fn foo(x: &u8) -> Foo<'_> { From 64172481da44e0dd6a705afcea22b58b45b42c5a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 4 Jun 2025 08:12:54 +0000 Subject: [PATCH 49/59] Report the actual item that evaluation failed for --- compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 5 +++-- tests/ui/consts/miri_unleashed/assoc_const.stderr | 2 +- tests/ui/consts/uninhabited-const-issue-61744.rs | 1 + tests/ui/consts/uninhabited-const-issue-61744.stderr | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 01625b91353b..2556e57a58f3 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -454,11 +454,12 @@ fn report_eval_error<'tcx>( // FIXME(oli-obk): figure out how to use structured diagnostics again. diag.code(E0080); diag.span_label(span, crate::fluent_generated::const_eval_error); - diag.arg("instance", instance); - diag.arg("error_kind", kind); for frame in frames { diag.subdiagnostic(frame); } + // Add after the frame rendering above, as it adds its own `instance` args. + diag.arg("instance", instance); + diag.arg("error_kind", kind); }, ) } diff --git a/tests/ui/consts/miri_unleashed/assoc_const.stderr b/tests/ui/consts/miri_unleashed/assoc_const.stderr index b6dcb86f360f..e843615bd6de 100644 --- a/tests/ui/consts/miri_unleashed/assoc_const.stderr +++ b/tests/ui/consts/miri_unleashed/assoc_const.stderr @@ -2,7 +2,7 @@ error[E0080]: calling non-const function ` as Drop>::drop` --> $DIR/assoc_const.rs:12:31 | LL | const F: u32 = (U::X, 42).1; - | ^ evaluation of `drop_in_place::> - shim(Some(Vec))` failed here + | ^ evaluation of `, std::string::String>>::F` failed here | note: inside `drop_in_place::<(Vec, u32)> - shim(Some((Vec, u32)))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL diff --git a/tests/ui/consts/uninhabited-const-issue-61744.rs b/tests/ui/consts/uninhabited-const-issue-61744.rs index d33f9c268a51..698245b5cb8c 100644 --- a/tests/ui/consts/uninhabited-const-issue-61744.rs +++ b/tests/ui/consts/uninhabited-const-issue-61744.rs @@ -11,6 +11,7 @@ pub const unsafe fn hint_unreachable() -> ! { trait Const { const CONSTANT: i32 = unsafe { fake_type() }; //~ ERROR reached the configured maximum number of stack frames + //~^ NOTE evaluation of `::CONSTANT` failed here } impl Const for T {} diff --git a/tests/ui/consts/uninhabited-const-issue-61744.stderr b/tests/ui/consts/uninhabited-const-issue-61744.stderr index 3c5b5196aebf..f13f6126e944 100644 --- a/tests/ui/consts/uninhabited-const-issue-61744.stderr +++ b/tests/ui/consts/uninhabited-const-issue-61744.stderr @@ -2,7 +2,7 @@ error[E0080]: reached the configured maximum number of stack frames --> $DIR/uninhabited-const-issue-61744.rs:13:36 | LL | const CONSTANT: i32 = unsafe { fake_type() }; - | ^^^^^^^^^^^ evaluation of `fake_type::` failed here + | ^^^^^^^^^^^ evaluation of `::CONSTANT` failed here | note: inside `fake_type::` --> $DIR/uninhabited-const-issue-61744.rs:5:5 From 55f59fb0e329d0815a06d51dd469d774eb455cba Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 4 Jun 2025 15:51:36 +0000 Subject: [PATCH 50/59] Fix parsing of frontmatters with inner hyphens --- compiler/rustc_lexer/src/lib.rs | 4 +++- tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs | 10 ++++++++++ tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs | 11 +++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs create mode 100644 tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 2374f3882509..ece3f9107b00 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -545,11 +545,12 @@ impl Cursor<'_> { let mut s = self.as_str(); let mut found = false; + let mut size = 0; while let Some(closing) = s.find(&"-".repeat(length_opening as usize)) { let preceding_chars_start = s[..closing].rfind("\n").map_or(0, |i| i + 1); if s[preceding_chars_start..closing].chars().all(is_whitespace) { // candidate found - self.bump_bytes(closing); + self.bump_bytes(size + closing); // in case like // ---cargo // --- blahblah @@ -562,6 +563,7 @@ impl Cursor<'_> { break; } else { s = &s[closing + length_opening as usize..]; + size += closing + length_opening as usize; } } diff --git a/tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs b/tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs new file mode 100644 index 000000000000..985b1cfe9884 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs @@ -0,0 +1,10 @@ +--- +x ---🚧️ +--- + +// Regression test for #141483 +//@check-pass + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs b/tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs new file mode 100644 index 000000000000..3d5fb4538720 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs @@ -0,0 +1,11 @@ +--- +x ---y +--- + +// Test that hypens are allowed inside frontmatters if there is some +// non-whitespace character preceding them. +//@check-pass + +#![feature(frontmatter)] + +fn main() {} From b541f93372c24bcbf036efc2b454cff6f6b8f8c5 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 2 Jan 2025 17:47:10 -0800 Subject: [PATCH 51/59] Add Location::file_with_nul This is useful for C/C++ APIs which expect the const char* returned from __FILE__ or std::source_location::file_name. --- .../src/util/caller_location.rs | 21 ++++++---- library/core/src/panic/location.rs | 41 +++++++++++-------- .../file-is-nul-terminated.rs | 25 +++++++++++ 3 files changed, 63 insertions(+), 24 deletions(-) create mode 100644 tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index e926040e9ba1..9c867cc615ec 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -15,15 +15,20 @@ fn alloc_caller_location<'tcx>( line: u32, col: u32, ) -> MPlaceTy<'tcx> { + // Ensure that the filename itself does not contain nul bytes. + // This isn't possible via POSIX or Windows, but we should ensure no one + // ever does such a thing. + assert!(!filename.as_str().as_bytes().contains(&0)); + let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail; - // This can fail if rustc runs out of memory right here. Trying to emit an error would be - // pointless, since that would require allocating more memory than these short strings. - let file = if loc_details.file { - ecx.allocate_str_dedup(filename.as_str()).unwrap() - } else { - ecx.allocate_str_dedup("").unwrap() + let file_wide_ptr = { + let filename = if loc_details.file { filename.as_str() } else { "" }; + let filename_with_nul = filename.to_owned() + "\0"; + // This can fail if rustc runs out of memory right here. Trying to emit an error would be + // pointless, since that would require allocating more memory than these short strings. + let file_ptr = ecx.allocate_bytes_dedup(filename_with_nul.as_bytes()).unwrap(); + Immediate::new_slice(file_ptr.into(), filename_with_nul.len().try_into().unwrap(), ecx) }; - let file = file.map_provenance(CtfeProvenance::as_immutable); let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; @@ -36,7 +41,7 @@ fn alloc_caller_location<'tcx>( let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); // Initialize fields. - ecx.write_immediate(file.to_ref(ecx), &ecx.project_field(&location, 0).unwrap()) + ecx.write_immediate(file_wide_ptr, &ecx.project_field(&location, 0).unwrap()) .expect("writing to memory we just allocated cannot fail"); ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap()) .expect("writing to memory we just allocated cannot fail"); diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 1ad5c07d15cd..94cfd667ffae 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -1,3 +1,4 @@ +use crate::ffi::CStr; use crate::fmt; /// A struct containing information about the location of a panic. @@ -32,7 +33,12 @@ use crate::fmt; #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { - file: &'a str, + // Note: this filename will have exactly one nul byte at its end, but otherwise + // it must never contain interior nul bytes. This is relied on for the conversion + // to `CStr` below. + // + // The prefix of the string without the trailing nul byte will be a regular UTF8 `str`. + file_bytes_with_nul: &'a [u8], line: u32, col: u32, } @@ -125,9 +131,24 @@ impl<'a> Location<'a> { #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")] - #[inline] pub const fn file(&self) -> &str { - self.file + let str_len = self.file_bytes_with_nul.len() - 1; + // SAFETY: `file_bytes_with_nul` without the trailing nul byte is guaranteed to be + // valid UTF8. + unsafe { crate::str::from_raw_parts(self.file_bytes_with_nul.as_ptr(), str_len) } + } + + /// Returns the name of the source file as a nul-terminated `CStr`. + /// + /// This is useful for interop with APIs that expect C/C++ `__FILE__` or + /// `std::source_location::file_name`, both of which return a nul-terminated `const char*`. + #[must_use] + #[unstable(feature = "file_with_nul", issue = "141727")] + #[inline] + pub const fn file_with_nul(&self) -> &CStr { + // SAFETY: `file_bytes_with_nul` is guaranteed to have a trailing nul byte and no + // interior nul bytes. + unsafe { CStr::from_bytes_with_nul_unchecked(self.file_bytes_with_nul) } } /// Returns the line number from which the panic originated. @@ -181,22 +202,10 @@ impl<'a> Location<'a> { } } -#[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" -)] -impl<'a> Location<'a> { - #[doc(hidden)] - pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { - Location { file, line, col } - } -} - #[stable(feature = "panic_hook_display", since = "1.26.0")] impl fmt::Display for Location<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "{}:{}:{}", self.file, self.line, self.col) + write!(formatter, "{}:{}:{}", self.file(), self.line, self.col) } } diff --git a/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs b/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs new file mode 100644 index 000000000000..65e61a21f1a4 --- /dev/null +++ b/tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs @@ -0,0 +1,25 @@ +//@ run-pass +#![feature(file_with_nul)] + +#[track_caller] +const fn assert_file_has_trailing_zero() { + let caller = core::panic::Location::caller(); + let file_str = caller.file(); + let file_with_nul = caller.file_with_nul(); + if file_str.len() != file_with_nul.count_bytes() { + panic!("mismatched lengths"); + } + let trailing_byte: core::ffi::c_char = unsafe { + *file_with_nul.as_ptr().offset(file_with_nul.count_bytes() as _) + }; + if trailing_byte != 0 { + panic!("trailing byte was nonzero") + } +} + +#[allow(dead_code)] +const _: () = assert_file_has_trailing_zero(); + +fn main() { + assert_file_has_trailing_zero(); +} From e1e3cc24a22d0951e802b413b00bfd104ea22c7e Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 4 Jun 2025 08:20:08 +0000 Subject: [PATCH 52/59] Replace the musl submodule with a download script The submodule was causing issues in rust-lang/rust, so eliminiate it here. `build-musl` is also removed from `libm-test`'s default features so the crate doesn't need to be built by default. --- .../.github/workflows/main.yaml | 22 +++++++++---------- library/compiler-builtins/.gitignore | 3 +++ library/compiler-builtins/.gitmodules | 4 ---- library/compiler-builtins/ci/update-musl.sh | 15 +++++++++++++ .../crates/musl-math-sys/build.rs | 2 +- .../crates/musl-math-sys/musl | 1 - .../compiler-builtins/libm-test/Cargo.toml | 2 +- 7 files changed, 30 insertions(+), 19 deletions(-) delete mode 100644 library/compiler-builtins/.gitmodules create mode 100755 library/compiler-builtins/ci/update-musl.sh delete mode 160000 library/compiler-builtins/crates/musl-math-sys/musl diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 9f389d8b4857..95b0962b0824 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -108,8 +108,6 @@ jobs: - name: Print runner information run: uname -a - uses: actions/checkout@v4 - with: - submodules: true - name: Install Rust (rustup) shell: bash run: | @@ -146,6 +144,10 @@ jobs: shell: bash - run: echo "RUST_COMPILER_RT_ROOT=$(realpath ./compiler-rt)" >> "$GITHUB_ENV" shell: bash + + - name: Download musl source + run: ./ci/update-musl.sh + shell: bash - name: Verify API list if: matrix.os == 'ubuntu-24.04' @@ -182,8 +184,6 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - with: - submodules: true # Unlike rustfmt, stable clippy does not work on code with nightly features. - name: Install nightly `clippy` run: | @@ -191,6 +191,8 @@ jobs: rustup default nightly rustup component add clippy - uses: Swatinem/rust-cache@v2 + - name: Download musl source + run: ./ci/update-musl.sh - run: cargo clippy --workspace --all-targets benchmarks: @@ -205,8 +207,6 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@master - with: - submodules: true - uses: taiki-e/install-action@cargo-binstall - name: Set up dependencies @@ -223,6 +223,8 @@ jobs: - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} + - name: Download musl source + run: ./ci/update-musl.sh - name: Run icount benchmarks env: @@ -256,8 +258,6 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - with: - submodules: true - name: Install Rust (rustup) run: rustup update nightly --no-self-update && rustup default nightly shell: bash @@ -292,8 +292,6 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - with: - submodules: true - name: Install stable `rustfmt` run: rustup set profile minimal && rustup default stable && rustup component add rustfmt - run: cargo fmt -- --check @@ -317,13 +315,13 @@ jobs: TO_TEST: ${{ matrix.to_test }} steps: - uses: actions/checkout@v4 - with: - submodules: true - name: Install Rust run: | rustup update nightly --no-self-update rustup default nightly - uses: Swatinem/rust-cache@v2 + - name: download musl source + run: ./ci/update-musl.sh - name: Run extensive tests run: ./ci/run-extensive.sh - name: Print test logs if available diff --git a/library/compiler-builtins/.gitignore b/library/compiler-builtins/.gitignore index 5287a6c72be4..f12b871c2f78 100644 --- a/library/compiler-builtins/.gitignore +++ b/library/compiler-builtins/.gitignore @@ -14,3 +14,6 @@ iai-home *.bk *.rs.bk .#* + +# Manually managed +crates/musl-math-sys/musl diff --git a/library/compiler-builtins/.gitmodules b/library/compiler-builtins/.gitmodules deleted file mode 100644 index 792ed9ab21f0..000000000000 --- a/library/compiler-builtins/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "crates/musl-math-sys/musl"] - path = crates/musl-math-sys/musl - url = https://git.musl-libc.org/git/musl - shallow = true diff --git a/library/compiler-builtins/ci/update-musl.sh b/library/compiler-builtins/ci/update-musl.sh new file mode 100755 index 000000000000..b71cf5778300 --- /dev/null +++ b/library/compiler-builtins/ci/update-musl.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Download musl to a repository for `musl-math-sys` + +set -eux + +url=git://git.musl-libc.org/musl +ref=c47ad25ea3b484e10326f933e927c0bc8cded3da +dst=crates/musl-math-sys/musl + +if ! [ -d "$dst" ]; then + git clone "$url" "$dst" --single-branch --depth=1000 +fi + +git -C "$dst" fetch "$url" --depth=1 +git -C "$dst" checkout "$ref" diff --git a/library/compiler-builtins/crates/musl-math-sys/build.rs b/library/compiler-builtins/crates/musl-math-sys/build.rs index b00dbc73e280..59e42f2d2e67 100644 --- a/library/compiler-builtins/crates/musl-math-sys/build.rs +++ b/library/compiler-builtins/crates/musl-math-sys/build.rs @@ -120,7 +120,7 @@ fn build_musl_math(cfg: &Config) { let arch_dir = musl_dir.join("arch").join(&cfg.musl_arch); assert!( math.exists(), - "musl source not found. Is the submodule up to date?" + "musl source not found. You may need to run `./ci/update-musl.sh`." ); let source_map = find_math_source(&math, cfg); diff --git a/library/compiler-builtins/crates/musl-math-sys/musl b/library/compiler-builtins/crates/musl-math-sys/musl deleted file mode 160000 index c47ad25ea3b4..000000000000 --- a/library/compiler-builtins/crates/musl-math-sys/musl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c47ad25ea3b484e10326f933e927c0bc8cded3da diff --git a/library/compiler-builtins/libm-test/Cargo.toml b/library/compiler-builtins/libm-test/Cargo.toml index 01b45716b7a2..05fcc3234e00 100644 --- a/library/compiler-builtins/libm-test/Cargo.toml +++ b/library/compiler-builtins/libm-test/Cargo.toml @@ -6,7 +6,7 @@ publish = false license = "MIT OR Apache-2.0" [features] -default = ["build-mpfr", "build-musl", "unstable-float"] +default = ["build-mpfr", "unstable-float"] # Propagated from libm because this affects which functions we test. unstable-float = ["libm/unstable-float", "rug?/nightly-float"] From f67ca0fe37eb7de1a6ed748a926122f177ed6472 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 28 May 2025 14:45:14 +0000 Subject: [PATCH 53/59] Add an empty rust-version file This will be used by `josh` tooling. --- library/compiler-builtins/rust-version | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 library/compiler-builtins/rust-version diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version new file mode 100644 index 000000000000..e69de29bb2d1 From 3353a891e328715673f7687b3ccc7f90c52d1dca Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 18 May 2025 17:30:58 +0000 Subject: [PATCH 54/59] Add tooling for `josh` syncs Create a crate that handles pulling from and pushing to rust-lang/rust. This can be invoked with the following: $ cargo run -p josh-sync -- rustc-pull $ RUSTC_GIT=/path/to/rust/checkout cargo run -p josh-sync -- rustc-push --- library/compiler-builtins/Cargo.toml | 1 + .../crates/josh-sync/Cargo.toml | 7 + .../crates/josh-sync/src/main.rs | 45 +++ .../crates/josh-sync/src/sync.rs | 371 ++++++++++++++++++ 4 files changed, 424 insertions(+) create mode 100644 library/compiler-builtins/crates/josh-sync/Cargo.toml create mode 100644 library/compiler-builtins/crates/josh-sync/src/main.rs create mode 100644 library/compiler-builtins/crates/josh-sync/src/sync.rs diff --git a/library/compiler-builtins/Cargo.toml b/library/compiler-builtins/Cargo.toml index bc6b4bd29795..fb638f2fb379 100644 --- a/library/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "builtins-test", "compiler-builtins", + "crates/josh-sync", "crates/libm-macros", "crates/musl-math-sys", "crates/panic-handler", diff --git a/library/compiler-builtins/crates/josh-sync/Cargo.toml b/library/compiler-builtins/crates/josh-sync/Cargo.toml new file mode 100644 index 000000000000..1f3bb376d6d9 --- /dev/null +++ b/library/compiler-builtins/crates/josh-sync/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "josh-sync" +edition = "2024" +publish = false + +[dependencies] +directories = "6.0.0" diff --git a/library/compiler-builtins/crates/josh-sync/src/main.rs b/library/compiler-builtins/crates/josh-sync/src/main.rs new file mode 100644 index 000000000000..7f0b11900337 --- /dev/null +++ b/library/compiler-builtins/crates/josh-sync/src/main.rs @@ -0,0 +1,45 @@ +use std::io::{Read, Write}; +use std::process::exit; +use std::{env, io}; + +use crate::sync::{GitSync, Josh}; + +mod sync; + +const USAGE: &str = r#"Utility for synchroniing compiler-builtins with rust-lang/rust + +Usage: + + josh-sync rustc-pull + + Pull from rust-lang/rust to compiler-builtins. Creates a commit + updating the version file, followed by a merge commit. + + josh-sync rustc-push GITHUB_USERNAME [BRANCH] + + Create a branch off of rust-lang/rust updating compiler-builtins. +"#; + +fn main() { + let sync = GitSync::from_current_dir(); + + // Collect args, then recollect as str refs so we can match on them + let args: Vec<_> = env::args().collect(); + let args: Vec<&str> = args.iter().map(String::as_str).collect(); + + match args.as_slice()[1..] { + ["rustc-pull"] => sync.rustc_pull(None), + ["rustc-push", github_user, branch] => sync.rustc_push(github_user, Some(branch)), + ["rustc-push", github_user] => sync.rustc_push(github_user, None), + ["start-josh"] => { + let _josh = Josh::start(); + println!("press enter to stop"); + io::stdout().flush().unwrap(); + let _ = io::stdin().read(&mut [0u8]).unwrap(); + } + _ => { + println!("{USAGE}"); + exit(1); + } + } +} diff --git a/library/compiler-builtins/crates/josh-sync/src/sync.rs b/library/compiler-builtins/crates/josh-sync/src/sync.rs new file mode 100644 index 000000000000..003cf187d830 --- /dev/null +++ b/library/compiler-builtins/crates/josh-sync/src/sync.rs @@ -0,0 +1,371 @@ +use std::net::{SocketAddr, TcpStream}; +use std::process::{Command, Stdio, exit}; +use std::time::Duration; +use std::{env, fs, process, thread}; + +const JOSH_PORT: u16 = 42042; +const DEFAULT_PR_BRANCH: &str = "update-builtins"; + +pub struct GitSync { + upstream_repo: String, + upstream_ref: String, + upstream_url: String, + josh_filter: String, + josh_url_base: String, +} + +/// This code was adapted from the miri repository, via the rustc-dev-guide +/// () +impl GitSync { + pub fn from_current_dir() -> Self { + let upstream_repo = + env::var("UPSTREAM_ORG").unwrap_or_else(|_| "rust-lang".to_owned()) + "/rust"; + + Self { + upstream_url: format!("https://github.com/{upstream_repo}"), + upstream_repo, + upstream_ref: env::var("UPSTREAM_REF").unwrap_or_else(|_| "HEAD".to_owned()), + josh_filter: ":/library/compiler-builtins".to_owned(), + josh_url_base: format!("http://localhost:{JOSH_PORT}"), + } + } + + /// Pull from rust-lang/rust to compiler-builtins. + pub fn rustc_pull(&self, commit: Option) { + let Self { + upstream_ref, + upstream_url, + upstream_repo, + .. + } = self; + + let new_upstream_base = commit.unwrap_or_else(|| { + let out = check_output(["git", "ls-remote", upstream_url, upstream_ref]); + out.split_whitespace() + .next() + .unwrap_or_else(|| panic!("could not split output: '{out}'")) + .to_owned() + }); + + ensure_clean(); + + // Make sure josh is running. + let _josh = Josh::start(); + let josh_url_filtered = self.josh_url( + &self.upstream_repo, + Some(&new_upstream_base), + Some(&self.josh_filter), + ); + + let previous_upstream_base = fs::read_to_string("rust-version") + .expect("failed to read `rust-version`") + .trim() + .to_string(); + assert_ne!(previous_upstream_base, new_upstream_base, "nothing to pull"); + + let orig_head = check_output(["git", "rev-parse", "HEAD"]); + println!("original upstream base: {previous_upstream_base}"); + println!("new upstream base: {new_upstream_base}"); + println!("original HEAD: {orig_head}"); + + // Fetch the latest upstream HEAD so we can get a summary. Use the Josh URL for caching. + run([ + "git", + "fetch", + &self.josh_url(&self.upstream_repo, Some(&new_upstream_base), Some(":/")), + &new_upstream_base, + "--depth=1", + ]); + let new_summary = check_output(["git", "log", "-1", "--format=%h %s", &new_upstream_base]); + + // Update rust-version file. As a separate commit, since making it part of + // the merge has confused the heck out of josh in the past. + // We pass `--no-verify` to avoid running git hooks. + // We do this before the merge so that if there are merge conflicts, we have + // the right rust-version file while resolving them. + fs::write("rust-version", format!("{new_upstream_base}\n")) + .expect("failed to write rust-version"); + + let prep_message = format!( + "Update the upstream Rust version\n\n\ + To prepare for merging from {upstream_repo}, set the version file to:\n\n \ + {new_summary}\n\ + ", + ); + run([ + "git", + "commit", + "rust-version", + "--no-verify", + "-m", + &prep_message, + ]); + + // Fetch given rustc commit. + run(["git", "fetch", &josh_url_filtered]); + let incoming_ref = check_output(["git", "rev-parse", "FETCH_HEAD"]); + println!("incoming ref: {incoming_ref}"); + + let merge_message = format!( + "Merge ref '{upstream_head_short}{filter}' from {upstream_url}\n\n\ + Pull recent changes from {upstream_repo} via Josh.\n\n\ + Upstream ref: {new_upstream_base}\n\ + Filtered ref: {incoming_ref}\n\ + ", + upstream_head_short = &new_upstream_base[..12], + filter = self.josh_filter + ); + + // This should not add any new root commits. So count those before and after merging. + let num_roots = || -> u32 { + let out = check_output(["git", "rev-list", "HEAD", "--max-parents=0", "--count"]); + out.trim() + .parse::() + .unwrap_or_else(|e| panic!("failed to parse `{out}`: {e}")) + }; + let num_roots_before = num_roots(); + + let pre_merge_sha = check_output(["git", "rev-parse", "HEAD"]); + println!("pre-merge HEAD: {pre_merge_sha}"); + + // Merge the fetched commit. + run([ + "git", + "merge", + "FETCH_HEAD", + "--no-verify", + "--no-ff", + "-m", + &merge_message, + ]); + + let current_sha = check_output(["git", "rev-parse", "HEAD"]); + if current_sha == pre_merge_sha { + run(["git", "reset", "--hard", &orig_head]); + eprintln!( + "No merge was performed, no changes to pull were found. \ + Rolled back the preparation commit." + ); + exit(1); + } + + // Check that the number of roots did not increase. + assert_eq!( + num_roots(), + num_roots_before, + "Josh created a new root commit. This is probably not the history you want." + ); + } + + /// Construct an update to rust-lang/rust from compiler-builtins. + pub fn rustc_push(&self, github_user: &str, branch: Option<&str>) { + let Self { + josh_filter, + upstream_url, + .. + } = self; + + let branch = branch.unwrap_or(DEFAULT_PR_BRANCH); + let josh_url = self.josh_url(&format!("{github_user}/rust"), None, Some(josh_filter)); + let user_upstream_url = format!("git@github.com:{github_user}/rust.git"); + + let Ok(rustc_git) = env::var("RUSTC_GIT") else { + panic!("the RUSTC_GIT environment variable must be set to a rust-lang/rust checkout") + }; + + ensure_clean(); + let base = fs::read_to_string("rust-version") + .expect("failed to read `rust-version`") + .trim() + .to_string(); + + // Make sure josh is running. + let _josh = Josh::start(); + + // Prepare the branch. Pushing works much better if we use as base exactly + // the commit that we pulled from last time, so we use the `rust-version` + // file to find out which commit that would be. + println!("Preparing {github_user}/rust (base: {base})..."); + + if Command::new("git") + .args(["-C", &rustc_git, "fetch", &user_upstream_url, branch]) + .output() // capture output + .expect("could not run fetch") + .status + .success() + { + panic!( + "The branch '{branch}' seems to already exist in '{user_upstream_url}'. \ + Please delete it and try again." + ); + } + + run(["git", "-C", &rustc_git, "fetch", upstream_url, &base]); + + run_cfg("git", |c| { + c.args([ + "-C", + &rustc_git, + "push", + &user_upstream_url, + &format!("{base}:refs/heads/{branch}"), + ]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) // silence the "create GitHub PR" message + }); + println!("pushed PR branch"); + + // Do the actual push. + println!("Pushing changes..."); + run(["git", "push", &josh_url, &format!("HEAD:{branch}")]); + println!(); + + // Do a round-trip check to make sure the push worked as expected. + run(["git", "fetch", &josh_url, branch]); + + let head = check_output(["git", "rev-parse", "HEAD"]); + let fetch_head = check_output(["git", "rev-parse", "FETCH_HEAD"]); + assert_eq!( + head, fetch_head, + "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ + Expected {head}, got {fetch_head}." + ); + println!( + "Confirmed that the push round-trips back to compiler-builtins properly. Please \ + create a rustc PR:" + ); + // Open PR with `subtree update` title to silence the `no-merges` triagebot check + println!( + " {upstream_url}/compare/{github_user}:{branch}?quick_pull=1\ + &title=Update%20the%20%60compiler-builtins%60%20subtree\ + &body=Update%20the%20Josh%20subtree%20to%20https%3A%2F%2Fgithub.com%2Frust-lang%2F\ + compiler-builtins%2Fcommit%2F{head_short}.%0A%0Ar%3F%20%40ghost", + head_short = &head[..12], + ); + } + + /// Construct a url to the local Josh server with (optionally) + fn josh_url(&self, repo: &str, rev: Option<&str>, filter: Option<&str>) -> String { + format!( + "{base}/{repo}.git{at}{rev}{filter}{filt_git}", + base = self.josh_url_base, + at = if rev.is_some() { "@" } else { "" }, + rev = rev.unwrap_or_default(), + filter = filter.unwrap_or_default(), + filt_git = if filter.is_some() { ".git" } else { "" } + ) + } +} + +/// Fail if there are files that need to be checked in. +fn ensure_clean() { + let read = check_output(["git", "status", "--untracked-files=no", "--porcelain"]); + assert!( + read.is_empty(), + "working directory must be clean before performing rustc pull" + ); +} + +/* Helpers for running commands with logged invocations */ + +/// Run a command from an array, passing its output through. +fn run<'a, Args: AsRef<[&'a str]>>(l: Args) { + let l = l.as_ref(); + run_cfg(l[0], |c| c.args(&l[1..])); +} + +/// Run a command from an array, collecting its output. +fn check_output<'a, Args: AsRef<[&'a str]>>(l: Args) -> String { + let l = l.as_ref(); + check_output_cfg(l[0], |c| c.args(&l[1..])) +} + +/// [`run`] with configuration. +fn run_cfg(prog: &str, f: impl FnOnce(&mut Command) -> &mut Command) { + // self.read(l.as_ref()); + check_output_cfg(prog, |c| f(c.stdout(Stdio::inherit()))); +} + +/// [`read`] with configuration. All shell helpers print the command and pass stderr. +fn check_output_cfg(prog: &str, f: impl FnOnce(&mut Command) -> &mut Command) -> String { + let mut cmd = Command::new(prog); + cmd.stderr(Stdio::inherit()); + f(&mut cmd); + eprintln!("+ {cmd:?}"); + let out = cmd.output().expect("command failed"); + assert!(out.status.success()); + String::from_utf8(out.stdout.trim_ascii().to_vec()).expect("non-UTF8 output") +} + +/// Create a wrapper that stops Josh on drop. +pub struct Josh(process::Child); + +impl Josh { + pub fn start() -> Self { + // Determine cache directory. + let user_dirs = + directories::ProjectDirs::from("org", "rust-lang", "rustc-compiler-builtins-josh") + .unwrap(); + let local_dir = user_dirs.cache_dir().to_owned(); + + // Start josh, silencing its output. + #[expect(clippy::zombie_processes, reason = "clippy can't handle the loop")] + let josh = process::Command::new("josh-proxy") + .arg("--local") + .arg(local_dir) + .args([ + "--remote=https://github.com", + &format!("--port={JOSH_PORT}"), + "--no-background", + ]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("failed to start josh-proxy, make sure it is installed"); + + // Wait until the port is open. We try every 10ms until 1s passed. + for _ in 0..100 { + // This will generally fail immediately when the port is still closed. + let addr = SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)); + let josh_ready = TcpStream::connect_timeout(&addr, Duration::from_millis(1)); + + if josh_ready.is_ok() { + println!("josh up and running"); + return Josh(josh); + } + + // Not ready yet. + thread::sleep(Duration::from_millis(10)); + } + panic!("Even after waiting for 1s, josh-proxy is still not available.") + } +} + +impl Drop for Josh { + fn drop(&mut self) { + if cfg!(unix) { + // Try to gracefully shut it down. + Command::new("kill") + .args(["-s", "INT", &self.0.id().to_string()]) + .output() + .expect("failed to SIGINT josh-proxy"); + // Sadly there is no "wait with timeout"... so we just give it some time to finish. + thread::sleep(Duration::from_millis(100)); + // Now hopefully it is gone. + if self + .0 + .try_wait() + .expect("failed to wait for josh-proxy") + .is_some() + { + return; + } + } + // If that didn't work (or we're not on Unix), kill it hard. + eprintln!( + "I have to kill josh-proxy the hard way, let's hope this does not \ + break anything." + ); + self.0.kill().expect("failed to SIGKILL josh-proxy"); + } +} From eb45e57afe52ba549a3929b58dc9060541bf0a20 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 4 Jun 2025 18:10:07 +0000 Subject: [PATCH 55/59] Update the upstream Rust version To prepare for merging from rust-lang/rust, set the version file to: df8102fe5f Auto merge of #142002 - onur-ozkan:follow-ups2, r=jieyouxu --- library/compiler-builtins/rust-version | 1 + 1 file changed, 1 insertion(+) diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version index e69de29bb2d1..e05aaa0573ca 100644 --- a/library/compiler-builtins/rust-version +++ b/library/compiler-builtins/rust-version @@ -0,0 +1 @@ +df8102fe5f24f28a918660b0cd918d7331c3896e From ab453db3c4e7a49d0ed3811c12ae658cf578f78b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 4 Jun 2025 21:17:51 +0000 Subject: [PATCH 56/59] `panic-handler`: Remove the `no_core` feature This was introduced before `#[panic_handler]` was stable, but should no longer be needed. Additionally, we only need it for `builtins-test-intrinsics`, not as a dependency of `compiler-builtins`. --- .../compiler-builtins/builtins-test-intrinsics/Cargo.toml | 2 +- library/compiler-builtins/compiler-builtins/Cargo.toml | 3 --- library/compiler-builtins/crates/panic-handler/src/lib.rs | 7 ++----- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml b/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml index 704de20c5ad6..064b7cad2f64 100644 --- a/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml +++ b/library/compiler-builtins/builtins-test-intrinsics/Cargo.toml @@ -6,7 +6,7 @@ publish = false license = "MIT OR Apache-2.0" [dependencies] -compiler_builtins = { path = "../compiler-builtins", features = ["compiler-builtins"]} +compiler_builtins = { path = "../compiler-builtins", features = ["compiler-builtins"] } panic-handler = { path = "../crates/panic-handler" } [features] diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 6bee8da68a0a..11ee91954384 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -24,9 +24,6 @@ core = { version = "1.0.1", optional = true, package = "rustc-std-workspace-core [build-dependencies] cc = { optional = true, version = "1.2" } -[dev-dependencies] -panic-handler = { path = "../crates/panic-handler" } - [features] default = ["compiler-builtins"] diff --git a/library/compiler-builtins/crates/panic-handler/src/lib.rs b/library/compiler-builtins/crates/panic-handler/src/lib.rs index 673e005224bd..f4d7c839740b 100644 --- a/library/compiler-builtins/crates/panic-handler/src/lib.rs +++ b/library/compiler-builtins/crates/panic-handler/src/lib.rs @@ -1,11 +1,8 @@ //! This is needed for tests on targets that require a `#[panic_handler]` function -#![feature(no_core)] -#![no_core] - -extern crate core; +#![no_std] #[panic_handler] -fn panic(_: &core::panic::PanicInfo) -> ! { +fn panic(_: &core::panic::PanicInfo<'_>) -> ! { loop {} } From 91ad4bf08784916edfb4e498c3749bed76b0686a Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Thu, 5 Jun 2025 11:33:38 +0000 Subject: [PATCH 57/59] jsondocck: Explain what `KNOWN_DIRECTIVE_NAMES` is doing --- src/tools/jsondocck/src/directive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs index 5269ffb469fa..fdb2fa6dbbe0 100644 --- a/src/tools/jsondocck/src/directive.rs +++ b/src/tools/jsondocck/src/directive.rs @@ -105,7 +105,7 @@ impl DirectiveKind { [_path, value] => Self::HasNotValue { value: value.clone() }, _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"), }, - + // Ignore compiletest directives, like //@ edition (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => { return None; } From f8e9778eb12573eabac183f5d617e1a7404505c7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 9 May 2025 20:37:06 +0000 Subject: [PATCH 58/59] Make #[used(linker)] the default on ELF too #[used] currently is an alias for #[used(linker)] on all platforms except ELF based ones where it is an alias for #[used(compiler)]. The latter has surprising behavior and the LLVM LangRef explicitly states that it "should only be used in rare circumstances, and should not be exposed to source languages." The reason #[used] still was an alias to #[used(compiler)] on ELF is because the gold linker has issues with it. Luckily gold has been deprecated with GCC 15 and seems to be unable to bootstrap rustc anyway. As such we shouldn't really care about supporting gold. --- compiler/rustc_codegen_llvm/src/consts.rs | 12 +++---- compiler/rustc_codegen_ssa/src/back/link.rs | 5 +-- .../rustc_codegen_ssa/src/codegen_attrs.rs | 33 +++---------------- 3 files changed, 13 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index fe2f20273276..866696276e17 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -541,12 +541,12 @@ impl<'ll> CodegenCx<'ll, '_> { // in the handling of `.init_array` (the static constructor list) in versions of // the gold linker (prior to the one released with binutils 2.36). // - // That said, we only ever emit these when compiling for ELF targets, unless - // `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage - // on other targets, in particular MachO targets have *their* static constructor - // lists broken if `llvm.compiler.used` is emitted rather than `llvm.used`. However, - // that check happens when assigning the `CodegenFnAttrFlags` in - // `rustc_hir_analysis`, so we don't need to take care of it here. + // That said, we only ever emit these when `#[used(compiler)]` is explicitly + // requested. This is to avoid similar breakage on other targets, in particular + // MachO targets have *their* static constructor lists broken if `llvm.compiler.used` + // is emitted rather than `llvm.used`. However, that check happens when assigning + // the `CodegenFnAttrFlags` in the `codegen_fn_attrs` query, so we don't need to + // take care of it here. self.add_compiler_used_global(g); } if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c5792da26781..30128c4bea2f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1986,7 +1986,7 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor /// This method creates a synthetic object file, which contains undefined references to all symbols /// that are necessary for the linking. They are only present in symbol table but not actually /// used in any sections, so the linker will therefore pick relevant rlibs for linking, but -/// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections. +/// unused `#[no_mangle]` or `#[used(compiler)]` can still be discard by GC sections. /// /// There's a few internal crates in the standard library (aka libcore and /// libstd) which actually have a circular dependence upon one another. This @@ -2020,7 +2020,8 @@ fn add_linked_symbol_object( if file.format() == object::BinaryFormat::MachO { // Divide up the sections into sub-sections via symbols for dead code stripping. - // Without this flag, unused `#[no_mangle]` or `#[used]` cannot be discard on MachO targets. + // Without this flag, unused `#[no_mangle]` or `#[used(compiler)]` cannot be + // discard on MachO targets. file.set_subsections_via_symbols(); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4bcafa3be366..0b31fa8fa886 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -195,35 +195,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() }); } None => { - // Unfortunately, unconditionally using `llvm.used` causes - // issues in handling `.init_array` with the gold linker, - // but using `llvm.compiler.used` caused a nontrivial amount - // of unintentional ecosystem breakage -- particularly on - // Mach-O targets. - // - // As a result, we emit `llvm.compiler.used` only on ELF - // targets. This is somewhat ad-hoc, but actually follows - // our pre-LLVM 13 behavior (prior to the ecosystem - // breakage), and seems to match `clang`'s behavior as well - // (both before and after LLVM 13), possibly because they - // have similar compatibility concerns to us. See - // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 - // and following comments for some discussion of this, as - // well as the comments in `rustc_codegen_llvm` where these - // flags are handled. - // - // Anyway, to be clear: this is still up in the air - // somewhat, and is subject to change in the future (which - // is a good thing, because this would ideally be a bit - // more firmed up). - let is_like_elf = !(tcx.sess.target.is_like_darwin - || tcx.sess.target.is_like_windows - || tcx.sess.target.is_like_wasm); - codegen_fn_attrs.flags |= if is_like_elf { - CodegenFnAttrFlags::USED_COMPILER - } else { - CodegenFnAttrFlags::USED_LINKER - }; + // Unconditionally using `llvm.used` causes issues in handling + // `.init_array` with the gold linker. Luckily gold has been + // deprecated with GCC 15 and rustc now warns about using gold. + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER } } } From d945c8562b49df98af25ed43b41bdbc59b8385cc Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 5 Jun 2025 23:09:28 -0700 Subject: [PATCH 59/59] compiler: Add track_caller to AbiMapping::unwrap Same reason as it is on Option's. --- compiler/rustc_target/src/spec/abi_map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index d9101f79f046..be94e18dc397 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -29,6 +29,7 @@ impl AbiMapping { } } + #[track_caller] pub fn unwrap(self) -> CanonAbi { self.into_option().unwrap() }